core/vm, eth/tracers: move dynamic gas and memory handling to opcode handlers

This commit is contained in:
Ömer Faruk IRMAK 2025-07-11 12:19:35 +03:00
parent e3ebb95289
commit cbe64f51c7
9 changed files with 978 additions and 297 deletions

View file

@ -910,7 +910,7 @@ Pre-execution info:
| 20 | PUSH1 | 3 | 19900 |[0x0,0x0,0x0,0x0,0x0] |
| 22 | GAS | 2 | 19900 |[0x0,0x0,0x0,0x0,0x0,0xf9] |
| 23 | CALLCODE | 100 | 19900 |[0x0,0x0,0x0,0x0,0x0,0xf9,0x885] |
Error: out of gas: out of gas
Error: at pc=23, op=242: out of gas: out of gas
| 1786 | POP | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26,0x0] |
| 1787 | POP | 2 | 0 |[0x94a843a7335fc63be036fbdecc40b1365f3c5f26] |
| 1788 | STOP | 0 | 0 | [] |

View file

@ -217,6 +217,17 @@ type Hooks struct {
OnBlockHashRead BlockHashReadHook
}
// HooksVM returns if any of the VM events are being hooked
func (h *Hooks) HooksVM() bool {
return h.OnTxStart != nil ||
h.OnTxEnd != nil ||
h.OnEnter != nil ||
h.OnExit != nil ||
h.OnOpcode != nil ||
h.OnFault != nil ||
h.OnGasChange != nil
}
// HooksState returns if any of the state events are being hooked
func (h *Hooks) HooksState() bool {
return h.OnBalanceChange != nil ||

View file

@ -25,29 +25,26 @@ import (
// calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64
func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
if !l.IsUint64() {
return 0, true
func calcMemSize64(off, len *uint256.Int) (uint64, bool) {
lenOverflowed := (len[1] | len[2] | len[3]) != 0
// if length is zero, memsize is always zero, regardless of offset
if len[0] == 0 {
return 0, lenOverflowed
}
return calcMemSize64WithUint(off, l.Uint64())
offOverflowed := (off[1] | off[2] | off[3]) != 0
memSize := off[0] + len[0]
// if value < either of it's parts, then it overflowed
return memSize, lenOverflowed || offOverflowed || memSize < off[0]
}
// calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a uint64
func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset
if length64 == 0 {
return 0, false
}
// Check that offset doesn't overflow
offset64, overflow := off.Uint64WithOverflow()
if overflow {
return 0, true
}
val := offset64 + length64
// Identical to calcMemSize64, but length is a uint64 and assumed to be non-zero
func calcMemSize64WithUint(offset *uint256.Int, length64 uint64) (uint64, bool) {
overflowed := (offset[1] | offset[2] | offset[3]) != 0
memSize := offset[0] + length64
// if value < either of it's parts, then it overflowed
return val, val < offset64
return memSize, overflowed || memSize < length64
}
// getData returns a slice from the data based on the start and size and pads

View file

@ -117,45 +117,45 @@ func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
// enable2200 applies EIP-2200 (Rebalance net-metered SSTORE)
func enable2200(jt *JumpTable) {
jt[SLOAD].constantGas = params.SloadGasEIP2200
jt[SSTORE].dynamicGas = gasSStoreEIP2200
jt[SSTORE].execute = opSstoreEIP2200
}
// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
// https://eips.ethereum.org/EIPS/eip-2929
func enable2929(jt *JumpTable) {
jt[SSTORE].dynamicGas = gasSStoreEIP2929
jt[SSTORE].execute = opSstoreEIP2929
jt[SLOAD].constantGas = 0
jt[SLOAD].dynamicGas = gasSLoadEIP2929
jt[SLOAD].execute = opSLoadEIP2929
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
jt[EXTCODECOPY].execute = opExtCodeCopyEIP2929
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
jt[EXTCODESIZE].execute = opExtCodeSizeEIP2929
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
jt[EXTCODEHASH].execute = opExtCodeHashEIP2929
jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
jt[BALANCE].execute = opBalanceEIP2929
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
jt[CALL].dynamicGas = gasCallEIP2929
jt[CALL].execute = opCallEIP2929
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
jt[CALLCODE].execute = opCallCodeEIP2929
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
jt[STATICCALL].execute = opStaticCallEIP2929
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
jt[DELEGATECALL].execute = opDelegateCallEIP2929
// This was previously part of the dynamic cost, but we're using it as a constantGas
// factor here
jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
jt[SELFDESTRUCT].execute = opSelfdestructEIP2929
}
// enable3529 enabled "EIP-3529: Reduction in refunds":
@ -163,8 +163,8 @@ func enable2929(jt *JumpTable) {
// - Reduces refunds for SSTORE
// - Reduces max refunds to 20% gas
func enable3529(jt *JumpTable) {
jt[SSTORE].dynamicGas = gasSStoreEIP3529
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
jt[SSTORE].execute = opSstoreEIP3529
jt[SELFDESTRUCT].execute = opSelfdestructEIP3529
}
// enable3198 applies EIP-3198 (BASEFEE Opcode)
@ -245,8 +245,8 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// enable3860 enables "EIP-3860: Limit and meter initcode"
// https://eips.ethereum.org/EIPS/eip-3860
func enable3860(jt *JumpTable) {
jt[CREATE].dynamicGas = gasCreateEip3860
jt[CREATE2].dynamicGas = gasCreate2Eip3860
jt[CREATE].execute = opCreateEIP3860
jt[CREATE2].execute = opCreate2EIP3860
}
// enable5656 enables EIP-5656 (MCOPY opcode)
@ -255,15 +255,26 @@ func enable5656(jt *JumpTable) {
jt[MCOPY] = &operation{
execute: opMcopy,
constantGas: GasFastestStep,
dynamicGas: gasMcopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryMcopy,
}
}
// 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)
if err != nil {
return nil, err
}
dynamicCost, err := gasMcopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
var (
dst = scope.Stack.pop()
src = scope.Stack.pop()
@ -334,8 +345,7 @@ func enable7516(jt *JumpTable) {
// enable6780 applies EIP-6780 (deactivate SELFDESTRUCT)
func enable6780(jt *JumpTable) {
jt[SELFDESTRUCT] = &operation{
execute: opSelfdestruct6780,
dynamicGas: gasSelfdestructEIP3529,
execute: opSelfdestructEIP6780,
constantGas: params.SelfdestructGasEIP150,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
@ -343,6 +353,19 @@ func enable6780(jt *JumpTable) {
}
func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryExtCodeCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasExtCodeCopyEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
var (
stack = scope.Stack
a = stack.pop()
@ -425,112 +448,90 @@ func makePushEIP4762(size uint64, pushByteSize int) executionFunc {
func enable4762(jt *JumpTable) {
jt[SSTORE] = &operation{
dynamicGas: gasSStore4762,
execute: opSstore,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: opSstoreEIP4762,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
}
jt[SLOAD] = &operation{
dynamicGas: gasSLoad4762,
execute: opSload,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opSLoadEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[BALANCE] = &operation{
execute: opBalance,
dynamicGas: gasBalance4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opBalanceEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[EXTCODESIZE] = &operation{
execute: opExtCodeSize,
dynamicGas: gasExtCodeSize4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opExtCodeSizeEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[EXTCODEHASH] = &operation{
execute: opExtCodeHash,
dynamicGas: gasExtCodeHash4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
execute: opExtCodeHashEIP4762,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
jt[EXTCODECOPY] = &operation{
execute: opExtCodeCopyEIP4762,
dynamicGas: gasExtCodeCopyEIP4762,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryExtCodeCopy,
execute: opExtCodeCopyEIP4762,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
}
jt[CODECOPY] = &operation{
execute: opCodeCopy,
execute: opCodeCopyEIP4762,
constantGas: GasFastestStep,
dynamicGas: gasCodeCopyEip4762,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCodeCopy,
}
jt[SELFDESTRUCT] = &operation{
execute: opSelfdestruct6780,
dynamicGas: gasSelfdestructEIP4762,
execute: opSelfdestructEIP4762,
constantGas: params.SelfdestructGasEIP150,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
}
jt[CREATE] = &operation{
execute: opCreate,
execute: opCreateEIP3860,
constantGas: params.CreateNGasEip4762,
dynamicGas: gasCreateEip3860,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
memorySize: memoryCreate,
}
jt[CREATE2] = &operation{
execute: opCreate2,
execute: opCreate2EIP3860,
constantGas: params.CreateNGasEip4762,
dynamicGas: gasCreate2Eip3860,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
}
jt[CALL] = &operation{
execute: opCall,
dynamicGas: gasCallEIP4762,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
execute: opCallEIP4762,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
}
jt[CALLCODE] = &operation{
execute: opCallCode,
dynamicGas: gasCallCodeEIP4762,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
execute: opCallCodeEIP4762,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
}
jt[STATICCALL] = &operation{
execute: opStaticCall,
dynamicGas: gasStaticCallEIP4762,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryStaticCall,
execute: opStaticCallEIP4762,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
}
jt[DELEGATECALL] = &operation{
execute: opDelegateCall,
dynamicGas: gasDelegateCallEIP4762,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
execute: opDelegateCallEIP4762,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
}
jt[PUSH1] = &operation{
@ -551,8 +552,8 @@ func enable4762(jt *JumpTable) {
// enable7702 the EIP-7702 changes to support delegation designators.
func enable7702(jt *JumpTable) {
jt[CALL].dynamicGas = gasCallEIP7702
jt[CALLCODE].dynamicGas = gasCallCodeEIP7702
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
jt[CALL].execute = opCallEIP7702
jt[CALLCODE].execute = opCallCodeEIP7702
jt[STATICCALL].execute = opStaticCallEIP7702
jt[DELEGATECALL].execute = opDelegateCallEIP7702
}

View file

@ -17,6 +17,7 @@
package vm
import (
"fmt"
"math"
"github.com/ethereum/go-ethereum/common"
@ -26,6 +27,19 @@ import (
"github.com/holiman/uint256"
)
// 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)
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if overflow || memSize > math.MaxUint64-31 {
return 0, ErrGasUintOverflow
}
memorySize := ((memSize + 31) / 32) * 32
return memorySize, nil
}
func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x, y := scope.Stack.pop(), scope.Stack.peek()
y.Add(&x, y)
@ -68,6 +82,30 @@ func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
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)
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 opExp(pc, interpreter, scope)
}
func opExpFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasExpFrontier(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opExp(pc, interpreter, scope)
}
func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
base, exponent := scope.Stack.pop(), scope.Stack.peek()
exponent.Exp(&base, exponent)
@ -230,6 +268,19 @@ 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)
if err != nil {
return nil, err
}
dynamicCost, err := gasKeccak256(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
offset, size := scope.Stack.pop(), scope.Stack.peek()
data := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
@ -250,6 +301,30 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
return nil, nil
}
func opBalanceEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasBalance4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opBalance(pc, interpreter, scope)
}
func opBalanceEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opBalance(pc, interpreter, scope)
}
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
address := common.Address(slot.Bytes20())
@ -289,6 +364,19 @@ func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCallDataCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallDataCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
var (
memOffset = scope.Stack.pop()
dataOffset = scope.Stack.pop()
@ -312,6 +400,19 @@ func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
}
func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryReturnDataCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasReturnDataCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
var (
memOffset = scope.Stack.pop()
dataOffset = scope.Stack.pop()
@ -333,6 +434,28 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
return nil, nil
}
func opExtCodeSizeEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasExtCodeSize4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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)
}
func opExtCodeSizeEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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)
}
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
@ -344,6 +467,38 @@ func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return nil, nil
}
func opCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCodeCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCodeCopyEip4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCodeCopy(pc, interpreter, scope)
}
func opCodeCopyFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCodeCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCodeCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCodeCopy(pc, interpreter, scope)
}
func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
memOffset = scope.Stack.pop()
@ -360,6 +515,38 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return nil, nil
}
func opExtCodeCopyEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryExtCodeCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasExtCodeCopyEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opExtCodeCopy(pc, interpreter, scope)
}
func opExtCodeCopyFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryExtCodeCopy, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasExtCodeCopy(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opExtCodeCopy(pc, interpreter, scope)
}
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
@ -380,6 +567,28 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
return nil, nil
}
func opExtCodeHashEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasExtCodeHash4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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)
}
func opExtCodeHashEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasEip2929AccountCheck(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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)
}
// opExtCodeHash returns the code hash of a specified account.
// There are several cases when the function is called, while we can relay everything
// to `state.GetCodeHash` function to ensure the correctness.
@ -492,6 +701,19 @@ func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte
}
func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryMLoad, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasMLoad(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
v := scope.Stack.peek()
offset := v.Uint64()
v.SetBytes(scope.Memory.GetPtr(offset, 32))
@ -499,17 +721,65 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
}
func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryMStore, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasMStore(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
mStart, val := scope.Stack.pop(), scope.Stack.pop()
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)
if err != nil {
return nil, err
}
dynamicCost, err := gasMStore8(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
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)
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)
}
func opSLoadEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSLoadEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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)
}
func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
loc := scope.Stack.peek()
hash := common.Hash(loc.Bytes32())
@ -518,6 +788,66 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
return nil, nil
}
func opSstoreEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSStore4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSstore(pc, interpreter, scope)
}
func opSstoreEIP3529(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSStoreEIP3529(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSstore(pc, interpreter, scope)
}
func opSstoreEIP2200(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSStoreEIP2200(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSstore(pc, interpreter, scope)
}
func opSstoreEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSStoreEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSstore(pc, interpreter, scope)
}
func opSstoreFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSStore(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSstore(pc, interpreter, scope)
}
func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
@ -653,10 +983,45 @@ func opSwap16(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
return nil, nil
}
func opCreateEIP3860(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCreate, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCreateEip3860(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCreate(pc, interpreter, scope)
}
func opCreateFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCreate, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCreate(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCreate(pc, interpreter, scope)
}
func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
var (
value = scope.Stack.pop()
offset, size = scope.Stack.pop(), scope.Stack.pop()
@ -696,6 +1061,40 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
return nil, nil
}
func opCreate2EIP3860(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCreate2, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCreate2Eip3860(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCreate2(pc, interpreter, scope)
}
func opCreate2Constantinople(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCreate2, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCreate2(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCreate2(pc, interpreter, scope)
}
func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
@ -732,6 +1131,74 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
return nil, nil
}
func opCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCall(pc, interpreter, scope)
}
func opCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCall(pc, interpreter, scope)
}
func opCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCall(pc, interpreter, scope)
}
func opCallFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCall(pc, interpreter, scope)
}
func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := scope.Stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
@ -768,6 +1235,74 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
return ret, nil
}
func opCallCodeEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallCodeEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCallCode(pc, interpreter, scope)
}
func opCallCodeEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallCodeEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCallCode(pc, interpreter, scope)
}
func opCallCodeEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallCodeEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCallCode(pc, interpreter, scope)
}
func opCallCodeFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasCallCode(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opCallCode(pc, interpreter, scope)
}
func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
@ -801,6 +1336,57 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
return ret, nil
}
func opDelegateCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasDelegateCallEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opDelegateCall(pc, interpreter, scope)
}
func opDelegateCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasDelegateCallEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opDelegateCall(pc, interpreter, scope)
}
func opDelegateCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasDelegateCallEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opDelegateCall(pc, interpreter, scope)
}
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := scope.Stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
@ -830,6 +1416,91 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
return ret, nil
}
func opDelegateCallHomestead(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryDelegateCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasDelegateCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opDelegateCall(pc, interpreter, scope)
}
func opStaticCallEIP7702(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasStaticCallEIP7702(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opStaticCall(pc, interpreter, scope)
}
func opStaticCallByzantium(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasStaticCall(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opStaticCall(pc, interpreter, scope)
}
func opStaticCallEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasStaticCallEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opStaticCall(pc, interpreter, scope)
}
func opStaticCallEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryStaticCall, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasStaticCallEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
return opStaticCall(pc, interpreter, scope)
}
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
@ -860,6 +1531,19 @@ 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)
if err != nil {
return nil, err
}
dynamicCost, err := gasReturn(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
offset, size := scope.Stack.pop(), scope.Stack.pop()
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
@ -867,6 +1551,19 @@ func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
memorySize, err := calculateMemorySize(memoryRevert, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := gasRevert(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
offset, size := scope.Stack.pop(), scope.Stack.pop()
ret := scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
@ -882,10 +1579,59 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
return nil, errStopToken
}
func opSelfdestructEIP6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSelfdestructEIP3529(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSelfdestruct6780(pc, interpreter, scope)
}
func opSelfdestructEIP3529(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSelfdestructEIP3529(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSelfdestruct(pc, interpreter, scope)
}
func opSelfdestructEIP2929(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSelfdestructEIP2929(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSelfdestruct(pc, interpreter, scope)
}
func opSelfdestructFrontier(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSelfdestruct(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSelfdestruct(pc, interpreter, scope)
}
func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]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)
@ -899,6 +1645,18 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
return nil, errStopToken
}
func opSelfdestructEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
dynamicCost, err := gasSelfdestructEIP4762(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, 0)
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 opSelfdestruct6780(pc, interpreter, scope)
}
func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
@ -921,10 +1679,24 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
// make log instruction function
func makeLog(size int) executionFunc {
logGas := makeGasLog(uint64(size))
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
memorySize, err := calculateMemorySize(memoryLog, scope.Stack, scope.Memory)
if err != nil {
return nil, err
}
dynamicCost, err := logGas(interpreter.evm, scope.Contract, scope.Stack, scope.Memory, memorySize)
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
}
scope.Memory.Resize(memorySize)
topics := make([]common.Hash, size)
stack := scope.Stack
mStart, mSize := stack.pop(), stack.pop()

View file

@ -519,22 +519,24 @@ func BenchmarkOpIsZero(b *testing.B) {
func TestOpMstore(t *testing.T) {
var (
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
contract = &Contract{}
)
mem.Resize(64)
pc := uint64(0)
contract.Gas = 30_000_000
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
stack.push(new(uint256.Int))
opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil})
opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, contract})
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
}
stack.push(new(uint256.Int).SetUint64(0x1))
stack.push(new(uint256.Int))
opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, nil})
opMstore(&pc, evm.interpreter, &ScopeContext{mem, stack, contract})
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
@ -846,10 +848,12 @@ func TestOpMCopy(t *testing.T) {
},
} {
var (
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
evm = NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
contract = &Contract{}
)
contract.Gas = 30_000_000
data := common.FromHex(strings.ReplaceAll(tc.pre, " ", ""))
// Set pre
mem := NewMemory()
@ -889,7 +893,7 @@ func TestOpMCopy(t *testing.T) {
mem.Resize(memorySize)
}
// Do the copy
opMcopy(&pc, evm.interpreter, &ScopeContext{mem, stack, nil})
opMcopy(&pc, evm.interpreter, &ScopeContext{mem, stack, contract})
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)

View file

@ -17,10 +17,7 @@
package vm
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
@ -139,7 +136,7 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
table = &frontierInstructionSet
}
var extraEips []int
if len(evm.Config.ExtraEips) > 0 {
if len(evm.Config.ExtraEips) > 0 || evm.Config.Tracer.HooksVM() {
// Deep-copy jumptable to prevent modification of opcodes in other tables
table = copyJumpTable(table)
}
@ -151,10 +148,41 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
extraEips = append(extraEips, eip)
}
}
if evm.Config.Tracer.HooksVM() {
for _, op := range table {
op.execute = executeWithTracer(&evm.Config.Tracer, op.execute)
}
}
evm.Config.ExtraEips = extraEips
return &EVMInterpreter{evm: evm, table: table, hasher: crypto.NewKeccakState()}
}
// executeWithTracer is a wrapper that enables tracing the given executionFunc
func executeWithTracer(tracer *tracing.Hooks, execF executionFunc) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
pcCopy := *pc
op := callContext.Contract.GetOp(pcCopy)
operation := interpreter.table[op]
cost := operation.constantGas
gasCopy := callContext.Contract.Gas + cost
if tracer.OnGasChange != nil {
tracer.OnGasChange(gasCopy, gasCopy-cost, tracing.GasChangeCallOpCode)
}
if tracer.OnOpcode != nil {
tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, interpreter.returnData,
interpreter.evm.depth, nil)
}
ret, err := execF(pc, interpreter, callContext)
if err != nil && err != errStopToken && tracer.OnFault != nil {
tracer.OnFault(pcCopy, byte(op), gasCopy, gasCopy-callContext.Contract.Gas, callContext, interpreter.evm.depth, VMErrorFromErr(err))
}
return ret, err
}
}
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
//
@ -195,13 +223,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC
// to be uint256. Practically much less so feasible.
pc = uint64(0) // program counter
cost, dynamicCost uint64
// copies used by tracer
pcCopy uint64 // needed for the deferred EVMLogger
gasCopy uint64 // for EVMLogger to log gas remaining before execution
logged bool // deferred EVMLogger should ignore already logged steps
res []byte // result of the opcode execution function
pc = uint64(0) // program counter
cost uint64
res []byte // result of the opcode execution function
)
// Don't move this deferred function, it's placed before the OnOpcode-deferred method,
// so that it gets executed _after_: the OnOpcode needs the stacks before
@ -212,26 +236,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}()
contract.Input = input
defer func() { // this deferred method handles exit-with-error
if err == nil {
return
// This is just temporary until we move everything that can error in to the execution function
traceAndReturnError := func(err error) error {
if in.evm.Config.Tracer.OnFault != nil {
in.evm.Config.Tracer.OnFault(pc, byte(op), contract.Gas, cost, callContext, in.evm.depth, VMErrorFromErr(err))
}
if !logged && in.evm.Config.Tracer.OnOpcode != nil {
in.evm.Config.Tracer.OnOpcode(pcCopy, byte(op), gasCopy, cost, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
}
if logged && in.evm.Config.Tracer.OnFault != nil {
in.evm.Config.Tracer.OnFault(pcCopy, byte(op), gasCopy, cost, callContext, in.evm.depth, VMErrorFromErr(err))
}
}()
return err
}
// The Interpreter main run loop (contextual). This loop runs until either an
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
// parent context.
_ = jumpTable[0] // nil-check the jumpTable out of the loop
for {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy, dynamicCost = false, pc, contract.Gas, 0
if in.evm.chainRules.IsEIP4762 && !contract.IsDeployment && !contract.IsSystemCall {
// if the PC ends up in a new "chunk" of verkleized code, charge the
// associated costs.
@ -239,7 +257,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
consumed, wanted := in.evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas)
contract.UseGas(consumed, in.evm.Config.Tracer.OnGasChange, tracing.GasChangeWitnessCodeChunk)
if consumed < wanted {
return nil, ErrOutOfGas
return nil, traceAndReturnError(ErrOutOfGas)
}
}
@ -250,67 +268,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
cost = operation.constantGas // For tracing
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
return nil, traceAndReturnError(&ErrStackUnderflow{stackLen: sLen, required: operation.minStack})
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
return nil, traceAndReturnError(&ErrStackOverflow{stackLen: sLen, limit: operation.maxStack})
}
// for tracing: this gas consumption event is emitted below in the debug section.
// for tracing: this gas consumption event is emitted in the executeWithTracer wrapper.
if contract.Gas < cost {
return nil, ErrOutOfGas
return nil, traceAndReturnError(ErrOutOfGas)
} else {
contract.Gas -= cost
}
// All ops with a dynamic memory usage also has a dynamic gas cost.
var memorySize uint64
if operation.dynamicGas != nil {
// calculate the new memory size and expand the memory to fit
// the operation
// Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows
if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack)
if overflow {
return nil, ErrGasUintOverflow
}
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
return nil, ErrGasUintOverflow
}
}
// Consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
cost += dynamicCost // for tracing
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err)
}
// for tracing: this gas consumption event is emitted below in the debug section.
if contract.Gas < dynamicCost {
return nil, ErrOutOfGas
} else {
contract.Gas -= dynamicCost
}
}
// Do tracing before potential memory expansion
if in.evm.Config.Tracer.OnGasChange != nil {
// Trace the constant cost only
in.evm.Config.Tracer.OnGasChange(gasCopy, gasCopy-operation.constantGas, tracing.GasChangeCallOpCode)
}
if in.evm.Config.Tracer.OnOpcode != nil {
in.evm.Config.Tracer.OnOpcode(pc, byte(op), gasCopy, operation.constantGas, callContext, in.returnData, in.evm.depth, VMErrorFromErr(err))
// If any, trace the dynamic cost as well
if in.evm.Config.Tracer.OnGasChange != nil && dynamicCost > 0 {
in.evm.Config.Tracer.OnGasChange(gasCopy-operation.constantGas, gasCopy-cost, tracing.GasChangeCallOpCodeDynamic)
}
logged = true
}
if memorySize > 0 {
mem.Resize(memorySize)
}
// execute the operation
res, err = operation.execute(&pc, in, callContext)
if err != nil {

View file

@ -33,16 +33,12 @@ type operation struct {
// execute is the operation function
execute executionFunc
constantGas uint64
dynamicGas gasFunc
// 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
// memorySize returns the memory size required for the operation
memorySize memorySizeFunc
// undefined denotes if the instruction is not officially defined in the jump table
undefined bool
}
@ -73,15 +69,6 @@ func validate(jt JumpTable) JumpTable {
if op == nil {
panic(fmt.Sprintf("op %#x is not set", i))
}
// The interpreter has an assumption that if the memorySize function is
// set, then the dynamicGas function is also set. This is a somewhat
// arbitrary assumption, and can be removed if we need to -- but it
// allows us to avoid a condition check. As long as we have that assumption
// in there, this little sanity check prevents us from merging in a
// change which violates it.
if op.memorySize != nil && op.dynamicGas == nil {
panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
}
}
return jt
}
@ -191,12 +178,10 @@ func newConstantinopleInstructionSet() JumpTable {
maxStack: maxStack(1, 1),
}
instructionSet[CREATE2] = &operation{
execute: opCreate2,
execute: opCreate2Constantinople,
constantGas: params.Create2Gas,
dynamicGas: gasCreate2,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
}
return validate(instructionSet)
}
@ -206,12 +191,10 @@ func newConstantinopleInstructionSet() JumpTable {
func newByzantiumInstructionSet() JumpTable {
instructionSet := newSpuriousDragonInstructionSet()
instructionSet[STATICCALL] = &operation{
execute: opStaticCall,
execute: opStaticCallByzantium,
constantGas: params.CallGasEIP150,
dynamicGas: gasStaticCall,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryStaticCall,
}
instructionSet[RETURNDATASIZE] = &operation{
execute: opReturnDataSize,
@ -222,17 +205,13 @@ func newByzantiumInstructionSet() JumpTable {
instructionSet[RETURNDATACOPY] = &operation{
execute: opReturnDataCopy,
constantGas: GasFastestStep,
dynamicGas: gasReturnDataCopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryReturnDataCopy,
}
instructionSet[REVERT] = &operation{
execute: opRevert,
dynamicGas: gasRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
execute: opRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
}
return validate(instructionSet)
}
@ -240,7 +219,7 @@ func newByzantiumInstructionSet() JumpTable {
// EIP 158 a.k.a Spurious Dragon
func newSpuriousDragonInstructionSet() JumpTable {
instructionSet := newTangerineWhistleInstructionSet()
instructionSet[EXP].dynamicGas = gasExpEIP158
instructionSet[EXP].execute = opExpEIP158
return validate(instructionSet)
}
@ -262,12 +241,10 @@ func newTangerineWhistleInstructionSet() JumpTable {
func newHomesteadInstructionSet() JumpTable {
instructionSet := newFrontierInstructionSet()
instructionSet[DELEGATECALL] = &operation{
execute: opDelegateCall,
dynamicGas: gasDelegateCall,
execute: opDelegateCallHomestead,
constantGas: params.CallGasFrontier,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
}
return validate(instructionSet)
}
@ -337,10 +314,9 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(3, 1),
},
EXP: {
execute: opExp,
dynamicGas: gasExpFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
execute: opExpFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
},
SIGNEXTEND: {
execute: opSignExtend,
@ -417,10 +393,8 @@ func newFrontierInstructionSet() JumpTable {
KECCAK256: {
execute: opKeccak256,
constantGas: params.Keccak256Gas,
dynamicGas: gasKeccak256,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
memorySize: memoryKeccak256,
},
ADDRESS: {
execute: opAddress,
@ -467,10 +441,8 @@ func newFrontierInstructionSet() JumpTable {
CALLDATACOPY: {
execute: opCallDataCopy,
constantGas: GasFastestStep,
dynamicGas: gasCallDataCopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCallDataCopy,
},
CODESIZE: {
execute: opCodeSize,
@ -479,12 +451,10 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(0, 1),
},
CODECOPY: {
execute: opCodeCopy,
execute: opCodeCopyFrontier,
constantGas: GasFastestStep,
dynamicGas: gasCodeCopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCodeCopy,
},
GASPRICE: {
execute: opGasprice,
@ -499,12 +469,10 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(1, 1),
},
EXTCODECOPY: {
execute: opExtCodeCopy,
execute: opExtCodeCopyFrontier,
constantGas: params.ExtcodeCopyBaseFrontier,
dynamicGas: gasExtCodeCopy,
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryExtCodeCopy,
},
BLOCKHASH: {
execute: opBlockhash,
@ -551,24 +519,18 @@ func newFrontierInstructionSet() JumpTable {
MLOAD: {
execute: opMload,
constantGas: GasFastestStep,
dynamicGas: gasMLoad,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
memorySize: memoryMLoad,
},
MSTORE: {
execute: opMstore,
constantGas: GasFastestStep,
dynamicGas: gasMStore,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryMStore,
},
MSTORE8: {
execute: opMstore8,
constantGas: GasFastestStep,
dynamicGas: gasMStore8,
memorySize: memoryMStore8,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
@ -579,10 +541,9 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(1, 1),
},
SSTORE: {
execute: opSstore,
dynamicGas: gasSStore,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
execute: opSstoreFrontier,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
JUMP: {
execute: opJump,
@ -1005,76 +966,57 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxSwapStack(17),
},
LOG0: {
execute: makeLog(0),
dynamicGas: makeGasLog(0),
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryLog,
execute: makeLog(0),
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
LOG1: {
execute: makeLog(1),
dynamicGas: makeGasLog(1),
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryLog,
execute: makeLog(1),
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
},
LOG2: {
execute: makeLog(2),
dynamicGas: makeGasLog(2),
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryLog,
execute: makeLog(2),
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
},
LOG3: {
execute: makeLog(3),
dynamicGas: makeGasLog(3),
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
memorySize: memoryLog,
execute: makeLog(3),
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
},
LOG4: {
execute: makeLog(4),
dynamicGas: makeGasLog(4),
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
memorySize: memoryLog,
execute: makeLog(4),
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
},
CREATE: {
execute: opCreate,
execute: opCreateFrontier,
constantGas: params.CreateGas,
dynamicGas: gasCreate,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
memorySize: memoryCreate,
},
CALL: {
execute: opCall,
execute: opCallFrontier,
constantGas: params.CallGasFrontier,
dynamicGas: gasCall,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
},
CALLCODE: {
execute: opCallCode,
execute: opCallCodeFrontier,
constantGas: params.CallGasFrontier,
dynamicGas: gasCallCode,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
},
RETURN: {
execute: opReturn,
dynamicGas: gasReturn,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryReturn,
execute: opReturn,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
},
SELFDESTRUCT: {
execute: opSelfdestruct,
dynamicGas: gasSelfdestruct,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
execute: opSelfdestructFrontier,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
},
INVALID: {
execute: opUndefined,

View file

@ -62,17 +62,3 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
func (op *operation) Stack() (int, int) {
return op.minStack, op.maxStack
}
// HasCost returns true if the opcode has a cost. Opcodes which do _not_ have
// a cost assigned are one of two things:
// - undefined, a.k.a invalid opcodes,
// - the STOP opcode.
// This method can thus be used to check if an opcode is "Invalid (or STOP)".
func (op *operation) HasCost() bool {
// Ideally, we'd check this:
// return op.execute == opUndefined
// However, go-lang does now allow that. So we'll just check some other
// 'indicators' that this is an invalid op. Alas, STOP is impossible to
// filter out
return op.dynamicGas != nil || op.constantGas != 0
}