From 27d6bed9302a250aa17393b7a4be4719bf1ff10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20IRMAK?= Date: Tue, 8 Jul 2025 19:45:52 +0300 Subject: [PATCH] core/vm: refactor stack for less implicit bound checks --- core/vm/eips.go | 163 +-- core/vm/evm.go | 2 +- core/vm/gas_table.go | 196 ++- core/vm/instructions.go | 1083 +++++++++++------ core/vm/instructions_test.go | 70 +- core/vm/interpreter.go | 9 +- core/vm/interpreter_test.go | 8 +- core/vm/jump_table.go | 359 +----- core/vm/jump_table_export.go | 5 - core/vm/memory_table.go | 122 -- core/vm/operations_acl.go | 376 +++--- core/vm/operations_verkle.go | 123 +- core/vm/stack.go | 465 ++++++- core/vm/stack_table.go | 42 - .../internal/tracetest/calltrace_test.go | 2 +- 15 files changed, 1541 insertions(+), 1484 deletions(-) delete mode 100644 core/vm/memory_table.go delete mode 100644 core/vm/stack_table.go diff --git a/core/vm/eips.go b/core/vm/eips.go index ccb099c5e1..caa73e4391 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -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), } } } diff --git a/core/vm/evm.go b/core/vm/evm.go index 2702ef9c5d..514849d96f 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -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) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index c7c1274bf2..8282167f5c 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -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 diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 95c31a0987..7cb65a82d8 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -29,8 +29,35 @@ import ( // calculateMemorySize calculates the required memory size // it is important that this function is inlinable -func calculateMemorySize(memF memorySizeFunc, stack *Stack, mem *Memory) (uint64, error) { - memSize, overflow := memF(stack) +func calculateMemorySize(offset, length *uint256.Int) (uint64, error) { + memSize, overflow := calcMemSize64(offset, length) + return memorySizeCeil(memSize, overflow) +} + +// calculateMemorySizeU64 calculates the required memory size +// it is important that this function is inlinable +func calculateMemorySizeU64(offset *uint256.Int, length uint64) (uint64, error) { + memSize, overflow := calcMemSize64WithUint(offset, length) + return memorySizeCeil(memSize, overflow) +} + +// calculateCallMemorySize calculates the required memory size for a call operation +func calculateCallMemorySize(argOffset, argSize, retOffset, retSize *uint256.Int) (uint64, error) { + x, overflow := calcMemSize64(retOffset, retSize) + if overflow { + return 0, ErrGasUintOverflow + } + y, overflow := calcMemSize64(argOffset, argSize) + if overflow { + return 0, ErrGasUintOverflow + } + if x > y { + return memorySizeCeil(x, false) + } + return memorySizeCeil(y, false) +} + +func memorySizeCeil(memSize uint64, overflow bool) (uint64, error) { // memory is expanded in words of 32 bytes. Gas // is also calculated in words. if overflow || memSize > math.MaxUint64-31 { @@ -41,49 +68,74 @@ func calculateMemorySize(memF memorySizeFunc, stack *Stack, mem *Memory) (uint64 } func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Add(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Add(x, y) return nil, nil } func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Sub(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Sub(x, y) return nil, nil } func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Mul(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Mul(x, y) return nil, nil } func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Div(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Div(x, y) return nil, nil } func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.SDiv(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.SDiv(x, y) return nil, nil } func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Mod(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Mod(x, y) return nil, nil } func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.SMod(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.SMod(x, y) return nil, nil } func opExpEIP158(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasExpEIP158(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + base, exponent, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasExpEIP158(exponent) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -91,11 +143,15 @@ func opExpEIP158(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( return nil, ErrOutOfGas } - return opExp(pc, interpreter, scope) + return opExp(base, exponent) } func opExpFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasExpFrontier(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + base, exponent, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasExpFrontier(exponent) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -103,29 +159,39 @@ func opExpFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) return nil, ErrOutOfGas } - return opExp(pc, interpreter, scope) + return opExp(base, exponent) } -func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - base, exponent := scope.Stack.pop(), scope.Stack.peek() - exponent.Exp(&base, exponent) +func opExp(base, exponent *uint256.Int) ([]byte, error) { + exponent.Exp(base, exponent) return nil, nil } func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - back, num := scope.Stack.pop(), scope.Stack.peek() - num.ExtendSign(num, &back) + back, num, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + + num.ExtendSign(num, back) return nil, nil } func opNot(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.Not(x) return nil, nil } func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if x.Lt(y) { y.SetOne() } else { @@ -135,7 +201,10 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, } func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if x.Gt(y) { y.SetOne() } else { @@ -145,7 +214,10 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, } func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if x.Slt(y) { y.SetOne() } else { @@ -155,7 +227,10 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte } func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if x.Sgt(y) { y.SetOne() } else { @@ -165,7 +240,10 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte } func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if x.Eq(y) { y.SetOne() } else { @@ -175,7 +253,11 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, } func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() + x, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + if x.IsZero() { x.SetOne() } else { @@ -185,38 +267,56 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.And(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.And(x, y) return nil, nil } func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Or(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Or(x, y) return nil, nil } func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y := scope.Stack.pop(), scope.Stack.peek() - y.Xor(&x, y) + x, y, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + y.Xor(x, y) return nil, nil } func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - th, val := scope.Stack.pop(), scope.Stack.peek() - val.Byte(&th) + th, val, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } + val.Byte(th) return nil, nil } func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() - z.AddMod(&x, &y, z) + x, y, z, err := scope.Stack.pop3(1) + if err != nil { + return nil, err + } + z.AddMod(x, y, z) return nil, nil } func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() - z.MulMod(&x, &y, z) + x, y, z, err := scope.Stack.pop3(1) + if err != nil { + return nil, err + } + z.MulMod(x, y, z) return nil, nil } @@ -225,7 +325,10 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b // and pushes on the stack arg2 shifted to the left by arg1 number of bits. func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if shift.LtUint64(256) { value.Lsh(value, uint(shift.Uint64())) } else { @@ -239,7 +342,10 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill. func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if shift.LtUint64(256) { value.Rsh(value, uint(shift.Uint64())) } else { @@ -252,7 +358,10 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2, // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension. func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - shift, value := scope.Stack.pop(), scope.Stack.peek() + shift, value, err := scope.Stack.pop2(1) + if err != nil { + return nil, err + } if shift.GtUint64(256) { if value.Sign() >= 0 { value.Clear() @@ -268,11 +377,15 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte } func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryKeccak256, scope.Stack, scope.Memory) + offset, size, err := scope.Stack.pop2(1) if err != nil { return nil, err } - dynamicCost, err := gasKeccak256(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := gasKeccak256(scope.Memory, memorySize, size) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -281,9 +394,7 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } scope.Memory.Resize(memorySize) - offset, size := scope.Stack.pop(), scope.Stack.peek() data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - interpreter.hasher.Reset() interpreter.hasher.Write(data) interpreter.hasher.Read(interpreter.hasherBuf[:]) @@ -297,12 +408,15 @@ func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes())) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes())) } func opBalanceEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasBalance4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasBalance4762(interpreter.evm, scope.Contract, slot) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -310,11 +424,15 @@ func opBalanceEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte return nil, ErrOutOfGas } - return opBalance(pc, interpreter, scope) + return opBalance(interpreter, slot) } func opBalanceEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, slot) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -322,33 +440,40 @@ func opBalanceEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte return nil, ErrOutOfGas } - return opBalance(pc, interpreter, scope) + return opBalance(interpreter, slot) } -func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() +func opBalanceFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + return opBalance(interpreter, slot) +} + +func opBalance(interpreter *EVMInterpreter, slot *uint256.Int) ([]byte, error) { address := common.Address(slot.Bytes20()) slot.Set(interpreter.evm.StateDB.GetBalance(address)) return nil, nil } func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes())) } func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes())) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes())) } func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(scope.Contract.value) - return nil, nil + return nil, scope.Stack.push(scope.Contract.value) } func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - x := scope.Stack.peek() + x, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } if offset, overflow := x.Uint64WithOverflow(); !overflow { data := getData(scope.Contract.Input, offset, 32) x.SetBytes(data) @@ -359,16 +484,20 @@ func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input)))) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input)))) } func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCallDataCopy, scope.Stack, scope.Memory) + memOffset, dataOffset, length, err := scope.Stack.pop3(0) if err != nil { return nil, err } - dynamicCost, err := gasCallDataCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + + memorySize, err := calculateMemorySize(memOffset, 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) } @@ -377,11 +506,6 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } scope.Memory.Resize(memorySize) - var ( - memOffset = scope.Stack.pop() - dataOffset = scope.Stack.pop() - length = scope.Stack.pop() - ) dataOffset64, overflow := dataOffset.Uint64WithOverflow() if overflow { dataOffset64 = math.MaxUint64 @@ -395,16 +519,19 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData)))) } func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryReturnDataCopy, scope.Stack, scope.Memory) + memOffset, dataOffset, length, err := scope.Stack.pop3(0) if err != nil { return nil, err } - dynamicCost, err := gasReturnDataCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(memOffset, 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) } @@ -413,19 +540,13 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } scope.Memory.Resize(memorySize) - var ( - memOffset = scope.Stack.pop() - dataOffset = scope.Stack.pop() - length = scope.Stack.pop() - ) - offset64, overflow := dataOffset.Uint64WithOverflow() if overflow { return nil, ErrReturnDataOutOfBounds } // we can reuse dataOffset now (aliasing it for clarity) var end = dataOffset - end.Add(&dataOffset, &length) + end.Add(dataOffset, length) end64, overflow := end.Uint64WithOverflow() if overflow || uint64(len(interpreter.returnData)) < end64 { return nil, ErrReturnDataOutOfBounds @@ -435,44 +556,62 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } func opExtCodeSizeEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasExtCodeSize4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasExtCodeSize4762(interpreter.evm, scope.Contract, slot) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } if !scope.Contract.UseGas(dynamicCost, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallOpCodeDynamic) { return nil, ErrOutOfGas } - return opExtCodeSize(pc, interpreter, scope) + return opExtCodeSize(interpreter, slot) } func opExtCodeSizeEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, slot) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } if !scope.Contract.UseGas(dynamicCost, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallOpCodeDynamic) { return nil, ErrOutOfGas } - return opExtCodeSize(pc, interpreter, scope) + return opExtCodeSize(interpreter, slot) } -func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() +func opExtCodeSizeFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + return opExtCodeSize(interpreter, slot) +} + +func opExtCodeSize(interpreter *EVMInterpreter, slot *uint256.Int) ([]byte, error) { slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20()))) return nil, nil } func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code)))) } func opCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCodeCopy, scope.Stack, scope.Memory) + memOffset, codeOffset, length, err := scope.Stack.pop3(0) if err != nil { return nil, err } - dynamicCost, err := gasCodeCopyEip4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(memOffset, length) + if err != nil { + return nil, err + } + dynamicCost, err := gasCodeCopyEip4762(interpreter.evm, scope.Contract, scope.Memory, memorySize, codeOffset, length) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -480,15 +619,19 @@ func opCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCont return nil, ErrOutOfGas } scope.Memory.Resize(memorySize) - return opCodeCopy(pc, interpreter, scope) + return opCodeCopy(scope, memOffset, codeOffset, length) } func opCodeCopyFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCodeCopy, scope.Stack, scope.Memory) + memOffset, codeOffset, length, err := scope.Stack.pop3(0) if err != nil { return nil, err } - dynamicCost, err := gasCodeCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(memOffset, 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) } @@ -496,15 +639,10 @@ func opCodeCopyFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon return nil, ErrOutOfGas } scope.Memory.Resize(memorySize) - return opCodeCopy(pc, interpreter, scope) + return opCodeCopy(scope, memOffset, codeOffset, length) } -func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - var ( - memOffset = scope.Stack.pop() - codeOffset = scope.Stack.pop() - length = scope.Stack.pop() - ) +func opCodeCopy(scope *ScopeContext, memOffset, codeOffset, length *uint256.Int) ([]byte, error) { uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { uint64CodeOffset = math.MaxUint64 @@ -516,11 +654,15 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opExtCodeCopyEIP2929(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 := gasExtCodeCopyEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(memOffset, length) + if err != nil { + return nil, err + } + dynamicCost, err := gasExtCodeCopyEIP2929(interpreter.evm, scope.Memory, memorySize, a, length) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -528,15 +670,19 @@ func opExtCodeCopyEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC return nil, ErrOutOfGas } scope.Memory.Resize(memorySize) - return opExtCodeCopy(pc, interpreter, scope) + return opExtCodeCopy(interpreter, scope, a, memOffset, codeOffset, length) } func opExtCodeCopyFrontier(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 := gasExtCodeCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(memOffset, 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) } @@ -544,17 +690,10 @@ func opExtCodeCopyFrontier(pc *uint64, interpreter *EVMInterpreter, scope *Scope return nil, ErrOutOfGas } scope.Memory.Resize(memorySize) - return opExtCodeCopy(pc, interpreter, scope) + return opExtCodeCopy(interpreter, scope, a, memOffset, codeOffset, length) } -func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - var ( - stack = scope.Stack - a = stack.pop() - memOffset = stack.pop() - codeOffset = stack.pop() - length = stack.pop() - ) +func opExtCodeCopy(interpreter *EVMInterpreter, scope *ScopeContext, a, memOffset, codeOffset, length *uint256.Int) ([]byte, error) { uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() if overflow { uint64CodeOffset = math.MaxUint64 @@ -568,25 +707,41 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } func opExtCodeHashEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasExtCodeHash4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasExtCodeHash4762(interpreter.evm, scope.Contract, slot) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } if !scope.Contract.UseGas(dynamicCost, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallOpCodeDynamic) { return nil, ErrOutOfGas } - return opExtCodeHash(pc, interpreter, scope) + return opExtCodeHash(interpreter, slot) } func opExtCodeHashEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, slot) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } if !scope.Contract.UseGas(dynamicCost, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallOpCodeDynamic) { return nil, ErrOutOfGas } - return opExtCodeHash(pc, interpreter, scope) + return opExtCodeHash(interpreter, slot) +} + +func opExtCodeHashConstantinople(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + slot, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + return opExtCodeHash(interpreter, slot) } // opExtCodeHash returns the code hash of a specified account. @@ -615,8 +770,7 @@ func opExtCodeHashEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC // // 6. Caller tries to get the code hash for an account which is marked as deleted, this // account should be regarded as a non-existent account and zero should be returned. -func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - slot := scope.Stack.peek() +func opExtCodeHash(interpreter *EVMInterpreter, slot *uint256.Int) ([]byte, error) { address := common.Address(slot.Bytes20()) if interpreter.evm.StateDB.Empty(address) { slot.Clear() @@ -628,12 +782,14 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.GasPrice) - scope.Stack.push(v) - return nil, nil + return nil, scope.Stack.push(v) } func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - num := scope.Stack.peek() + num, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } num64, overflow := num.Uint64WithOverflow() if overflow { num.Clear() @@ -663,49 +819,47 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( } func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes())) } func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time)) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time)) } func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber) - scope.Stack.push(v) - return nil, nil + return nil, scope.Stack.push(v) } func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty) - scope.Stack.push(v) - return nil, nil + return nil, scope.Stack.push(v) } func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { v := new(uint256.Int).SetBytes(interpreter.evm.Context.Random.Bytes()) - scope.Stack.push(v) - return nil, nil + return nil, scope.Stack.push(v) } func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit)) } func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.pop() - return nil, nil + _, err := scope.Stack.pop(0) + return nil, err } func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryMLoad, scope.Stack, scope.Memory) + v, err := scope.Stack.pop(1) if err != nil { return nil, err } - dynamicCost, err := gasMLoad(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySizeU64(v, 32) + if err != nil { + return nil, err + } + dynamicCost, err := memoryGasCost(scope.Memory, memorySize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -714,18 +868,21 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by } scope.Memory.Resize(memorySize) - v := scope.Stack.peek() offset := v.Uint64() v.SetBytes(scope.Memory.GetPtr(offset, 32)) return nil, nil } func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryMStore, scope.Stack, scope.Memory) + mStart, val, err := scope.Stack.pop2(0) if err != nil { return nil, err } - dynamicCost, err := gasMStore(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySizeU64(mStart, 32) + if err != nil { + return nil, err + } + dynamicCost, err := memoryGasCost(scope.Memory, memorySize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -734,17 +891,21 @@ func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Memory.Resize(memorySize) - mStart, val := scope.Stack.pop(), scope.Stack.pop() - scope.Memory.Set32(mStart.Uint64(), &val) + scope.Memory.Set32(mStart.Uint64(), val) return nil, nil } func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryMStore8, scope.Stack, scope.Memory) + off, val, err := scope.Stack.pop2(0) if err != nil { return nil, err } - dynamicCost, err := gasMStore8(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + + memorySize, err := calculateMemorySizeU64(off, 1) + if err != nil { + return nil, err + } + dynamicCost, err := memoryGasCost(scope.Memory, memorySize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -753,35 +914,50 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } scope.Memory.Resize(memorySize) - off, val := scope.Stack.pop(), scope.Stack.pop() scope.Memory.store[off.Uint64()] = byte(val.Uint64()) return nil, nil } func opSLoadEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSLoad4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + + dynamicCost, err := gasSLoad4762(interpreter.evm, scope.Contract, loc) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } if !scope.Contract.UseGas(dynamicCost, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallOpCodeDynamic) { return nil, ErrOutOfGas } - return opSload(pc, interpreter, scope) + return opSload(interpreter, scope, loc) } func opSLoadEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSLoadEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + dynamicCost, err := gasSLoadEIP2929(interpreter.evm, scope.Contract, loc) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } if !scope.Contract.UseGas(dynamicCost, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallOpCodeDynamic) { return nil, ErrOutOfGas } - return opSload(pc, interpreter, scope) + return opSload(interpreter, scope, loc) } -func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - loc := scope.Stack.peek() +func opSLoadFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + loc, err := scope.Stack.pop(1) + if err != nil { + return nil, err + } + return opSload(interpreter, scope, loc) +} + +func opSload(interpreter *EVMInterpreter, scope *ScopeContext, loc *uint256.Int) ([]byte, error) { hash := common.Hash(loc.Bytes32()) val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash) loc.SetBytes(val.Bytes()) @@ -789,7 +965,11 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by } func opSstoreEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSStore4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, val, err := scope.Stack.pop2(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSStore4762(interpreter.evm, scope.Contract, loc) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -797,11 +977,15 @@ func opSstoreEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex return nil, ErrOutOfGas } - return opSstore(pc, interpreter, scope) + return opSstore(interpreter, scope, loc, val) } func opSstoreEIP3529(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSStoreEIP3529(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, val, err := scope.Stack.pop2(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSStoreWithClearingRefund(interpreter.evm, scope.Contract, loc, val, params.SstoreClearsScheduleRefundEIP3529) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -809,11 +993,15 @@ func opSstoreEIP3529(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex return nil, ErrOutOfGas } - return opSstore(pc, interpreter, scope) + return opSstore(interpreter, scope, loc, val) } func opSstoreEIP2200(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSStoreEIP2200(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, val, err := scope.Stack.pop2(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSStoreEIP2200(interpreter.evm, scope.Contract, loc, val) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -821,11 +1009,15 @@ func opSstoreEIP2200(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex return nil, ErrOutOfGas } - return opSstore(pc, interpreter, scope) + return opSstore(interpreter, scope, loc, val) } func opSstoreEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSStoreEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, val, err := scope.Stack.pop2(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSStoreWithClearingRefund(interpreter.evm, scope.Contract, loc, val, params.SstoreClearsScheduleRefundEIP2200) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -833,11 +1025,15 @@ func opSstoreEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex return nil, ErrOutOfGas } - return opSstore(pc, interpreter, scope) + return opSstore(interpreter, scope, loc, val) } func opSstoreFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSStore(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + loc, val, err := scope.Stack.pop2(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSStore(interpreter.evm, scope.Contract, loc, val) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -845,15 +1041,13 @@ func opSstoreFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte return nil, ErrOutOfGas } - return opSstore(pc, interpreter, scope) + return opSstore(interpreter, scope, loc, val) } -func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opSstore(interpreter *EVMInterpreter, scope *ScopeContext, loc, val *uint256.Int) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } - loc := scope.Stack.pop() - val := scope.Stack.pop() interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32()) return nil, nil } @@ -862,8 +1056,11 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if interpreter.evm.abort.Load() { return nil, errStopToken } - pos := scope.Stack.pop() - if !scope.Contract.validJumpdest(&pos) { + pos, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + if !scope.Contract.validJumpdest(pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop @@ -874,9 +1071,12 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by if interpreter.evm.abort.Load() { return nil, errStopToken } - pos, cond := scope.Stack.pop(), scope.Stack.pop() + pos, cond, err := scope.Stack.pop2(0) + if err != nil { + return nil, err + } if !cond.IsZero() { - if !scope.Contract.validJumpdest(&pos) { + if !scope.Contract.validJumpdest(pos) { return nil, ErrInvalidJump } *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop @@ -889,106 +1089,91 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(*pc)) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(*pc)) } func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len()))) } func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas)) - return nil, nil + return nil, scope.Stack.push(new(uint256.Int).SetUint64(scope.Contract.Gas)) } func opSwap1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap1() - return nil, nil + return nil, scope.Stack.swap1() } func opSwap2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap2() - return nil, nil + return nil, scope.Stack.swap2() } func opSwap3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap3() - return nil, nil + return nil, scope.Stack.swap3() } func opSwap4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap4() - return nil, nil + return nil, scope.Stack.swap4() } func opSwap5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap5() - return nil, nil + return nil, scope.Stack.swap5() } func opSwap6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap6() - return nil, nil + return nil, scope.Stack.swap6() } func opSwap7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap7() - return nil, nil + return nil, scope.Stack.swap7() } func opSwap8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap8() - return nil, nil + return nil, scope.Stack.swap8() } func opSwap9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap9() - return nil, nil + return nil, scope.Stack.swap9() } func opSwap10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap10() - return nil, nil + return nil, scope.Stack.swap10() } func opSwap11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap11() - return nil, nil + return nil, scope.Stack.swap11() } func opSwap12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap12() - return nil, nil + return nil, scope.Stack.swap12() } func opSwap13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap13() - return nil, nil + return nil, scope.Stack.swap13() } func opSwap14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap14() - return nil, nil + return nil, scope.Stack.swap14() } func opSwap15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap15() - return nil, nil + return nil, scope.Stack.swap15() } func opSwap16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.swap16() - return nil, nil + return nil, scope.Stack.swap16() } func opCreateEIP3860(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCreate, scope.Stack, scope.Memory) + value, offset, size, err := scope.Stack.pop3(0) if err != nil { return nil, err } - dynamicCost, err := gasCreateEip3860(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := gasCreateEip3860(scope.Memory, memorySize, size) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -997,15 +1182,19 @@ func opCreateEIP3860(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex } scope.Memory.Resize(memorySize) - return opCreate(pc, interpreter, scope) + return opCreate(interpreter, scope, value, offset, size) } func opCreateFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCreate, scope.Stack, scope.Memory) + value, offset, size, err := scope.Stack.pop3(0) if err != nil { return nil, err } - dynamicCost, err := gasCreate(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := memoryGasCost(scope.Memory, memorySize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1014,19 +1203,17 @@ func opCreateFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } scope.Memory.Resize(memorySize) - return opCreate(pc, interpreter, scope) + return opCreate(interpreter, scope, value, offset, size) } -func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opCreate(interpreter *EVMInterpreter, scope *ScopeContext, value, offset, size *uint256.Int) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } var ( - value = scope.Stack.pop() - offset, size = scope.Stack.pop(), scope.Stack.pop() - input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) - gas = scope.Contract.Gas + input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) + gas = scope.Contract.Gas ) if interpreter.evm.chainRules.IsEIP150 { gas -= gas / 64 @@ -1037,7 +1224,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallContractCreation) - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract.Address(), input, gas, &value) + res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract.Address(), input, gas, value) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must @@ -1049,7 +1236,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } else { stackvalue.SetBytes(addr.Bytes()) } - scope.Stack.push(&stackvalue) + scope.Stack.push(stackvalue) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded) @@ -1062,11 +1249,15 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } func opCreate2EIP3860(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCreate2, scope.Stack, scope.Memory) + endowment, offset, size, salt, err := scope.Stack.pop4(0) if err != nil { return nil, err } - dynamicCost, err := gasCreate2Eip3860(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := gasCreate2Eip3860(scope.Memory, memorySize, size) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1075,15 +1266,19 @@ func opCreate2EIP3860(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } scope.Memory.Resize(memorySize) - return opCreate2(pc, interpreter, scope) + return opCreate2(interpreter, scope, endowment, offset, size, salt) } func opCreate2Constantinople(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCreate2, scope.Stack, scope.Memory) + endowment, offset, size, salt, err := scope.Stack.pop4(0) if err != nil { return nil, err } - dynamicCost, err := gasCreate2(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := gasCreate2(scope.Memory, memorySize, size) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1092,19 +1287,16 @@ func opCreate2Constantinople(pc *uint64, interpreter *EVMInterpreter, scope *Sco } scope.Memory.Resize(memorySize) - return opCreate2(pc, interpreter, scope) + return opCreate2(interpreter, scope, endowment, offset, size, salt) } -func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opCreate2(interpreter *EVMInterpreter, scope *ScopeContext, endowment, offset, size, salt *uint256.Int) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } var ( - endowment = scope.Stack.pop() - offset, size = scope.Stack.pop(), scope.Stack.pop() - salt = scope.Stack.pop() - input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) - gas = scope.Contract.Gas + input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) + gas = scope.Contract.Gas ) // Apply EIP150 @@ -1113,14 +1305,14 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] // reuse size int for stackvalue stackvalue := size res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract.Address(), input, gas, - &endowment, &salt) + endowment, salt) // Push item on the stack based on the returned error. if suberr != nil { stackvalue.Clear() } else { stackvalue.SetBytes(addr.Bytes()) } - scope.Stack.push(&stackvalue) + scope.Stack.push(stackvalue) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded) if suberr == ErrExecutionReverted { @@ -1132,11 +1324,17 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] } func opCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP4762(interpreter.evm, scope.Contract, func() (uint64, error) { + return gasCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, addr, value) + }, addr, value, true) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1145,15 +1343,21 @@ func opCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } scope.Memory.Resize(memorySize) - return opCall(pc, interpreter, scope) + return opCall(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } func opCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP7702(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, addr, value) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1162,15 +1366,21 @@ func opCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } scope.Memory.Resize(memorySize) - return opCall(pc, interpreter, scope) + return opCall(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } func opCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP2929(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, addr, value) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1179,15 +1389,19 @@ func opCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } scope.Memory.Resize(memorySize) - return opCall(pc, interpreter, scope) + return opCall(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } func opCallFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, addr, value) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1196,17 +1410,15 @@ func opCallFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } scope.Memory.Resize(memorySize) - return opCall(pc, interpreter, scope) + return opCall(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } -func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opCall(interpreter *EVMInterpreter, scope *ScopeContext, + temp, addr, value, inOffset, inSize, retOffset, retSize *uint256.Int, +) ([]byte, error) { stack := scope.Stack - // Pop gas. The actual gas in interpreter.evm.callGasTemp. - // We can use this as a temporary value - temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get the arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -1217,17 +1429,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt if !value.IsZero() { gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.Call(scope.Contract.Address(), toAddr, args, gas, &value) + ret, returnGas, err := interpreter.evm.Call(scope.Contract.Address(), toAddr, args, gas, value) if err != nil { temp.Clear() } else { temp.SetOne() } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + stack.push(temp) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded) @@ -1236,11 +1448,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } func opCallCodeEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallCodeEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP4762(interpreter.evm, scope.Contract, func() (uint64, error) { + return gasCallCode(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, value) + }, addr, value, false) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1249,15 +1467,21 @@ func opCallCodeEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCont } scope.Memory.Resize(memorySize) - return opCallCode(pc, interpreter, scope) + return opCallCode(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } func opCallCodeEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallCodeEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP7702(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasCallCode(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, value) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1266,15 +1490,21 @@ func opCallCodeEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCont } scope.Memory.Resize(memorySize) - return opCallCode(pc, interpreter, scope) + return opCallCode(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } func opCallCodeEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallCodeEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP2929(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasCallCode(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, value) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1283,15 +1513,19 @@ func opCallCodeEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCont } scope.Memory.Resize(memorySize) - return opCallCode(pc, interpreter, scope) + return opCallCode(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } func opCallCodeFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory) + gas, addr, value, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop7(0) if err != nil { return nil, err } - dynamicCost, err := gasCallCode(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallCode(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas, value) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1300,17 +1534,14 @@ func opCallCodeFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon } scope.Memory.Resize(memorySize) - return opCallCode(pc, interpreter, scope) + return opCallCode(interpreter, scope, gas, addr, value, inOffset, inSize, retOffset, retSize) } -func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - stack := scope.Stack - // We use it as a temporary value - temp := stack.pop() +func opCallCode(interpreter *EVMInterpreter, scope *ScopeContext, + temp, addr, value, inOffset, inSize, retOffset, retSize *uint256.Int, +) ([]byte, error) { gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -1319,16 +1550,16 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ gas += params.CallStipend } - ret, returnGas, err := interpreter.evm.CallCode(scope.Contract.Address(), toAddr, args, gas, &value) + ret, returnGas, err := interpreter.evm.CallCode(scope.Contract.Address(), toAddr, args, gas, value) if err != nil { temp.Clear() } else { temp.SetOne() } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + scope.Stack.push(temp) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded) @@ -1337,11 +1568,17 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ } func opDelegateCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasDelegateCallEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP7702(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasDelegateCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1350,15 +1587,21 @@ func opDelegateCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *Scope } scope.Memory.Resize(memorySize) - return opDelegateCall(pc, interpreter, scope) + return opDelegateCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } func opDelegateCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasDelegateCallEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP2929(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasDelegateCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1367,15 +1610,21 @@ func opDelegateCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *Scope } scope.Memory.Resize(memorySize) - return opDelegateCall(pc, interpreter, scope) + return opDelegateCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } func opDelegateCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasDelegateCallEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP4762(interpreter.evm, scope.Contract, func() (uint64, error) { + return gasDelegateCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) + }, addr, nil, false) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1384,17 +1633,14 @@ func opDelegateCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *Scope } scope.Memory.Resize(memorySize) - return opDelegateCall(pc, interpreter, scope) + return opDelegateCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } -func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opDelegateCall(interpreter *EVMInterpreter, scope *ScopeContext, + temp, addr, inOffset, inSize, retOffset, retSize *uint256.Int, +) ([]byte, error) { stack := scope.Stack - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - // We use it as a temporary value - temp := stack.pop() gas := interpreter.evm.callGasTemp - // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -1405,10 +1651,10 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } else { temp.SetOne() } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + stack.push(temp) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded) @@ -1417,11 +1663,15 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opDelegateCallHomestead(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasDelegateCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasDelegateCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1430,15 +1680,21 @@ func opDelegateCallHomestead(pc *uint64, interpreter *EVMInterpreter, scope *Sco } scope.Memory.Resize(memorySize) - return opDelegateCall(pc, interpreter, scope) + return opDelegateCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } func opStaticCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasStaticCallEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP7702(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasStaticCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1447,15 +1703,19 @@ func opStaticCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCo } scope.Memory.Resize(memorySize) - return opStaticCall(pc, interpreter, scope) + return opStaticCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } func opStaticCallByzantium(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasStaticCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasStaticCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1464,15 +1724,21 @@ func opStaticCallByzantium(pc *uint64, interpreter *EVMInterpreter, scope *Scope } scope.Memory.Resize(memorySize) - return opStaticCall(pc, interpreter, scope) + return opStaticCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } func opStaticCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasStaticCallEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP2929(interpreter.evm, scope.Contract, addr, func() (uint64, error) { + return gasStaticCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) + }) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1481,15 +1747,21 @@ func opStaticCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCo } scope.Memory.Resize(memorySize) - return opStaticCall(pc, interpreter, scope) + return opStaticCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } func opStaticCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory) + gas, addr, inOffset, inSize, retOffset, retSize, err := scope.Stack.pop6(0) if err != nil { return nil, err } - dynamicCost, err := gasStaticCallEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateCallMemorySize(inOffset, inSize, retOffset, retSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasCallEIP4762(interpreter.evm, scope.Contract, func() (uint64, error) { + return gasStaticCall(interpreter.evm, scope.Contract, scope.Memory, memorySize, gas) + }, addr, nil, false) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1498,17 +1770,15 @@ func opStaticCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCo } scope.Memory.Resize(memorySize) - return opStaticCall(pc, interpreter, scope) + return opStaticCall(interpreter, scope, gas, addr, inOffset, inSize, retOffset, retSize) } -func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - // Pop gas. The actual gas is in interpreter.evm.callGasTemp. - stack := scope.Stack +func opStaticCall(interpreter *EVMInterpreter, scope *ScopeContext, + temp, addr, inOffset, inSize, retOffset, retSize *uint256.Int, +) ([]byte, error) { // We use it as a temporary value - temp := stack.pop() gas := interpreter.evm.callGasTemp // Pop other call parameters. - addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() toAddr := common.Address(addr.Bytes20()) // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) @@ -1519,10 +1789,10 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } else { temp.SetOne() } - stack.push(&temp) if err == nil || err == ErrExecutionReverted { scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) } + scope.Stack.push(temp) scope.Contract.RefundGas(returnGas, interpreter.evm.Config.Tracer.OnGasChange, tracing.GasChangeCallLeftOverRefunded) @@ -1531,11 +1801,15 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) } func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryReturn, scope.Stack, scope.Memory) + offset, size, err := scope.Stack.pop2(0) if err != nil { return nil, err } - dynamicCost, err := gasReturn(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := memoryGasCost(scope.Memory, memorySize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1544,18 +1818,21 @@ func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Memory.Resize(memorySize) - offset, size := scope.Stack.pop(), scope.Stack.pop() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) return ret, errStopToken } func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - memorySize, err := calculateMemorySize(memoryRevert, scope.Stack, scope.Memory) + offset, size, err := scope.Stack.pop2(0) if err != nil { return nil, err } - dynamicCost, err := gasRevert(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + memorySize, err := calculateMemorySize(offset, size) + if err != nil { + return nil, err + } + dynamicCost, err := memoryGasCost(scope.Memory, memorySize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1564,7 +1841,6 @@ func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b } scope.Memory.Resize(memorySize) - offset, size := scope.Stack.pop(), scope.Stack.pop() ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64()) interpreter.returnData = ret @@ -1580,7 +1856,11 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt } func opSelfdestructEIP6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSelfdestructEIP3529(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + beneficiary, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSelfdestructEIP(interpreter.evm, scope.Contract, beneficiary, false) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1588,11 +1868,15 @@ func opSelfdestructEIP6780(pc *uint64, interpreter *EVMInterpreter, scope *Scope return nil, ErrOutOfGas } - return opSelfdestruct6780(pc, interpreter, scope) + return opSelfdestruct6780(interpreter, scope, beneficiary) } func opSelfdestructEIP3529(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSelfdestructEIP3529(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + beneficiary, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSelfdestructEIP(interpreter.evm, scope.Contract, beneficiary, false) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1600,11 +1884,15 @@ func opSelfdestructEIP3529(pc *uint64, interpreter *EVMInterpreter, scope *Scope return nil, ErrOutOfGas } - return opSelfdestruct(pc, interpreter, scope) + return opSelfdestruct(interpreter, scope, beneficiary) } func opSelfdestructEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSelfdestructEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + beneficiary, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSelfdestructEIP(interpreter.evm, scope.Contract, beneficiary, true) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1612,11 +1900,15 @@ func opSelfdestructEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *Scope return nil, ErrOutOfGas } - return opSelfdestruct(pc, interpreter, scope) + return opSelfdestruct(interpreter, scope, beneficiary) } func opSelfdestructFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSelfdestruct(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + beneficiary, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSelfdestruct(interpreter.evm, scope.Contract, beneficiary) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1624,15 +1916,14 @@ func opSelfdestructFrontier(pc *uint64, interpreter *EVMInterpreter, scope *Scop return nil, ErrOutOfGas } - return opSelfdestruct(pc, interpreter, scope) + return opSelfdestruct(interpreter, scope, beneficiary) } -func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opSelfdestruct(interpreter *EVMInterpreter, scope *ScopeContext, beneficiary *uint256.Int) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } - beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) @@ -1646,7 +1937,11 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext } func opSelfdestructEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - dynamicCost, err := gasSelfdestructEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0) + beneficiary, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + dynamicCost, err := gasSelfdestructEIP4762(interpreter.evm, scope.Contract, beneficiary) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1654,14 +1949,13 @@ func opSelfdestructEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *Scope return nil, ErrOutOfGas } - return opSelfdestruct6780(pc, interpreter, scope) + return opSelfdestruct6780(interpreter, scope, beneficiary) } -func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { +func opSelfdestruct6780(interpreter *EVMInterpreter, scope *ScopeContext, beneficiary *uint256.Int) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } - beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) @@ -1678,17 +1972,29 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon // following functions are used by the instruction jump table // make log instruction function -func makeLog(size int) executionFunc { - logGas := makeGasLog(uint64(size)) +func makeLog(size uint64) executionFunc { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { if interpreter.readOnly { return nil, ErrWriteProtection } - memorySize, err := calculateMemorySize(memoryLog, scope.Stack, scope.Memory) + mStart, mSize, err := scope.Stack.pop2(0) if err != nil { return nil, err } - dynamicCost, err := logGas(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize) + topics := make([]common.Hash, size) + for i := uint64(0); i < size; i++ { + addr, err := scope.Stack.pop(0) + if err != nil { + return nil, err + } + topics[i] = addr.Bytes32() + } + + memorySize, err := calculateMemorySize(mStart, mSize) + if err != nil { + return nil, err + } + dynamicCost, err := gasLog(scope.Memory, memorySize, size, mSize) if err != nil { return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) } @@ -1697,14 +2003,6 @@ func makeLog(size int) executionFunc { } scope.Memory.Resize(memorySize) - topics := make([]common.Hash, size) - stack := scope.Stack - mStart, mSize := stack.pop(), stack.pop() - for i := 0; i < size; i++ { - addr := stack.pop() - topics[i] = addr.Bytes32() - } - d := scope.Memory.GetCopy(mStart.Uint64(), mSize.Uint64()) interpreter.evm.StateDB.AddLog(&types.Log{ Address: scope.Contract.Address(), @@ -1727,11 +2025,9 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by ) *pc += 1 if *pc < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) - } else { - scope.Stack.push(integer.Clear()) + return nil, scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) } - return nil, nil + return nil, scope.Stack.push(integer.Clear()) } // opPush2 is a specialized version of pushN @@ -1740,12 +2036,16 @@ func opPush2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by codeLen = uint64(len(scope.Contract.Code)) integer = new(uint256.Int) ) + var err error if *pc+2 < codeLen { - scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) + err = scope.Stack.push(integer.SetBytes2(scope.Contract.Code[*pc+1 : *pc+3])) } else if *pc+1 < codeLen { - scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) + err = scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc+1]) << 8)) } else { - scope.Stack.push(integer.Clear()) + err = scope.Stack.push(integer.Clear()) + } + if err != nil { + return nil, err } *pc += 2 return nil, nil @@ -1765,16 +2065,71 @@ func makePush(size uint64, pushByteSize int) executionFunc { if missing := pushByteSize - (end - start); missing > 0 { a.Lsh(a, uint(8*missing)) } - scope.Stack.push(a) *pc += size - return nil, nil + return nil, scope.Stack.push(a) } } -// make dup instruction function -func makeDup(size int64) executionFunc { - return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { - scope.Stack.dup(int(size)) - return nil, nil - } +func opDup1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup1() +} + +func opDup2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup2() +} + +func opDup3(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup3() +} + +func opDup4(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup4() +} + +func opDup5(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup5() +} + +func opDup6(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup6() +} + +func opDup7(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup7() +} + +func opDup8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup8() +} + +func opDup9(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup9() +} + +func opDup10(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup10() +} + +func opDup11(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup11() +} + +func opDup12(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup12() +} + +func opDup13(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup13() +} + +func opDup14(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup14() +} + +func opDup15(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup15() +} + +func opDup16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + return nil, scope.Stack.dup16() } diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 317cb1aee0..88d8bc2b66 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -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) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index b6a44550b0..d3b1c083c4 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -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) diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 8ed512316b..bb8bc54586 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -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) } } diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 37e334ea82..18a9f47058 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -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} } } diff --git a/core/vm/jump_table_export.go b/core/vm/jump_table_export.go index bc41512074..70fbcda56d 100644 --- a/core/vm/jump_table_export.go +++ b/core/vm/jump_table_export.go @@ -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 -} diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go deleted file mode 100644 index 63ad967850..0000000000 --- a/core/vm/memory_table.go +++ /dev/null @@ -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 . - -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)) -} diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go index 49e82f50b3..6cb7bdcb6a 100644 --- a/core/vm/operations_acl.go +++ b/core/vm/operations_acl.go @@ -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 } diff --git a/core/vm/operations_verkle.go b/core/vm/operations_verkle.go index 30f9957775..decb640aa5 100644 --- a/core/vm/operations_verkle.go +++ b/core/vm/operations_verkle.go @@ -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 diff --git a/core/vm/stack.go b/core/vm/stack.go index 879dc9aa6d..2a29b60074 100644 --- a/core/vm/stack.go +++ b/core/vm/stack.go @@ -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]) } diff --git a/core/vm/stack_table.go b/core/vm/stack_table.go deleted file mode 100644 index 10c12901af..0000000000 --- a/core/vm/stack_table.go +++ /dev/null @@ -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 . - -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 -} diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 8bf9d048d7..a65420e4ab 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -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",