core/vm: refactor stack for less implicit bound checks

This commit is contained in:
Ömer Faruk IRMAK 2025-07-08 19:45:52 +03:00
parent cbe64f51c7
commit 27d6bed930
15 changed files with 1541 additions and 1484 deletions

View file

@ -84,15 +84,12 @@ func enable1884(jt *JumpTable) {
jt[SELFBALANCE] = &operation{
execute: opSelfBalance,
constantGas: GasFastStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
scope.Stack.push(balance)
return nil, nil
return nil, scope.Stack.push(balance)
}
// enable1344 applies EIP-1344 (ChainID Opcode)
@ -102,16 +99,13 @@ func enable1344(jt *JumpTable) {
jt[CHAINID] = &operation{
execute: opChainID,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
// opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
scope.Stack.push(chainId)
return nil, nil
return nil, scope.Stack.push(chainId)
}
// enable2200 applies EIP-2200 (Rebalance net-metered SSTORE)
@ -174,8 +168,6 @@ func enable3198(jt *JumpTable) {
jt[BASEFEE] = &operation{
execute: opBaseFee,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
@ -186,21 +178,21 @@ func enable1153(jt *JumpTable) {
jt[TLOAD] = &operation{
execute: opTload,
constantGas: params.WarmStorageReadCostEIP2929,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[TSTORE] = &operation{
execute: opTstore,
constantGas: params.WarmStorageReadCostEIP2929,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
}
}
// opTload implements TLOAD opcode
func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
loc := scope.Stack.peek()
loc, err := scope.Stack.pop(1)
if err != nil {
return nil, err
}
hash := common.Hash(loc.Bytes32())
val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
loc.SetBytes(val.Bytes())
@ -212,8 +204,11 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
if interpreter.readOnly {
return nil, ErrWriteProtection
}
loc := scope.Stack.pop()
val := scope.Stack.pop()
loc, val, err := scope.Stack.pop2(0)
if err != nil {
return nil, err
}
interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
return nil, nil
}
@ -221,8 +216,7 @@ func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// opBaseFee implements BASEFEE opcode
func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)
scope.Stack.push(baseFee)
return nil, nil
return nil, scope.Stack.push(baseFee)
}
// enable3855 applies EIP-3855 (PUSH0 opcode)
@ -231,15 +225,12 @@ func enable3855(jt *JumpTable) {
jt[PUSH0] = &operation{
execute: opPush0,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
// opPush0 implements the PUSH0 opcode
func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.push(new(uint256.Int))
return nil, nil
return nil, scope.Stack.push(new(uint256.Int))
}
// enable3860 enables "EIP-3860: Limit and meter initcode"
@ -255,18 +246,25 @@ func enable5656(jt *JumpTable) {
jt[MCOPY] = &operation{
execute: opMcopy,
constantGas: GasFastestStep,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
}
}
// opMcopy implements the MCOPY opcode (https://eips.ethereum.org/EIPS/eip-5656)
func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryMcopy, scope.Stack, scope.Memory)
dst, src, length, err := scope.Stack.pop3(0)
if err != nil {
return nil, err
}
dynamicCost, err := gasMcopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
mStart := dst
if src.Gt(mStart) {
mStart = src
}
memorySize, err := calculateMemorySize(mStart, length)
if err != nil {
return nil, err
}
dynamicCost, err := memoryCopierGas(scope.Memory, memorySize, length)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
}
@ -275,11 +273,6 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
}
scope.Memory.Resize(memorySize)
var (
dst = scope.Stack.pop()
src = scope.Stack.pop()
length = scope.Stack.pop()
)
// These values are checked for overflow during memory expansion calculation
// (the memorySize function on the opcode).
scope.Memory.Copy(dst.Uint64(), src.Uint64(), length.Uint64())
@ -288,7 +281,11 @@ func opMcopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// opBlobHash implements the BLOBHASH opcode
func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
index := scope.Stack.peek()
index, err := scope.Stack.pop(1)
if err != nil {
return nil, err
}
if index.LtUint64(uint64(len(interpreter.evm.TxContext.BlobHashes))) {
blobHash := interpreter.evm.TxContext.BlobHashes[index.Uint64()]
index.SetBytes32(blobHash[:])
@ -301,13 +298,16 @@ func opBlobHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// opBlobBaseFee implements BLOBBASEFEE opcode
func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
blobBaseFee, _ := uint256.FromBig(interpreter.evm.Context.BlobBaseFee)
scope.Stack.push(blobBaseFee)
return nil, nil
return nil, scope.Stack.push(blobBaseFee)
}
// opCLZ implements the CLZ opcode (count leading zero bytes)
func opCLZ(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := scope.Stack.peek()
x, err := scope.Stack.pop(1)
if err != nil {
return nil, err
}
x.SetUint64(256 - uint64(x.BitLen()))
return nil, nil
}
@ -317,8 +317,6 @@ func enable4844(jt *JumpTable) {
jt[BLOBHASH] = &operation{
execute: opBlobHash,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
}
@ -327,8 +325,6 @@ func enable7939(jt *JumpTable) {
jt[CLZ] = &operation{
execute: opCLZ,
constantGas: GasFastStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
}
@ -337,8 +333,6 @@ func enable7516(jt *JumpTable) {
jt[BLOBBASEFEE] = &operation{
execute: opBlobBaseFee,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
@ -347,17 +341,20 @@ func enable6780(jt *JumpTable) {
jt[SELFDESTRUCT] = &operation{
execute: opSelfdestructEIP6780,
constantGas: params.SelfdestructGasEIP150,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
}
}
func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryExtCodeCopy, scope.Stack, scope.Memory)
a, memOffset, codeOffset, length, err := scope.Stack.pop4(0)
if err != nil {
return nil, err
}
dynamicCost, err := gasExtCodeCopyEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
memorySize, err := calculateMemorySize(memOffset, length)
if err != nil {
return nil, err
}
dynamicCost, err := gasExtCodeCopyEIP4762(interpreter.evm, scope.Contract, scope.Memory, memorySize, a, length)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
}
@ -366,13 +363,6 @@ func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
}
scope.Memory.Resize(memorySize)
var (
stack = scope.Stack
a = stack.pop()
memOffset = stack.pop()
codeOffset = stack.pop()
length = stack.pop()
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
uint64CodeOffset = math.MaxUint64
@ -400,7 +390,9 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
)
*pc += 1
if *pc < codeLen {
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
if err := scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))); err != nil {
return nil, err
}
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall && *pc%31 == 0 {
// touch next chunk if PUSH1 is at the boundary. if so, *pc has
@ -412,10 +404,9 @@ func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
return nil, ErrOutOfGas
}
}
} else {
scope.Stack.push(integer.Clear())
return nil, nil
}
return nil, nil
return nil, scope.Stack.push(integer.Clear())
}
func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
@ -425,12 +416,14 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
start = min(codeLen, int(*pc+1))
end = min(codeLen, start+pushByteSize)
)
scope.Stack.push(new(uint256.Int).SetBytes(
if err := scope.Stack.push(new(uint256.Int).SetBytes(
common.RightPadBytes(
scope.Contract.Code[start:end],
pushByteSize,
)),
)
); err != nil {
return nil, err
}
if !scope.Contract.IsDeployment && !scope.Contract.IsSystemCall {
contractAddr := scope.Contract.Address()
@ -448,104 +441,72 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
func enable4762(jt *JumpTable) {
jt[SSTORE] = &operation{
execute: opSstoreEIP4762,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: opSstoreEIP4762,
}
jt[SLOAD] = &operation{
execute: opSLoadEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opSLoadEIP4762,
}
jt[BALANCE] = &operation{
execute: opBalanceEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opBalanceEIP4762,
}
jt[EXTCODESIZE] = &operation{
execute: opExtCodeSizeEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opExtCodeSizeEIP4762,
}
jt[EXTCODEHASH] = &operation{
execute: opExtCodeHashEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opExtCodeHashEIP4762,
}
jt[EXTCODECOPY] = &operation{
execute: opExtCodeCopyEIP4762,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
execute: opExtCodeCopyEIP4762,
}
jt[CODECOPY] = &operation{
execute: opCodeCopyEIP4762,
constantGas: GasFastestStep,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
}
jt[SELFDESTRUCT] = &operation{
execute: opSelfdestructEIP4762,
constantGas: params.SelfdestructGasEIP150,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
}
jt[CREATE] = &operation{
execute: opCreateEIP3860,
constantGas: params.CreateNGasEip4762,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
}
jt[CREATE2] = &operation{
execute: opCreate2EIP3860,
constantGas: params.CreateNGasEip4762,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
}
jt[CALL] = &operation{
execute: opCallEIP4762,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
execute: opCallEIP4762,
}
jt[CALLCODE] = &operation{
execute: opCallCodeEIP4762,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
execute: opCallCodeEIP4762,
}
jt[STATICCALL] = &operation{
execute: opStaticCallEIP4762,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
execute: opStaticCallEIP4762,
}
jt[DELEGATECALL] = &operation{
execute: opDelegateCallEIP4762,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
execute: opDelegateCallEIP4762,
}
jt[PUSH1] = &operation{
execute: opPush1EIP4762,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
for i := 1; i < 32; i++ {
jt[PUSH1+OpCode(i)] = &operation{
execute: makePushEIP4762(uint64(i+1), i+1),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
}
}

View file

@ -378,7 +378,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
evm.StateDB.AddBalance(addr, common.U2560, tracing.BalanceChangeTouchAccount)
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer.OnGasChange)

View file

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
@ -56,50 +57,32 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
return 0, nil
}
// memoryCopierGas creates the gas functions for the following opcodes, and takes
// the stack position of the operand which determines the size of the data to copy
// as argument:
// CALLDATACOPY (stack position 2)
// CODECOPY (stack position 2)
// MCOPY (stack position 2)
// EXTCODECOPY (stack position 3)
// RETURNDATACOPY (stack position 2)
func memoryCopierGas(stackpos int) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// Gas for expanding the memory
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
// memoryCopierGas
func memoryCopierGas(mem *Memory, memorySize uint64, words256 *uint256.Int) (uint64, error) {
// Gas for expanding the memory
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
words, overflow := words256.Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
var (
gasCallDataCopy = memoryCopierGas(2)
gasCodeCopy = memoryCopierGas(2)
gasMcopy = memoryCopierGas(2)
gasExtCodeCopy = memoryCopierGas(3)
gasReturnDataCopy = memoryCopierGas(2)
)
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasSStore(evm *EVM, contract *Contract, loc, val *uint256.Int) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), loc.Bytes32())
)
// The legacy gas metering only takes into consideration the current state
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
@ -111,9 +94,9 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a non-zero to a non-zero (CHANGE)
switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
case current == (common.Hash{}) && val.Sign() != 0: // 0 => non 0
return params.SstoreSetGas, nil
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
case current != (common.Hash{}) && val.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
default: // non 0 => non 0 (or 0 => 0)
@ -135,7 +118,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// (2.2.2.) If original value equals new value (this storage slot is reset)
// (2.2.2.1.) If original value is 0, add 19800 gas to refund counter.
// (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
value := common.Hash(y.Bytes32())
value := common.Hash(val.Bytes32())
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
@ -180,17 +163,16 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// (2.2.2.) If original value equals new value (this storage slot is reset):
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasSStoreEIP2200(evm *EVM, contract *Contract, loc, val *uint256.Int) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.Back(0)
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), loc.Bytes32())
)
value := common.Hash(y.Bytes32())
value := common.Hash(val.Bytes32())
if current == value { // noop (1)
return params.SloadGasEIP2200, nil
@ -221,42 +203,40 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
return params.SloadGasEIP2200, nil // dirty update (2.2)
}
func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
return 0, ErrGasUintOverflow
}
var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
func gasLog(mem *Memory, memorySize uint64, n uint64, size *uint256.Int) (uint64, error) {
requestedSize, overflow := size.Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
}
func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
return 0, ErrGasUintOverflow
}
var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
func gasKeccak256(mem *Memory, memorySize uint64, size *uint256.Int) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
wordGas, overflow := size.Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@ -269,28 +249,12 @@ func gasKeccak256(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
return gas, nil
}
// pureMemoryGascost is used by several operations, which aside from their
// static cost have a dynamic cost which is solely based on the memory
// expansion
func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryGasCost(mem, memorySize)
}
var (
gasReturn = pureMemoryGascost
gasRevert = pureMemoryGascost
gasMLoad = pureMemoryGascost
gasMStore8 = pureMemoryGascost
gasMStore = pureMemoryGascost
gasCreate = pureMemoryGascost
)
func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasCreate2(mem *Memory, memorySize uint64, size *uint256.Int) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
wordGas, overflow := stack.Back(2).Uint64WithOverflow()
wordGas, overflow := size.Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@ -303,12 +267,12 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
return gas, nil
}
func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasCreateEip3860(mem *Memory, memorySize uint64, size256 *uint256.Int) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
size, overflow := size256.Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@ -322,12 +286,12 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
}
return gas, nil
}
func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasCreate2Eip3860(mem *Memory, memorySize uint64, size256 *uint256.Int) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
size, overflow := stack.Back(2).Uint64WithOverflow()
size, overflow := size256.Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@ -342,8 +306,8 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
return gas, nil
}
func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
func gasExpFrontier(exp *uint256.Int) (uint64, error) {
expByteLen := uint64((exp.BitLen() + 7) / 8)
var (
gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
@ -355,8 +319,8 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
return gas, nil
}
func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
func gasExpEIP158(exp *uint256.Int) (uint64, error) {
expByteLen := uint64((exp.BitLen() + 7) / 8)
var (
gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
@ -368,11 +332,11 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
return gas, nil
}
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasCall(evm *EVM, contract *Contract, mem *Memory, memorySize uint64, callCost, addr, value *uint256.Int) (uint64, error) {
var (
gas uint64
transfersValue = !stack.Back(2).IsZero()
address = common.Address(stack.Back(1).Bytes20())
transfersValue = !value.IsZero()
address = common.Address(addr.Bytes20())
)
if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) {
@ -393,7 +357,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
return 0, ErrGasUintOverflow
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, callCost)
if err != nil {
return 0, err
}
@ -404,7 +368,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
return gas, nil
}
func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasCallCode(evm *EVM, contract *Contract, mem *Memory, memorySize uint64, callCost, value *uint256.Int) (uint64, error) {
memoryGas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
@ -413,13 +377,13 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
gas uint64
overflow bool
)
if stack.Back(2).Sign() != 0 && !evm.chainRules.IsEIP4762 {
if value.Sign() != 0 && !evm.chainRules.IsEIP4762 {
gas += params.CallValueTransferGas
}
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
return 0, ErrGasUintOverflow
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, callCost)
if err != nil {
return 0, err
}
@ -429,12 +393,12 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
return gas, nil
}
func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasDelegateCall(evm *EVM, contract *Contract, mem *Memory, memorySize uint64, callCost *uint256.Int) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, callCost)
if err != nil {
return 0, err
}
@ -445,12 +409,12 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
return gas, nil
}
func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasStaticCall(evm *EVM, contract *Contract, mem *Memory, memorySize uint64, callCost *uint256.Int) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, callCost)
if err != nil {
return 0, err
}
@ -461,12 +425,12 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
return gas, nil
}
func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasSelfdestruct(evm *EVM, contract *Contract, addr *uint256.Int) (uint64, error) {
var gas uint64
// EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150
var address = common.Address(stack.Back(0).Bytes20())
var address = common.Address(addr.Bytes20())
if evm.chainRules.IsEIP158 {
// if empty and transfers value

File diff suppressed because it is too large Load diff

View file

@ -20,13 +20,13 @@ import (
"bytes"
"encoding/json"
"fmt"
"math"
"math/big"
"os"
"strings"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@ -77,7 +77,7 @@ func init() {
"sdiv": opSdiv,
"mod": opMod,
"smod": opSmod,
"exp": opExp,
"exp": opExpFrontier,
"signext": opSignExtend,
"lt": opLt,
"gt": opGt,
@ -107,11 +107,11 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x)
stack.push(y)
opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil})
opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, &Contract{Gas: math.MaxUint64}})
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
}
actual := stack.pop()
actual, _ := stack.pop(0)
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
@ -222,7 +222,7 @@ func TestAddMod(t *testing.T) {
stack.push(y)
stack.push(x)
opAddmod(&pc, evm.interpreter, &ScopeContext{nil, stack, nil})
actual := stack.pop()
actual, _ := stack.pop(0)
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
}
@ -248,7 +248,7 @@ func TestWriteExpectedValues(t *testing.T) {
stack.push(x)
stack.push(y)
opFn(&pc, evm.interpreter, &ScopeContext{nil, stack, nil})
actual := stack.pop()
actual, _ := stack.pop(0)
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
}
return result
@ -283,7 +283,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
var (
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
scope = &ScopeContext{nil, stack, nil}
scope = &ScopeContext{nil, stack, &Contract{Gas: math.MaxUint64}}
)
// convert args
intArgs := make([]*uint256.Int, len(args))
@ -297,7 +297,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
stack.push(arg)
}
op(&pc, evm.interpreter, scope)
stack.pop()
stack.pop(0)
}
bench.StopTimer()
@ -401,7 +401,7 @@ func BenchmarkOpExp(b *testing.B) {
x := alphabetSoup
y := alphabetSoup
opBenchmark(b, opExp, x, y)
opBenchmark(b, opExpFrontier, x, y)
}
func BenchmarkOpSignExtend(b *testing.B) {
@ -595,7 +595,7 @@ func TestOpTstore(t *testing.T) {
if stack.len() != 1 {
t.Fatal("stack wrong size")
}
val := stack.peek()
val, _ := stack.pop(1)
if !bytes.Equal(val.Bytes(), value) {
t.Fatal("incorrect element read from transient storage")
}
@ -676,15 +676,6 @@ func TestCreate2Addresses(t *testing.T) {
code := common.FromHex(tt.code)
codeHash := crypto.Keccak256(code)
address := crypto.CreateAddress2(origin, salt, codeHash)
/*
stack := newstack()
// salt, but we don't need that for this test
stack.push(big.NewInt(int64(len(code)))) //size
stack.push(big.NewInt(0)) // memstart
stack.push(big.NewInt(0)) // value
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
*/
expected := common.BytesToAddress(common.FromHex(tt.expected))
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
@ -713,7 +704,7 @@ func TestRandom(t *testing.T) {
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
}
actual := stack.pop()
actual, _ := stack.pop(0)
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.random.Bytes()))
if overflow {
t.Errorf("Testcase %v: invalid overflow", tt.name)
@ -755,7 +746,7 @@ func TestBlobHash(t *testing.T) {
if len(stack.data) != 1 {
t.Errorf("Expected one item on stack after %v, got %d: ", tt.name, len(stack.data))
}
actual := stack.pop()
actual, _ := stack.pop(0)
expected, overflow := uint256.FromBig(new(big.Int).SetBytes(tt.expect.Bytes()))
if overflow {
t.Errorf("Testcase %v: invalid overflow", tt.name)
@ -868,37 +859,20 @@ func TestOpMCopy(t *testing.T) {
stack.push(src)
stack.push(dst)
wantErr := (tc.wantGas == 0)
// Calc mem expansion
var memorySize uint64
if memSize, overflow := memoryMcopy(stack); overflow {
if wantErr {
continue
}
t.Errorf("overflow")
} else {
var overflow bool
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
t.Error(ErrGasUintOverflow)
}
}
// and the dynamic cost
var haveGas uint64
if dynamicCost, err := gasMcopy(evm, nil, stack, mem, memorySize); err != nil {
t.Error(err)
} else {
haveGas = GasFastestStep + dynamicCost
}
// Expand mem
if memorySize > 0 {
mem.Resize(memorySize)
}
// Do the copy
opMcopy(&pc, evm.interpreter, &ScopeContext{mem, stack, contract})
_, err := opMcopy(&pc, evm.interpreter, &ScopeContext{mem, stack, contract})
if wantErr {
if err == nil {
t.Error("wanted error")
}
continue
}
want := common.FromHex(strings.ReplaceAll(tc.want, " ", ""))
if have := mem.store; !bytes.Equal(want, have) {
t.Errorf("case %d: \nwant: %#x\nhave: %#x\n", i, want, have)
}
wantGas := tc.wantGas
haveGas := 30_000_000 - contract.Gas + GasFastestStep
if haveGas != wantGas {
t.Errorf("case %d: gas wrong, want %d have %d\n", i, wantGas, haveGas)
}
@ -970,7 +944,7 @@ func TestPush(t *testing.T) {
pc := new(uint64)
*pc = uint64(i)
push32(pc, nil, scope)
res := scope.Stack.pop()
res, _ := scope.Stack.pop(0)
if have := res.Hex(); have != want {
t.Fatalf("case %d, have %v want %v", i, have, want)
}
@ -1010,7 +984,7 @@ func TestOpCLZ(t *testing.T) {
if gotLen := stack.len(); gotLen != 1 {
t.Fatalf("stack length = %d; want 1", gotLen)
}
result := stack.pop()
result, _ := stack.pop(0)
if got := result.Uint64(); got != tc.want {
t.Fatalf("clz(%q) = %d; want %d", tc.inputHex, got, tc.want)
}

View file

@ -261,17 +261,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
}
// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
// Get the operation from the jump table
op = contract.GetOp(pc)
operation := jumpTable[op]
cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, traceAndReturnError(&ErrStackUnderflow{stackLen: sLen, required: operation.minStack})
} else if sLen > operation.maxStack {
return nil, traceAndReturnError(&ErrStackOverflow{stackLen: sLen, limit: operation.maxStack})
}
// for tracing: this gas consumption event is emitted in the executeWithTracer wrapper.
if contract.Gas < cost {
return nil, traceAndReturnError(ErrOutOfGas)

View file

@ -82,15 +82,11 @@ func BenchmarkInterpreter(b *testing.B) {
evm = NewEVM(BlockContext{BlockNumber: big.NewInt(1), Time: 1, Random: &common.Hash{}}, statedb, params.MergedTestChainConfig, Config{})
startGas uint64 = 100_000_000
value = uint256.NewInt(0)
stack = newstack()
mem = NewMemory()
contract = NewContract(common.Address{}, common.Address{}, value, startGas, nil)
)
stack.push(uint256.NewInt(123))
stack.push(uint256.NewInt(123))
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
args := uint256.NewInt(123)
b.ResetTimer()
for i := 0; i < b.N; i++ {
gasSStoreEIP3529(evm, contract, stack, mem, 1234)
gasSStoreWithClearingRefund(evm, contract, args, args, params.SstoreClearsScheduleRefundEIP2200)
}
}

View file

@ -22,23 +22,12 @@ import (
"github.com/ethereum/go-ethereum/params"
)
type (
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
gasFunc 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 func(*Stack) (size uint64, overflow bool)
)
type executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
type operation struct {
// execute is the operation function
execute executionFunc
constantGas uint64
// minStack tells how many stack items are required
minStack int
// maxStack specifies the max length the stack can have for this operation
// to not overflow the stack.
maxStack int
// undefined denotes if the instruction is not officially defined in the jump table
undefined bool
}
@ -114,8 +103,6 @@ func newMergeInstructionSet() JumpTable {
instructionSet[PREVRANDAO] = &operation{
execute: opRandom,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
return validate(instructionSet)
}
@ -156,32 +143,22 @@ func newConstantinopleInstructionSet() JumpTable {
instructionSet[SHL] = &operation{
execute: opSHL,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
}
instructionSet[SHR] = &operation{
execute: opSHR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
}
instructionSet[SAR] = &operation{
execute: opSAR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
}
instructionSet[EXTCODEHASH] = &operation{
execute: opExtCodeHash,
execute: opExtCodeHashConstantinople,
constantGas: params.ExtcodeHashGasConstantinople,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
instructionSet[CREATE2] = &operation{
execute: opCreate2Constantinople,
constantGas: params.Create2Gas,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
}
return validate(instructionSet)
}
@ -193,25 +170,17 @@ func newByzantiumInstructionSet() JumpTable {
instructionSet[STATICCALL] = &operation{
execute: opStaticCallByzantium,
constantGas: params.CallGasEIP150,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
}
instructionSet[RETURNDATASIZE] = &operation{
execute: opReturnDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
}
instructionSet[RETURNDATACOPY] = &operation{
execute: opReturnDataCopy,
constantGas: GasFastestStep,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
}
instructionSet[REVERT] = &operation{
execute: opRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: opRevert,
}
return validate(instructionSet)
}
@ -243,8 +212,6 @@ func newHomesteadInstructionSet() JumpTable {
instructionSet[DELEGATECALL] = &operation{
execute: opDelegateCallHomestead,
constantGas: params.CallGasFrontier,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
}
return validate(instructionSet)
}
@ -256,779 +223,519 @@ func newFrontierInstructionSet() JumpTable {
STOP: {
execute: opStop,
constantGas: 0,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
},
ADD: {
execute: opAdd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
MUL: {
execute: opMul,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
SUB: {
execute: opSub,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
DIV: {
execute: opDiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
SDIV: {
execute: opSdiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
MOD: {
execute: opMod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
SMOD: {
execute: opSmod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
ADDMOD: {
execute: opAddmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
},
MULMOD: {
execute: opMulmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
},
EXP: {
execute: opExpFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
execute: opExpFrontier,
},
SIGNEXTEND: {
execute: opSignExtend,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
LT: {
execute: opLt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
GT: {
execute: opGt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
SLT: {
execute: opSlt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
SGT: {
execute: opSgt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
EQ: {
execute: opEq,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
ISZERO: {
execute: opIszero,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
AND: {
execute: opAnd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
XOR: {
execute: opXor,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
OR: {
execute: opOr,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
NOT: {
execute: opNot,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
BYTE: {
execute: opByte,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
KECCAK256: {
execute: opKeccak256,
constantGas: params.Keccak256Gas,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
ADDRESS: {
execute: opAddress,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
BALANCE: {
execute: opBalance,
execute: opBalanceFrontier,
constantGas: params.BalanceGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
ORIGIN: {
execute: opOrigin,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
CALLER: {
execute: opCaller,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
CALLVALUE: {
execute: opCallValue,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
CALLDATALOAD: {
execute: opCallDataLoad,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
CALLDATASIZE: {
execute: opCallDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
CALLDATACOPY: {
execute: opCallDataCopy,
constantGas: GasFastestStep,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
},
CODESIZE: {
execute: opCodeSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
CODECOPY: {
execute: opCodeCopyFrontier,
constantGas: GasFastestStep,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
},
GASPRICE: {
execute: opGasprice,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
EXTCODESIZE: {
execute: opExtCodeSize,
execute: opExtCodeSizeFrontier,
constantGas: params.ExtcodeSizeGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
EXTCODECOPY: {
execute: opExtCodeCopyFrontier,
constantGas: params.ExtcodeCopyBaseFrontier,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
},
BLOCKHASH: {
execute: opBlockhash,
constantGas: GasExtStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
COINBASE: {
execute: opCoinbase,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
TIMESTAMP: {
execute: opTimestamp,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
NUMBER: {
execute: opNumber,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
DIFFICULTY: {
execute: opDifficulty,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
GASLIMIT: {
execute: opGasLimit,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
POP: {
execute: opPop,
constantGas: GasQuickStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
},
MLOAD: {
execute: opMload,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
MSTORE: {
execute: opMstore,
constantGas: GasFastestStep,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
MSTORE8: {
execute: opMstore8,
constantGas: GasFastestStep,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
SLOAD: {
execute: opSload,
execute: opSLoadFrontier,
constantGas: params.SloadGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
},
SSTORE: {
execute: opSstoreFrontier,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: opSstoreFrontier,
},
JUMP: {
execute: opJump,
constantGas: GasMidStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
},
JUMPI: {
execute: opJumpi,
constantGas: GasSlowStep,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
PC: {
execute: opPc,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
MSIZE: {
execute: opMsize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
GAS: {
execute: opGas,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
JUMPDEST: {
execute: opJumpdest,
constantGas: params.JumpdestGas,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
},
PUSH1: {
execute: opPush1,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH2: {
execute: opPush2,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH3: {
execute: makePush(3, 3),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH4: {
execute: makePush(4, 4),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH5: {
execute: makePush(5, 5),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH6: {
execute: makePush(6, 6),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH7: {
execute: makePush(7, 7),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH8: {
execute: makePush(8, 8),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH9: {
execute: makePush(9, 9),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH10: {
execute: makePush(10, 10),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH11: {
execute: makePush(11, 11),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH12: {
execute: makePush(12, 12),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH13: {
execute: makePush(13, 13),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH14: {
execute: makePush(14, 14),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH15: {
execute: makePush(15, 15),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH16: {
execute: makePush(16, 16),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH17: {
execute: makePush(17, 17),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH18: {
execute: makePush(18, 18),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH19: {
execute: makePush(19, 19),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH20: {
execute: makePush(20, 20),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH21: {
execute: makePush(21, 21),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH22: {
execute: makePush(22, 22),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH23: {
execute: makePush(23, 23),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH24: {
execute: makePush(24, 24),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH25: {
execute: makePush(25, 25),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH26: {
execute: makePush(26, 26),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH27: {
execute: makePush(27, 27),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH28: {
execute: makePush(28, 28),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH29: {
execute: makePush(29, 29),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH30: {
execute: makePush(30, 30),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH31: {
execute: makePush(31, 31),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
PUSH32: {
execute: makePush(32, 32),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
},
DUP1: {
execute: makeDup(1),
execute: opDup1,
constantGas: GasFastestStep,
minStack: minDupStack(1),
maxStack: maxDupStack(1),
},
DUP2: {
execute: makeDup(2),
execute: opDup2,
constantGas: GasFastestStep,
minStack: minDupStack(2),
maxStack: maxDupStack(2),
},
DUP3: {
execute: makeDup(3),
execute: opDup3,
constantGas: GasFastestStep,
minStack: minDupStack(3),
maxStack: maxDupStack(3),
},
DUP4: {
execute: makeDup(4),
execute: opDup4,
constantGas: GasFastestStep,
minStack: minDupStack(4),
maxStack: maxDupStack(4),
},
DUP5: {
execute: makeDup(5),
execute: opDup5,
constantGas: GasFastestStep,
minStack: minDupStack(5),
maxStack: maxDupStack(5),
},
DUP6: {
execute: makeDup(6),
execute: opDup6,
constantGas: GasFastestStep,
minStack: minDupStack(6),
maxStack: maxDupStack(6),
},
DUP7: {
execute: makeDup(7),
execute: opDup7,
constantGas: GasFastestStep,
minStack: minDupStack(7),
maxStack: maxDupStack(7),
},
DUP8: {
execute: makeDup(8),
execute: opDup8,
constantGas: GasFastestStep,
minStack: minDupStack(8),
maxStack: maxDupStack(8),
},
DUP9: {
execute: makeDup(9),
execute: opDup9,
constantGas: GasFastestStep,
minStack: minDupStack(9),
maxStack: maxDupStack(9),
},
DUP10: {
execute: makeDup(10),
execute: opDup10,
constantGas: GasFastestStep,
minStack: minDupStack(10),
maxStack: maxDupStack(10),
},
DUP11: {
execute: makeDup(11),
execute: opDup11,
constantGas: GasFastestStep,
minStack: minDupStack(11),
maxStack: maxDupStack(11),
},
DUP12: {
execute: makeDup(12),
execute: opDup12,
constantGas: GasFastestStep,
minStack: minDupStack(12),
maxStack: maxDupStack(12),
},
DUP13: {
execute: makeDup(13),
execute: opDup13,
constantGas: GasFastestStep,
minStack: minDupStack(13),
maxStack: maxDupStack(13),
},
DUP14: {
execute: makeDup(14),
execute: opDup14,
constantGas: GasFastestStep,
minStack: minDupStack(14),
maxStack: maxDupStack(14),
},
DUP15: {
execute: makeDup(15),
execute: opDup15,
constantGas: GasFastestStep,
minStack: minDupStack(15),
maxStack: maxDupStack(15),
},
DUP16: {
execute: makeDup(16),
execute: opDup16,
constantGas: GasFastestStep,
minStack: minDupStack(16),
maxStack: maxDupStack(16),
},
SWAP1: {
execute: opSwap1,
constantGas: GasFastestStep,
minStack: minSwapStack(2),
maxStack: maxSwapStack(2),
},
SWAP2: {
execute: opSwap2,
constantGas: GasFastestStep,
minStack: minSwapStack(3),
maxStack: maxSwapStack(3),
},
SWAP3: {
execute: opSwap3,
constantGas: GasFastestStep,
minStack: minSwapStack(4),
maxStack: maxSwapStack(4),
},
SWAP4: {
execute: opSwap4,
constantGas: GasFastestStep,
minStack: minSwapStack(5),
maxStack: maxSwapStack(5),
},
SWAP5: {
execute: opSwap5,
constantGas: GasFastestStep,
minStack: minSwapStack(6),
maxStack: maxSwapStack(6),
},
SWAP6: {
execute: opSwap6,
constantGas: GasFastestStep,
minStack: minSwapStack(7),
maxStack: maxSwapStack(7),
},
SWAP7: {
execute: opSwap7,
constantGas: GasFastestStep,
minStack: minSwapStack(8),
maxStack: maxSwapStack(8),
},
SWAP8: {
execute: opSwap8,
constantGas: GasFastestStep,
minStack: minSwapStack(9),
maxStack: maxSwapStack(9),
},
SWAP9: {
execute: opSwap9,
constantGas: GasFastestStep,
minStack: minSwapStack(10),
maxStack: maxSwapStack(10),
},
SWAP10: {
execute: opSwap10,
constantGas: GasFastestStep,
minStack: minSwapStack(11),
maxStack: maxSwapStack(11),
},
SWAP11: {
execute: opSwap11,
constantGas: GasFastestStep,
minStack: minSwapStack(12),
maxStack: maxSwapStack(12),
},
SWAP12: {
execute: opSwap12,
constantGas: GasFastestStep,
minStack: minSwapStack(13),
maxStack: maxSwapStack(13),
},
SWAP13: {
execute: opSwap13,
constantGas: GasFastestStep,
minStack: minSwapStack(14),
maxStack: maxSwapStack(14),
},
SWAP14: {
execute: opSwap14,
constantGas: GasFastestStep,
minStack: minSwapStack(15),
maxStack: maxSwapStack(15),
},
SWAP15: {
execute: opSwap15,
constantGas: GasFastestStep,
minStack: minSwapStack(16),
maxStack: maxSwapStack(16),
},
SWAP16: {
execute: opSwap16,
constantGas: GasFastestStep,
minStack: minSwapStack(17),
maxStack: maxSwapStack(17),
},
LOG0: {
execute: makeLog(0),
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: makeLog(0),
},
LOG1: {
execute: makeLog(1),
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
execute: makeLog(1),
},
LOG2: {
execute: makeLog(2),
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
execute: makeLog(2),
},
LOG3: {
execute: makeLog(3),
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
execute: makeLog(3),
},
LOG4: {
execute: makeLog(4),
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
execute: makeLog(4),
},
CREATE: {
execute: opCreateFrontier,
constantGas: params.CreateGas,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
},
CALL: {
execute: opCallFrontier,
constantGas: params.CallGasFrontier,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
},
CALLCODE: {
execute: opCallCodeFrontier,
constantGas: params.CallGasFrontier,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
},
RETURN: {
execute: opReturn,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: opReturn,
},
SELFDESTRUCT: {
execute: opSelfdestructFrontier,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
execute: opSelfdestructFrontier,
},
INVALID: {
execute: opUndefined,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
execute: opUndefined,
},
}
// Fill all unassigned slots with opUndefined.
for i, entry := range tbl {
if entry == nil {
tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0), undefined: true}
tbl[i] = &operation{execute: opUndefined, undefined: true}
}
}

View file

@ -57,8 +57,3 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
}
return newFrontierInstructionSet(), nil
}
// Stack returns the minimum and maximum stack requirements.
func (op *operation) Stack() (int, int) {
return op.minStack, op.maxStack
}

View file

@ -1,122 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
func memoryKeccak256(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryCallDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(2))
}
func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(2))
}
func memoryCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(2))
}
func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(1), stack.Back(3))
}
func memoryMLoad(stack *Stack) (uint64, bool) {
return calcMemSize64WithUint(stack.Back(0), 32)
}
func memoryMStore8(stack *Stack) (uint64, bool) {
return calcMemSize64WithUint(stack.Back(0), 1)
}
func memoryMStore(stack *Stack) (uint64, bool) {
return calcMemSize64WithUint(stack.Back(0), 32)
}
func memoryMcopy(stack *Stack) (uint64, bool) {
mStart := stack.Back(0) // stack[0]: dest
if stack.Back(1).Gt(mStart) {
mStart = stack.Back(1) // stack[1]: source
}
return calcMemSize64(mStart, stack.Back(2)) // stack[2]: length
}
func memoryCreate(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(1), stack.Back(2))
}
func memoryCreate2(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(1), stack.Back(2))
}
func memoryCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryDelegateCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryStaticCall(stack *Stack) (uint64, bool) {
x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
if overflow {
return 0, true
}
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
}
func memoryReturn(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryRevert(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}
func memoryLog(stack *Stack) (uint64, bool) {
return calcMemSize64(stack.Back(0), stack.Back(1))
}

View file

@ -24,70 +24,68 @@ import (
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.peek()
slot = common.Hash(x.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = uint64(0)
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
value := common.Hash(y.Bytes32())
func gasSStoreWithClearingRefund(evm *EVM, contract *Contract, slot256, value256 *uint256.Int, clearingRefund uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
slot = common.Hash(slot256.Bytes32())
current, original = evm.StateDB.GetStateAndCommittedState(contract.Address(), slot)
cost = uint64(0)
)
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
cost = params.ColdSloadCostEIP2929
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
}
value := common.Hash(value256.Bytes32())
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
if current == value { // noop (1)
// EIP 2200 original clause:
// return params.SloadGasEIP2200, nil
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return cost + params.SstoreSetGasEIP2200, nil
}
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return cost + params.SstoreSetGasEIP2200, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(clearingRefund)
}
// EIP-2200 original clause:
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(clearingRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(clearingRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
// EIP 2200 Original clause:
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(clearingRefund)
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(clearingRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(clearingRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
// EIP 2200 Original clause:
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
} else { // reset to original existing slot (2.2.2.2)
// EIP 2200 Original clause:
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
}
}
// EIP-2200 original clause:
//return params.SloadGasEIP2200, nil // dirty update (2.2)
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
}
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
@ -95,8 +93,7 @@ func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
// whose storage is being read) is not yet in accessed_storage_keys,
// charge 2100 gas and add the pair to accessed_storage_keys.
// If the pair is already in accessed_storage_keys, charge 100 gas.
func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
loc := stack.peek()
func gasSLoadEIP2929(evm *EVM, contract *Contract, loc *uint256.Int) (uint64, error) {
slot := common.Hash(loc.Bytes32())
// Check slot presence in the access list
if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
@ -113,13 +110,13 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// > If the target is not in accessed_addresses,
// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
// > Otherwise, charge WARM_STORAGE_READ_COST gas.
func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasExtCodeCopyEIP2929(evm *EVM, mem *Memory, memorySize uint64, a, length *uint256.Int) (uint64, error) {
// memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
gas, err := memoryCopierGas(mem, memorySize, length)
if err != nil {
return 0, err
}
addr := common.Address(stack.peek().Bytes20())
addr := common.Address(a.Bytes20())
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
@ -140,172 +137,129 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
// - extcodehash,
// - extcodesize,
// - (ext) balance
func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.peek().Bytes20())
func gasEip2929AccountCheck(evm *EVM, addr *uint256.Int) (uint64, error) {
address := common.Address(addr.Bytes20())
// 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
evm.StateDB.AddAddressToAccessList(addr)
evm.StateDB.AddAddressToAccessList(address)
// The warm storage read cost is already charged as constantGas
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
}
return 0, nil
}
func makeCallVariantGasCallEIP2929(oldCalculator gasFunc, addressPosition int) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
addr := common.Address(stack.Back(addressPosition).Bytes20())
// Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr)
func gasCallEIP2929(evm *EVM, contract *Contract, address *uint256.Int, oldCalculator func() (uint64, error)) (uint64, error) {
addr := common.Address(address.Bytes20())
// Check slot presence in the access list
warmAccess := evm.StateDB.AddressInAccessList(addr)
// 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
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !warmAccess {
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
}
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
gas, err := oldCalculator()
if warmAccess || err != nil {
return gas, err
}
// In case of a cold access, we temporarily add the cold charge back, and also
// add it to the returned gas. By adding it to the return, it will be charged
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.
contract.Gas += coldCost
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
}
func gasSelfdestructEIP(evm *EVM, contract *Contract, addr *uint256.Int, refundsEnabled bool) (uint64, error) {
var (
gas uint64
address = common.Address(addr.Bytes20())
)
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(address)
gas = params.ColdAccountAccessCostEIP2929
}
// if empty and transfers value
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas += params.CreateBySelfdestructGas
}
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
}
func gasCallEIP7702(evm *EVM, contract *Contract, address *uint256.Int, oldCalculator func() (uint64, error)) (uint64, error) {
var (
total uint64 // total dynamic gas used
addr = common.Address(address.Bytes20())
)
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
// 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
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
if !warmAccess {
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
if warmAccess || err != nil {
return gas, err
}
// In case of a cold access, we temporarily add the cold charge back, and also
// add it to the returned gas. By adding it to the return, it will be charged
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.
contract.Gas += coldCost
var overflow bool
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
return 0, ErrGasUintOverflow
}
return gas, nil
total += coldCost
}
}
var (
gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall, 1)
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall, 1)
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall, 1)
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode, 1)
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
//
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
// Additionally, modify the parameters defined in EIP 2200 as follows:
//
// Parameter Old value New value
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
//
//The other parameters defined in EIP 2200 are unchanged.
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
)
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
address = common.Address(stack.peek().Bytes20())
)
if !evm.StateDB.AddressInAccessList(address) {
// If the caller cannot afford the cost, this change will be rolled back
evm.StateDB.AddAddressToAccessList(address)
gas = params.ColdAccountAccessCostEIP2929
// Check if code is a delegation and if so, charge for resolution.
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
var cost uint64
if evm.StateDB.AddressInAccessList(target) {
cost = params.WarmStorageReadCostEIP2929
} else {
evm.StateDB.AddAddressToAccessList(target)
cost = params.ColdAccountAccessCostEIP2929
}
// if empty and transfers value
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas += params.CreateBySelfdestructGas
if !contract.UseGas(cost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
if refundsEnabled && !evm.StateDB.HasSelfDestructed(contract.Address()) {
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
total += cost
}
return gasFunc
}
var (
gasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCall)
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCall)
gasStaticCallEIP7702 = makeCallVariantGasCallEIP7702(gasStaticCall)
gasCallCodeEIP7702 = makeCallVariantGasCallEIP7702(gasCallCode)
)
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
total uint64 // total dynamic gas used
addr = common.Address(stack.Back(1).Bytes20())
)
// Check slot presence in the access list
if !evm.StateDB.AddressInAccessList(addr) {
evm.StateDB.AddAddressToAccessList(addr)
// 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
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += coldCost
}
// Check if code is a delegation and if so, charge for resolution.
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
var cost uint64
if evm.StateDB.AddressInAccessList(target) {
cost = params.WarmStorageReadCostEIP2929
} else {
evm.StateDB.AddAddressToAccessList(target)
cost = params.ColdAccountAccessCostEIP2929
}
if !contract.UseGas(cost, evm.Config.Tracer.OnGasChange, tracing.GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
total += cost
}
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
if err != nil {
return old, err
}
// Temporarily add the gas charge back to the contract and return value. By
// adding it to the return, it will be charged outside of this function, as
// part of the dynamic gas. This will ensure it is correctly reported to
// tracers.
contract.Gas += total
var overflow bool
if total, overflow = math.SafeAdd(old, total); overflow {
return 0, ErrGasUintOverflow
}
return total, nil
// Now call the old calculator, which takes into account
// - create new account
// - transfer value
// - memory expansion
// - 63/64ths rule
old, err := oldCalculator()
if err != nil {
return old, err
}
// Temporarily add the gas charge back to the contract and return value. By
// adding it to the return, it will be charged outside of this function, as
// part of the dynamic gas. This will ensure it is correctly reported to
// tracers.
contract.Gas += total
var overflow bool
if total, overflow = math.SafeAdd(old, total); overflow {
return 0, ErrGasUintOverflow
}
return total, nil
}

View file

@ -22,93 +22,84 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
func gasSStore4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), true, contract.Gas, true), nil
func gasSStore4762(evm *EVM, contract *Contract, loc *uint256.Int) (uint64, error) {
return evm.AccessEvents.SlotGas(contract.Address(), loc.Bytes32(), true, contract.Gas, true), nil
}
func gasSLoad4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return evm.AccessEvents.SlotGas(contract.Address(), stack.peek().Bytes32(), false, contract.Gas, true), nil
func gasSLoad4762(evm *EVM, contract *Contract, loc *uint256.Int) (uint64, error) {
return evm.AccessEvents.SlotGas(contract.Address(), loc.Bytes32(), false, contract.Gas, true), nil
}
func gasBalance4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
func gasBalance4762(evm *EVM, contract *Contract, addr *uint256.Int) (uint64, error) {
return evm.AccessEvents.BasicDataGas(addr.Bytes20(), false, contract.Gas, true), nil
}
func gasExtCodeSize4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
func gasExtCodeSize4762(evm *EVM, contract *Contract, addr *uint256.Int) (uint64, error) {
address := addr.Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile {
return 0, nil
}
return evm.AccessEvents.BasicDataGas(address, false, contract.Gas, true), nil
}
func gasExtCodeHash4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
address := stack.peek().Bytes20()
func gasExtCodeHash4762(evm *EVM, contract *Contract, addr *uint256.Int) (uint64, error) {
address := addr.Bytes20()
if _, isPrecompile := evm.precompile(address); isPrecompile {
return 0, nil
}
return evm.AccessEvents.CodeHashGas(address, false, contract.Gas, true), nil
}
func makeCallVariantGasEIP4762(oldCalculator gasFunc, withTransferCosts bool) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
target = common.Address(stack.Back(1).Bytes20())
witnessGas uint64
_, isPrecompile = evm.precompile(target)
isSystemContract = target == params.HistoryStorageAddress
)
func gasCallEIP4762(evm *EVM, contract *Contract, oldCalculator func() (uint64, error), addr, value *uint256.Int, withTransferCosts bool) (uint64, error) {
var (
target = common.Address(addr.Bytes20())
witnessGas uint64
_, isPrecompile = evm.precompile(target)
isSystemContract = target == params.HistoryStorageAddress
)
// If value is transferred, it is charged before 1/64th
// is subtracted from the available gas pool.
if withTransferCosts && !stack.Back(2).IsZero() {
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas)
if wantedValueTransferWitnessGas > contract.Gas {
return wantedValueTransferWitnessGas, nil
}
witnessGas = wantedValueTransferWitnessGas
} else if isPrecompile || isSystemContract {
witnessGas = params.WarmStorageReadCostEIP2929
} else {
// The charging for the value transfer is done BEFORE subtracting
// the 1/64th gas, as this is considered part of the CALL instruction.
// (so before we get to this point)
// But the message call is part of the subcall, for which only 63/64th
// of the gas should be available.
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas)
var overflow bool
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
return 0, ErrGasUintOverflow
}
if witnessGas > contract.Gas {
return witnessGas, nil
}
// If value is transferred, it is charged before 1/64th
// is subtracted from the available gas pool.
if withTransferCosts && !value.IsZero() {
wantedValueTransferWitnessGas := evm.AccessEvents.ValueTransferGas(contract.Address(), target, contract.Gas)
if wantedValueTransferWitnessGas > contract.Gas {
return wantedValueTransferWitnessGas, nil
}
contract.Gas -= witnessGas
// if the operation fails, adds witness gas to the gas before returning the error
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite
witnessGas = wantedValueTransferWitnessGas
} else if isPrecompile || isSystemContract {
witnessGas = params.WarmStorageReadCostEIP2929
} else {
// The charging for the value transfer is done BEFORE subtracting
// the 1/64th gas, as this is considered part of the CALL instruction.
// (so before we get to this point)
// But the message call is part of the subcall, for which only 63/64th
// of the gas should be available.
wantedMessageCallWitnessGas := evm.AccessEvents.MessageCallGas(target, contract.Gas-witnessGas)
var overflow bool
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
if witnessGas, overflow = math.SafeAdd(witnessGas, wantedMessageCallWitnessGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, err
if witnessGas > contract.Gas {
return witnessGas, nil
}
}
contract.Gas -= witnessGas
// if the operation fails, adds witness gas to the gas before returning the error
gas, err := oldCalculator()
contract.Gas += witnessGas // restore witness gas so that it can be charged at the callsite
var overflow bool
if gas, overflow = math.SafeAdd(gas, witnessGas); overflow {
return 0, ErrGasUintOverflow
}
return gas, err
}
var (
gasCallEIP4762 = makeCallVariantGasEIP4762(gasCall, true)
gasCallCodeEIP4762 = makeCallVariantGasEIP4762(gasCallCode, false)
gasStaticCallEIP4762 = makeCallVariantGasEIP4762(gasStaticCall, false)
gasDelegateCallEIP4762 = makeCallVariantGasEIP4762(gasDelegateCall, false)
)
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
beneficiaryAddr := common.Address(stack.peek().Bytes20())
func gasSelfdestructEIP4762(evm *EVM, contract *Contract, beneficiary *uint256.Int) (uint64, error) {
beneficiaryAddr := common.Address(beneficiary.Bytes20())
if _, isPrecompile := evm.precompile(beneficiaryAddr); isPrecompile {
return 0, nil
}
@ -159,16 +150,12 @@ func gasSelfdestructEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Mem
return statelessGas, nil
}
func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := gasCodeCopy(evm, contract, stack, mem, memorySize)
func gasCodeCopyEip4762(evm *EVM, contract *Contract, mem *Memory, memorySize uint64, codeOffset, length *uint256.Int) (uint64, error) {
gas, err := memoryCopierGas(mem, memorySize, length)
if err != nil {
return 0, err
}
if !contract.IsDeployment && !contract.IsSystemCall {
var (
codeOffset = stack.Back(1)
length = stack.Back(2)
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
uint64CodeOffset = gomath.MaxUint64
@ -181,13 +168,13 @@ func gasCodeCopyEip4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory,
return gas, nil
}
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
func gasExtCodeCopyEIP4762(evm *EVM, contract *Contract, mem *Memory, memorySize uint64, a, length *uint256.Int) (uint64, error) {
// memory expansion first (dynamic part of pre-2929 implementation)
gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
gas, err := memoryCopierGas(mem, memorySize, length)
if err != nil {
return 0, err
}
addr := common.Address(stack.peek().Bytes20())
addr := common.Address(a.Bytes20())
_, isPrecompile := evm.precompile(addr)
if isPrecompile || addr == params.HistoryStorageAddress {
var overflow bool

View file

@ -17,8 +17,11 @@
package vm
import (
"reflect"
"sync"
"unsafe"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@ -49,79 +52,411 @@ func (st *Stack) Data() []uint256.Int {
return st.data
}
func (st *Stack) push(d *uint256.Int) {
// NOTE push limit (1024) is checked in baseCheck
st.data = append(st.data, *d)
}
func (st *Stack) pop() (ret uint256.Int) {
ret = st.data[len(st.data)-1]
st.data = st.data[:len(st.data)-1]
return
}
func (st *Stack) len() int {
return len(st.data)
}
func (st *Stack) swap1() {
st.data[st.len()-2], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-2]
}
func (st *Stack) swap2() {
st.data[st.len()-3], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-3]
}
func (st *Stack) swap3() {
st.data[st.len()-4], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-4]
}
func (st *Stack) swap4() {
st.data[st.len()-5], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-5]
}
func (st *Stack) swap5() {
st.data[st.len()-6], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-6]
}
func (st *Stack) swap6() {
st.data[st.len()-7], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-7]
}
func (st *Stack) swap7() {
st.data[st.len()-8], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-8]
}
func (st *Stack) swap8() {
st.data[st.len()-9], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-9]
}
func (st *Stack) swap9() {
st.data[st.len()-10], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-10]
}
func (st *Stack) swap10() {
st.data[st.len()-11], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-11]
}
func (st *Stack) swap11() {
st.data[st.len()-12], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-12]
}
func (st *Stack) swap12() {
st.data[st.len()-13], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-13]
}
func (st *Stack) swap13() {
st.data[st.len()-14], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-14]
}
func (st *Stack) swap14() {
st.data[st.len()-15], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-15]
}
func (st *Stack) swap15() {
st.data[st.len()-16], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-16]
}
func (st *Stack) swap16() {
st.data[st.len()-17], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-17]
func (st *Stack) growUnsafeByN(n uint) {
sh := (*reflect.SliceHeader)(unsafe.Pointer(&st.data))
sh.Len += int(n)
}
func (st *Stack) dup(n int) {
st.push(&st.data[st.len()-n])
func (st *Stack) push(d *uint256.Int) error {
if uint64(st.len()) >= params.StackLimit {
return ErrStackOverflow{st.len(), int(params.StackLimit)}
}
st.data = append(st.data, *d)
return nil
}
func (st *Stack) peek() *uint256.Int {
return &st.data[st.len()-1]
func (st *Stack) pop(peekLastN uint) (*uint256.Int, error) {
const requiredDepth = 1
depth := st.len()
if depth < requiredDepth {
return nil, ErrStackUnderflow{st.len(), requiredDepth}
}
stack := st.data
st.data = st.data[:depth-requiredDepth]
st.growUnsafeByN(peekLastN)
return &stack[depth-1], nil
}
// Back returns the n'th item in stack
func (st *Stack) Back(n int) *uint256.Int {
return &st.data[st.len()-n-1]
func (st *Stack) pop2(peekLastN uint) (*uint256.Int, *uint256.Int, error) {
const requiredDepth = 2
depth := st.len()
if depth < requiredDepth {
return nil, nil, ErrStackUnderflow{st.len(), requiredDepth}
}
stack := st.data
st.data = st.data[:depth-requiredDepth]
st.growUnsafeByN(peekLastN)
return &stack[depth-1], &stack[depth-2], nil
}
func (st *Stack) pop3(peekLastN uint) (*uint256.Int, *uint256.Int, *uint256.Int, error) {
const requiredDepth = 3
depth := st.len()
if depth < requiredDepth {
return nil, nil, nil, ErrStackUnderflow{st.len(), requiredDepth}
}
stack := st.data
st.data = st.data[:depth-requiredDepth]
st.growUnsafeByN(peekLastN)
return &stack[depth-1], &stack[depth-2], &stack[depth-3], nil
}
func (st *Stack) pop4(peekLastN uint) (*uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, error) {
const requiredDepth = 4
depth := st.len()
if depth < requiredDepth {
return nil, nil, nil, nil, ErrStackUnderflow{st.len(), requiredDepth}
}
stack := st.data
st.data = st.data[:depth-requiredDepth]
st.growUnsafeByN(peekLastN)
return &stack[depth-1], &stack[depth-2], &stack[depth-3], &stack[depth-4], nil
}
func (st *Stack) pop6(peekLastN uint) (*uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, error) {
const requiredDepth = 6
depth := st.len()
if depth < requiredDepth {
return nil, nil, nil, nil, nil, nil, ErrStackUnderflow{st.len(), requiredDepth}
}
stack := st.data
st.data = st.data[:depth-requiredDepth]
st.growUnsafeByN(peekLastN)
return &stack[depth-1], &stack[depth-2], &stack[depth-3], &stack[depth-4], &stack[depth-5], &stack[depth-6], nil
}
func (st *Stack) pop7(peekLastN uint) (*uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, error) {
const requiredDepth = 7
depth := st.len()
if depth < requiredDepth {
return nil, nil, nil, nil, nil, nil, nil, ErrStackUnderflow{st.len(), requiredDepth}
}
stack := st.data
st.data = st.data[:depth-requiredDepth]
st.growUnsafeByN(peekLastN)
return &stack[depth-1], &stack[depth-2], &stack[depth-3], &stack[depth-4], &stack[depth-5], &stack[depth-6], &stack[depth-7], nil
}
func (st *Stack) swap1() error {
const requiredDepth = 2
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap2() error {
const requiredDepth = 3
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap3() error {
const requiredDepth = 4
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap4() error {
const requiredDepth = 5
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap5() error {
const requiredDepth = 6
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap6() error {
const requiredDepth = 7
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap7() error {
const requiredDepth = 8
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap8() error {
const requiredDepth = 9
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap9() error {
const requiredDepth = 10
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap10() error {
const requiredDepth = 11
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap11() error {
const requiredDepth = 12
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap12() error {
const requiredDepth = 13
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap13() error {
const requiredDepth = 14
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap14() error {
const requiredDepth = 15
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap15() error {
const requiredDepth = 16
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) swap16() error {
const requiredDepth = 17
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
topOfStack := st.data[depth-requiredDepth:]
topOfStack[0], topOfStack[len(topOfStack)-1] = topOfStack[len(topOfStack)-1], topOfStack[0]
return nil
}
func (st *Stack) dup1() error {
const requiredDepth = 1
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup2() error {
const requiredDepth = 2
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup3() error {
const requiredDepth = 3
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup4() error {
const requiredDepth = 4
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup5() error {
const requiredDepth = 5
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup6() error {
const requiredDepth = 6
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup7() error {
const requiredDepth = 7
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup8() error {
const requiredDepth = 8
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup9() error {
const requiredDepth = 9
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup10() error {
const requiredDepth = 10
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup11() error {
const requiredDepth = 11
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup12() error {
const requiredDepth = 12
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup13() error {
const requiredDepth = 13
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup14() error {
const requiredDepth = 14
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup15() error {
const requiredDepth = 15
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}
func (st *Stack) dup16() error {
const requiredDepth = 16
depth := st.len()
if depth < requiredDepth {
return ErrStackUnderflow{st.len(), requiredDepth}
}
return st.push(&st.data[depth-requiredDepth])
}

View file

@ -1,42 +0,0 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"github.com/ethereum/go-ethereum/params"
)
func minSwapStack(n int) int {
return minStack(n, n)
}
func maxSwapStack(n int) int {
return maxStack(n, n)
}
func minDupStack(n int) int {
return minStack(n, n+1)
}
func maxDupStack(n int) int {
return maxStack(n, n+1)
}
func maxStack(pop, push int) int {
return int(params.StackLimit) + pop - push
}
func minStack(pops, push int) int {
return pops
}

View file

@ -286,7 +286,7 @@ func TestInternals(t *testing.T) {
name: "Stack depletion in LOG0",
code: []byte{byte(vm.LOG3)},
tracer: mkTracer("callTracer", json.RawMessage(`{ "withLog": true }`)),
want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x13880","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 5)","value":"0x0","type":"CALL"}`, originHex),
want: fmt.Sprintf(`{"from":"%s","gas":"0x13880","gasUsed":"0x13880","to":"0x00000000000000000000000000000000deadbeef","input":"0x","error":"stack underflow (0 \u003c=\u003e 2)","value":"0x0","type":"CALL"}`, originHex),
},
{
name: "Mem expansion in LOG0",