diff --git a/cmd/evm/testdata/evmrun/7.out.2.txt b/cmd/evm/testdata/evmrun/7.out.2.txt index d81d96c6f6..3019496263 100644 --- a/cmd/evm/testdata/evmrun/7.out.2.txt +++ b/cmd/evm/testdata/evmrun/7.out.2.txt @@ -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 | [] | diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 71f4dcff9a..2e8dfed7fd 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -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 || diff --git a/core/vm/common.go b/core/vm/common.go index 658803b820..1b1085ae71 100644 --- a/core/vm/common.go +++ b/core/vm/common.go @@ -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 diff --git a/core/vm/eips.go b/core/vm/eips.go index 23758d284e..ccb099c5e1 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -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 } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index fb52d8b6ff..95c31a0987 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -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() diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 8a82de5d8b..317cb1aee0 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -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) diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 33bf9496bb..b6a44550b0 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -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 { diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 22eed8754f..37e334ea82 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -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, diff --git a/core/vm/jump_table_export.go b/core/vm/jump_table_export.go index 89a2ebf6f4..bc41512074 100644 --- a/core/vm/jump_table_export.go +++ b/core/vm/jump_table_export.go @@ -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 -}