From d6837fcd5e35caa841eb61abf42abd08727b502f Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 15 May 2026 20:08:04 +0200 Subject: [PATCH 1/8] Add basic block runner mode --- vm/bytecode.go | 126 ++ vm/gen/main.go | 251 +++- vm/runner.go | 6 +- vm/runner_modes_test.go | 91 ++ vm/table_gen.go | 2479 +++++++++++++++++++++++++-------------- 5 files changed, 2055 insertions(+), 898 deletions(-) create mode 100644 vm/runner_modes_test.go diff --git a/vm/bytecode.go b/vm/bytecode.go index bd57ff10..7490f2ab 100644 --- a/vm/bytecode.go +++ b/vm/bytecode.go @@ -2,6 +2,7 @@ package vm import ( "encoding/binary" + "math" "github.com/Giulio2002/gevm/types" ) @@ -24,10 +25,38 @@ type Bytecode struct { jumpTableReady bool // Whether the jump table was set externally (must not be mutated by ensureJumpTable). jumpTableExternal bool + // Basic block metadata for the optimized interpreter. + basicBlocks []BasicBlockInfo + blockStarts []uint16 + blocksReady bool // The hash of the bytecode, lazily computed. hash *types.B256 } +// BasicBlockInfo holds precomputed static requirements for a straight-line +// bytecode block. Fork-varying gas is stored as small counters. +type BasicBlockInfo struct { + ConstGas uint64 + StackRequired int16 + StackMaxGrowth int16 + BalanceOps uint16 + ExtCodeSizeOps uint16 + ExtCodeHashOps uint16 + SloadOps uint16 + CallOps uint16 + SelfdestructOps uint16 +} + +func (b *BasicBlockInfo) baseGas(fg ForkGas) uint64 { + return b.ConstGas + + uint64(b.BalanceOps)*fg.Balance + + uint64(b.ExtCodeSizeOps)*fg.ExtCodeSize + + uint64(b.ExtCodeHashOps)*fg.ExtCodeHash + + uint64(b.SloadOps)*fg.Sload + + uint64(b.CallOps)*fg.Call + + uint64(b.SelfdestructOps)*fg.Selfdestruct +} + // bytecodeEndPadding is the number of zero bytes appended after bytecode. // Must be >= 33 to safely read PUSH32 immediates (1 opcode + 32 data bytes) // and ReadU16 (2 bytes) at any position without bounds checking. @@ -55,6 +84,7 @@ func NewBytecode(code []byte) *Bytecode { running: true, } bc.ensureJumpTable() + bc.ensureBasicBlocks() return bc } @@ -76,6 +106,7 @@ func (b *Bytecode) ResetWithHash(code []byte, hash types.B256) { b.pc = 0 b.running = true // jumpTable + jumpTableReady still valid from previous Reset + // basicBlocks + blockStarts still valid from previous Reset return } @@ -103,6 +134,7 @@ func (b *Bytecode) Reset(code []byte) { b.running = true b.hash = nil b.jumpTableReady = false // defer analysis until first IsValidJump + b.blocksReady = false } // SetJumpTable sets an externally-provided jump table, skipping analysis. @@ -125,6 +157,100 @@ func (b *Bytecode) GetJumpTable() []byte { return b.jumpTable } +func (b *Bytecode) BasicBlockAt(pc int) *BasicBlockInfo { + b.ensureBasicBlocks() + if pc < 0 || pc >= len(b.blockStarts) { + return nil + } + idx := b.blockStarts[pc] + if idx == 0 { + return nil + } + return &b.basicBlocks[idx-1] +} + +func (b *Bytecode) ensureBasicBlocks() { + if b.blocksReady { + return + } + b.blocksReady = true + if b.originalLen == 0 { + b.basicBlocks = b.basicBlocks[:0] + if cap(b.blockStarts) > 0 { + b.blockStarts = b.blockStarts[:0] + } + return + } + if cap(b.blockStarts) >= b.originalLen { + b.blockStarts = b.blockStarts[:b.originalLen] + clear(b.blockStarts) + } else { + b.blockStarts = make([]uint16, b.originalLen) + } + b.basicBlocks = b.basicBlocks[:0] + for pc := 0; pc < b.originalLen; { + start := pc + block := BasicBlockInfo{} + var stackChange int16 + for pc < b.originalLen { + op := b.code[pc] + info := blockInstructionInfo(op) + if pc != start && info.startsBlock { + break + } + block.addGas(info) + required := info.stackRequired - stackChange + if required > block.StackRequired { + block.StackRequired = required + } + stackChange += info.stackChange + if stackChange > block.StackMaxGrowth { + block.StackMaxGrowth = stackChange + } + pc += instructionLen(op) + if info.endsBlock { + break + } + } + if len(b.basicBlocks) == math.MaxUint16 { + // Extremely fragmented bytecode should still execute correctly. + // Stop indexing further blocks; the optimized runner falls back to + // per-op checks for unindexed PCs only when it cannot find metadata. + return + } + b.basicBlocks = append(b.basicBlocks, block) + b.blockStarts[start] = uint16(len(b.basicBlocks)) + if pc == start { + pc++ + } + } +} + +func (b *BasicBlockInfo) addGas(info blockOpcodeInfo) { + b.ConstGas += info.constGas + switch info.forkGas { + case blockGasBalance: + b.BalanceOps++ + case blockGasExtCodeSize: + b.ExtCodeSizeOps++ + case blockGasExtCodeHash: + b.ExtCodeHashOps++ + case blockGasSload: + b.SloadOps++ + case blockGasCall: + b.CallOps++ + case blockGasSelfdestruct: + b.SelfdestructOps++ + } +} + +func instructionLen(op byte) int { + if op >= 0x60 && op <= 0x7f { + return int(op-0x5f) + 1 + } + return 1 +} + // ensureJumpTable builds the jump table if not yet built for the current code. func (b *Bytecode) ensureJumpTable() { if b.jumpTableReady { diff --git a/vm/gen/main.go b/vm/gen/main.go index 21b3b074..8e87a7a9 100644 --- a/vm/gen/main.go +++ b/vm/gen/main.go @@ -260,9 +260,10 @@ func substituteLocals(body string) string { // --------------------------------------------------------------------------- type emitter struct { - buf *bytes.Buffer - bodies map[string]string - tracing bool // true → per-opcode gas deduction, hooks; false → gas accumulator + buf *bytes.Buffer + bodies map[string]string + immediateGas bool // true → per-opcode gas deduction; false → optimized block charging + optimized bool } func (e *emitter) p(format string, args ...interface{}) { @@ -271,9 +272,12 @@ func (e *emitter) p(format string, args ...interface{}) { // emitGas emits gas charging for a single opcode. // In accumulator mode: gasCounter += expr. -// In tracing mode: immediate check + deduction from gas.remaining. +// In immediate mode: immediate check + deduction from gas.remaining. func (e *emitter) emitGas(gasExpr string) { - if e.tracing { + if e.optimized { + return + } + if e.immediateGas { e.p("if gas.remaining < %s {\n", gasExpr) e.p("interp.HaltOOG()\n") e.p("return\n") @@ -285,9 +289,9 @@ func (e *emitter) emitGas(gasExpr string) { } // emitFlush emits the gasCounter flush check + deduction. -// No-op in tracing mode (gas is deducted per-opcode). +// No-op in immediate/optimized mode. func (e *emitter) emitFlush() { - if e.tracing { + if e.immediateGas || e.optimized { return } e.p("if gas.remaining < gasCounter {\n") @@ -299,9 +303,9 @@ func (e *emitter) emitFlush() { } // emitFlushOnError emits flush-on-error inside a stack check failure branch. -// No-op in tracing mode (gas already deducted). +// No-op in immediate/optimized mode. func (e *emitter) emitFlushOnError() { - if e.tracing { + if e.immediateGas || e.optimized { return } e.p("if gas.remaining < gasCounter {\n") @@ -406,6 +410,10 @@ func (e *emitter) emitAccumulateForkGated(op opDef) { // emitShapedBody emits the stack check + body for shaped opcodes. func (e *emitter) emitShapedBody(op opDef) { + if e.optimized { + e.emitUncheckedShapedBody(op) + return + } switch op.shape { case shapeBinaryOp: e.p("s := interp.Stack\n") @@ -461,10 +469,43 @@ func (e *emitter) emitShapedBody(op opDef) { } } +func (e *emitter) emitUncheckedShapedBody(op opDef) { + switch op.shape { + case shapeBinaryOp: + e.p("s := interp.Stack\n") + e.p("s.top--\n") + e.emitBody(op) + case shapeUnaryOp: + if op.inline && op.funcName != "" { + e.p("s := interp.Stack\n") + } + e.emitBody(op) + case shapeTernaryOp: + e.p("s := interp.Stack\n") + e.p("s.top -= 2\n") + e.emitBody(op) + case shapePushVal: + if op.inline && op.funcName != "" { + e.p("s := interp.Stack\n") + } + e.emitBody(op) + case shapePop1: + e.emitBody(op) + case shapeCustom: + e.emitBody(op) + } +} + // emitDup emits a DUP case (n=1..16). func (e *emitter) emitDup(n int) { e.p("case opcode.DUP%d:\n", n) e.emitGas("spec.GasVerylow") + if e.optimized { + e.p("s := interp.Stack\n") + e.p("s.data[s.top] = s.data[s.top-%d]\n", n) + e.p("s.top++\n") + return + } e.p("s := interp.Stack\n") e.p("if s.top < %d || s.top >= StackLimit {\n", n) e.emitFlushOnError() @@ -479,6 +520,12 @@ func (e *emitter) emitDup(n int) { func (e *emitter) emitSwap(n int) { e.p("case opcode.SWAP%d:\n", n) e.emitGas("spec.GasVerylow") + if e.optimized { + e.p("s := interp.Stack\n") + e.p("t := s.top - 1\n") + e.p("s.data[t], s.data[t-%d] = s.data[t-%d], s.data[t]\n", n, n) + return + } e.p("s := interp.Stack\n") e.p("t := s.top - 1\n") e.p("if t < %d {\n", n) @@ -515,6 +562,14 @@ func (e *emitter) emitPushGeneric() { } e.p(":\n") e.emitGas("spec.GasVerylow") + if e.optimized { + e.p("s := interp.Stack\n") + e.p("n := int(op - opcode.PUSH0)\n") + e.p("s.data[s.top] = *new(uint256.Int).SetBytes(bc.code[bc.pc : bc.pc+n])\n") + e.p("bc.pc += n\n") + e.p("s.top++\n") + return + } e.p("s := interp.Stack\n") e.p("if s.top >= StackLimit {\n") e.emitFlushOnError() @@ -537,6 +592,14 @@ func (e *emitter) emitPush(op opDef) { e.p("interp.HaltNotActivated()\n") e.p("} else {\n") } + if e.optimized { + e.p("s := interp.Stack\n") + e.emitInlineBody(op) + if op.fork != "" { + e.p("}\n") + } + return + } e.p("s := interp.Stack\n") e.p("if s.top >= StackLimit {\n") e.emitFlushOnError() @@ -583,18 +646,36 @@ func (e *emitter) emitAllCases() { // --------------------------------------------------------------------------- func (e *emitter) emitRunFunc() { - e.tracing = false + e.immediateGas = false + e.optimized = true e.p(`// Run executes bytecode until halted using direct switch dispatch. -// Static gas is accumulated in a local gasCounter variable. Instead of -// checking and deducting gas per-instruction, static gas costs are summed -// across a basic block and flushed (checked + deducted) at block boundaries -// (jumps, dynamic-gas opcodes, halting opcodes). This eliminates one branch -// + one memory write per static-gas instruction in the hot loop. +// Static gas and stack requirements are checked once per precomputed basic +// block. Dynamic opcodes are block boundaries, preserving exact GAS/call +// semantics while avoiding per-opcode static gas and stack checks in the +// straight-line hot path. func (DefaultRunner) Run(interp *Interpreter, host Host) { bc := interp.Bytecode gas := &interp.Gas -var gasCounter uint64 for bc.running { +if block := bc.BasicBlockAt(bc.pc); block != nil { + baseGas := block.baseGas(interp.ForkGas) + if gas.remaining < baseGas { + interp.HaltOOG() + return + } + sTop := interp.Stack.top + if sTop < int(block.StackRequired) { + gas.remaining -= baseGas + interp.HaltUnderflow() + return + } + if sTop+int(block.StackMaxGrowth) > StackLimit { + gas.remaining -= baseGas + interp.HaltOverflow() + return + } + gas.remaining -= baseGas +} op := bc.code[bc.pc] bc.pc++ @@ -604,6 +685,29 @@ switch op { e.p("}\n") // switch e.p("}\n") // for e.p("}\n") // func + e.optimized = false +} + +func (e *emitter) emitPlainRunFunc() { + e.immediateGas = true + e.optimized = false + e.p(`// Run executes bytecode with simple per-opcode gas and stack checks. +// It deliberately avoids the basic-block optimization and is intended for +// RPC/debug paths that prefer direct opcode-by-opcode behavior. +func (PlainRunner) Run(interp *Interpreter, host Host) { +bc := interp.Bytecode +gas := &interp.Gas +for bc.running { +op := bc.code[bc.pc] +bc.pc++ + +switch op { +`) + e.emitAllCases() + e.p("}\n") + e.p("}\n") + e.p("}\n") + e.immediateGas = false } // --------------------------------------------------------------------------- @@ -611,7 +715,8 @@ switch op { // --------------------------------------------------------------------------- func (e *emitter) emitRunWithTracingFunc() { - e.tracing = true + e.immediateGas = true + e.optimized = false e.p(`// Run executes bytecode with per-opcode tracing hooks. // Unlike DefaultRunner.Run, there is no gas accumulator — each opcode // deducts its static gas immediately, so the tracer receives accurate @@ -658,7 +763,7 @@ switch op { e.p("}\n") // for e.p("}\n") // func - e.tracing = false + e.immediateGas = false } // --------------------------------------------------------------------------- @@ -674,6 +779,112 @@ func gasToTableExpr(gas string) string { return gas } +func blockGasKind(gas string) string { + switch gas { + case "interp.ForkGas.Balance": + return "blockGasBalance" + case "interp.ForkGas.ExtCodeSize": + return "blockGasExtCodeSize" + case "interp.ForkGas.ExtCodeHash": + return "blockGasExtCodeHash" + case "interp.ForkGas.Sload": + return "blockGasSload" + case "interp.ForkGas.Call": + return "blockGasCall" + case "interp.ForkGas.Selfdestruct": + return "blockGasSelfdestruct" + default: + return "blockGasNone" + } +} + +func stackEffectForShape(s shape) (required, change int) { + switch s { + case shapeBinaryOp: + return 2, -1 + case shapeUnaryOp: + return 1, 0 + case shapeTernaryOp: + return 3, -2 + case shapePushVal: + return 0, 1 + case shapePop1: + return 1, -1 + default: + return 0, 0 + } +} + +func endsBasicBlock(op opDef) bool { + if op.mode == modeFlush && op.name != "JUMPDEST" { + return true + } + switch op.name { + case "STOP", "JUMP", "JUMPI", "RETURN", "REVERT", "INVALID", "SELFDESTRUCT": + return true + default: + return false + } +} + +func (e *emitter) emitBlockInstructionInfo() { + e.p("type blockGasKind uint8\n\n") + e.p("const (\n") + e.p("blockGasNone blockGasKind = iota\n") + e.p("blockGasBalance\n") + e.p("blockGasExtCodeSize\n") + e.p("blockGasExtCodeHash\n") + e.p("blockGasSload\n") + e.p("blockGasCall\n") + e.p("blockGasSelfdestruct\n") + e.p(")\n\n") + e.p("type blockOpcodeInfo struct {\n") + e.p("constGas uint64\n") + e.p("forkGas blockGasKind\n") + e.p("stackRequired int16\n") + e.p("stackChange int16\n") + e.p("startsBlock bool\n") + e.p("endsBlock bool\n") + e.p("}\n\n") + e.p("func blockInstructionInfo(op byte) blockOpcodeInfo {\n") + e.p("switch op {\n") + for _, op := range opcodes { + req, change := stackEffectForShape(op.shape) + starts := op.name == "JUMPDEST" || (op.mode == modeFlush && op.name != "JUMPDEST") + ends := endsBasicBlock(op) + constGas := "0" + forkGas := "blockGasNone" + if op.gas != "" { + if strings.HasPrefix(op.gas, "interp.ForkGas.") { + forkGas = blockGasKind(op.gas) + } else { + constGas = op.gas + } + } + e.p("case opcode.%s:\n", op.name) + e.p("return blockOpcodeInfo{constGas: %s, forkGas: %s, stackRequired: %d, stackChange: %d, startsBlock: %t, endsBlock: %t}\n", + constGas, forkGas, req, change, starts, ends) + } + e.p("case opcode.PUSH0:\n") + e.p("return blockOpcodeInfo{constGas: spec.GasBase, stackChange: 1}\n") + e.p("}\n") + e.p("switch {\n") + e.p("case op >= opcode.PUSH1 && op <= opcode.PUSH32:\n") + e.p("return blockOpcodeInfo{constGas: spec.GasVerylow, stackChange: 1}\n") + e.p("case op >= opcode.DUP1 && op <= opcode.DUP16:\n") + e.p("n := int16(op - opcode.DUP1 + 1)\n") + e.p("return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n, stackChange: 1}\n") + e.p("case op >= opcode.SWAP1 && op <= opcode.SWAP16:\n") + e.p("n := int16(op - opcode.SWAP1 + 2)\n") + e.p("return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n}\n") + e.p("case op >= opcode.LOG0 && op <= opcode.LOG4:\n") + e.p("return blockOpcodeInfo{startsBlock: true, endsBlock: true}\n") + e.p("default:\n") + e.p("return blockOpcodeInfo{endsBlock: true}\n") + e.p("}\n") + e.p("}\n\n") +} + func (e *emitter) emitDebugGasTable() { e.p("// buildDebugGasTable constructs a per-opcode static gas cost table.\n") e.p("// Used by the tracer to report cost in OnOpcode/OnFault hooks.\n") @@ -751,8 +962,12 @@ func main() { var buf bytes.Buffer e := &emitter{buf: &buf, bodies: bodies} e.emitHeader() + e.emitBlockInstructionInfo() + e.p("\n") e.emitRunFunc() e.p("\n") + e.emitPlainRunFunc() + e.p("\n") e.emitRunWithTracingFunc() e.p("\n") e.emitDebugGasTable() diff --git a/vm/runner.go b/vm/runner.go index 3b2c8687..c51a9445 100644 --- a/vm/runner.go +++ b/vm/runner.go @@ -10,10 +10,14 @@ type Runner interface { Run(interp *Interpreter, host Host) } -// DefaultRunner is the fast-path runner with gas accumulator and zero tracing overhead. +// DefaultRunner is the fast path with basic-block gas and stack checks. // Its Run method is generated in table_gen.go. type DefaultRunner struct{} +// PlainRunner is the simple RPC/debug runner with per-opcode gas and stack checks. +// Its Run method is generated in table_gen.go. +type PlainRunner struct{} + // TracingRunner executes with per-opcode gas deduction and tracing hooks. // Its Run method is generated in table_gen.go. // If Hooks.OnOpcode is nil, Run delegates to DefaultRunner for the fast path. diff --git a/vm/runner_modes_test.go b/vm/runner_modes_test.go new file mode 100644 index 00000000..878cff2a --- /dev/null +++ b/vm/runner_modes_test.go @@ -0,0 +1,91 @@ +package vm + +import ( + "testing" + + "github.com/Giulio2002/gevm/opcode" + "github.com/Giulio2002/gevm/spec" +) + +func runWithRunner(runner Runner, code []byte, gasLimit uint64) *Interpreter { + interp := NewInterpreter(NewMemory(), NewBytecode(code), Inputs{}, false, spec.Prague, gasLimit) + runner.Run(interp, nil) + return interp +} + +func TestDefaultRunnerMatchesPlainRunnerForSimpleBlock(t *testing.T) { + code := []byte{byte(opcode.PUSH1), 1, byte(opcode.PUSH1), 2, byte(opcode.ADD), byte(opcode.STOP)} + + fast := runWithRunner(DefaultRunner{}, code, 100) + plain := runWithRunner(PlainRunner{}, code, 100) + + if fast.HaltResult != plain.HaltResult { + t.Fatalf("halt mismatch: fast=%v plain=%v", fast.HaltResult, plain.HaltResult) + } + if fast.Gas.Remaining() != plain.Gas.Remaining() { + t.Fatalf("gas mismatch: fast=%d plain=%d", fast.Gas.Remaining(), plain.Gas.Remaining()) + } + if fast.StackLen() != 1 || plain.StackLen() != 1 { + t.Fatalf("stack len mismatch: fast=%d plain=%d", fast.StackLen(), plain.StackLen()) + } + if fast.Stack.data[0].Uint64() != 3 || plain.Stack.data[0].Uint64() != 3 { + t.Fatalf("stack value mismatch: fast=%d plain=%d", fast.Stack.data[0].Uint64(), plain.Stack.data[0].Uint64()) + } +} + +func TestDefaultRunnerMatchesPlainRunnerFailures(t *testing.T) { + for _, tc := range []struct { + name string + code []byte + gas uint64 + }{ + {name: "underflow", code: []byte{byte(opcode.ADD)}, gas: 100}, + {name: "oog-before-underflow", code: []byte{byte(opcode.ADD)}, gas: 2}, + } { + t.Run(tc.name, func(t *testing.T) { + fast := runWithRunner(DefaultRunner{}, tc.code, tc.gas) + plain := runWithRunner(PlainRunner{}, tc.code, tc.gas) + if fast.HaltResult != plain.HaltResult { + t.Fatalf("halt mismatch: fast=%v plain=%v", fast.HaltResult, plain.HaltResult) + } + if fast.Gas.Remaining() != plain.Gas.Remaining() { + t.Fatalf("gas mismatch: fast=%d plain=%d", fast.Gas.Remaining(), plain.Gas.Remaining()) + } + }) + } +} + +func TestDefaultRunnerGasOpcodeMatchesPlainRunner(t *testing.T) { + code := []byte{byte(opcode.GAS), byte(opcode.STOP)} + + fast := runWithRunner(DefaultRunner{}, code, 10) + plain := runWithRunner(PlainRunner{}, code, 10) + + if fast.HaltResult != plain.HaltResult { + t.Fatalf("halt mismatch: fast=%v plain=%v", fast.HaltResult, plain.HaltResult) + } + if fast.StackLen() != 1 || plain.StackLen() != 1 { + t.Fatalf("stack len mismatch: fast=%d plain=%d", fast.StackLen(), plain.StackLen()) + } + if fast.Stack.data[0] != plain.Stack.data[0] { + t.Fatalf("GAS opcode mismatch: fast=%d plain=%d", fast.Stack.data[0].Uint64(), plain.Stack.data[0].Uint64()) + } +} + +func TestTracingRunnerWithoutOpcodeHookUsesDefaultPath(t *testing.T) { + code := []byte{byte(opcode.PUSH1), 1, byte(opcode.PUSH1), 2, byte(opcode.ADD), byte(opcode.STOP)} + hooks := &Hooks{OnExit: func(int, []byte, uint64, error, bool) {}} + + fast := runWithRunner(DefaultRunner{}, code, 100) + tracing := runWithRunner(NewTracingRunner(hooks, spec.Prague), code, 100) + + if fast.HaltResult != tracing.HaltResult { + t.Fatalf("halt mismatch: fast=%v tracing=%v", fast.HaltResult, tracing.HaltResult) + } + if fast.Gas.Remaining() != tracing.Gas.Remaining() { + t.Fatalf("gas mismatch: fast=%d tracing=%d", fast.Gas.Remaining(), tracing.Gas.Remaining()) + } + if fast.StackLen() != tracing.StackLen() { + t.Fatalf("stack len mismatch: fast=%d tracing=%d", fast.StackLen(), tracing.StackLen()) + } +} diff --git a/vm/table_gen.go b/vm/table_gen.go index 36f92e82..76a6abea 100644 --- a/vm/table_gen.go +++ b/vm/table_gen.go @@ -15,41 +15,1132 @@ var ( _ = opcode.STOP ) +type blockGasKind uint8 + +const ( + blockGasNone blockGasKind = iota + blockGasBalance + blockGasExtCodeSize + blockGasExtCodeHash + blockGasSload + blockGasCall + blockGasSelfdestruct +) + +type blockOpcodeInfo struct { + constGas uint64 + forkGas blockGasKind + stackRequired int16 + stackChange int16 + startsBlock bool + endsBlock bool +} + +func blockInstructionInfo(op byte) blockOpcodeInfo { + switch op { + case opcode.STOP: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.ADD: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.MUL: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SUB: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.DIV: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SDIV: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.MOD: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SMOD: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.ADDMOD: + return blockOpcodeInfo{constGas: spec.GasMid, forkGas: blockGasNone, stackRequired: 3, stackChange: -2, startsBlock: false, endsBlock: false} + case opcode.MULMOD: + return blockOpcodeInfo{constGas: spec.GasMid, forkGas: blockGasNone, stackRequired: 3, stackChange: -2, startsBlock: false, endsBlock: false} + case opcode.EXP: + return blockOpcodeInfo{constGas: spec.GasHigh, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.SIGNEXTEND: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.LT: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.GT: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SLT: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SGT: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.EQ: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.ISZERO: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} + case opcode.AND: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.OR: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.XOR: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.NOT: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} + case opcode.BYTE: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SHL: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SHR: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.SAR: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.CLZ: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} + case opcode.KECCAK256: + return blockOpcodeInfo{constGas: spec.GasKeccak256, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.ADDRESS: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.BALANCE: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasBalance, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.ORIGIN: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.CALLER: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.CALLVALUE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.CALLDATALOAD: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} + case opcode.CALLDATASIZE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.CALLDATACOPY: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.CODESIZE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.CODECOPY: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.GASPRICE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.EXTCODESIZE: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasExtCodeSize, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.EXTCODECOPY: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasExtCodeSize, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.RETURNDATASIZE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.RETURNDATACOPY: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.EXTCODEHASH: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasExtCodeHash, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.BLOCKHASH: + return blockOpcodeInfo{constGas: spec.GasBlockhash, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} + case opcode.COINBASE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.TIMESTAMP: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.NUMBER: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.DIFFICULTY: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.GASLIMIT: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.CHAINID: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.SELFBALANCE: + return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.BASEFEE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.BLOBHASH: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.BLOBBASEFEE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.SLOTNUM: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.POP: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 1, stackChange: -1, startsBlock: false, endsBlock: false} + case opcode.MLOAD: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.MSTORE: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.MSTORE8: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.SLOAD: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasSload, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.SSTORE: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.JUMP: + return blockOpcodeInfo{constGas: spec.GasMid, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.JUMPI: + return blockOpcodeInfo{constGas: spec.GasHigh, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.PC: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.MSIZE: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} + case opcode.GAS: + return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.JUMPDEST: + return blockOpcodeInfo{constGas: spec.GasJumpdest, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: false} + case opcode.TLOAD: + return blockOpcodeInfo{constGas: spec.GasWarmStorageReadCost, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.TSTORE: + return blockOpcodeInfo{constGas: spec.GasWarmStorageReadCost, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.MCOPY: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.DUPN: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.SWAPN: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.EXCHANGE: + return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.CREATE: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.CALL: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.CALLCODE: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.RETURN: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.DELEGATECALL: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.CREATE2: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.STATICCALL: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.REVERT: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.INVALID: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.SELFDESTRUCT: + return blockOpcodeInfo{constGas: 0, forkGas: blockGasSelfdestruct, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} + case opcode.PUSH0: + return blockOpcodeInfo{constGas: spec.GasBase, stackChange: 1} + } + switch { + case op >= opcode.PUSH1 && op <= opcode.PUSH32: + return blockOpcodeInfo{constGas: spec.GasVerylow, stackChange: 1} + case op >= opcode.DUP1 && op <= opcode.DUP16: + n := int16(op - opcode.DUP1 + 1) + return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n, stackChange: 1} + case op >= opcode.SWAP1 && op <= opcode.SWAP16: + n := int16(op - opcode.SWAP1 + 2) + return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n} + case op >= opcode.LOG0 && op <= opcode.LOG4: + return blockOpcodeInfo{startsBlock: true, endsBlock: true} + default: + return blockOpcodeInfo{endsBlock: true} + } +} + // Run executes bytecode until halted using direct switch dispatch. -// Static gas is accumulated in a local gasCounter variable. Instead of -// checking and deducting gas per-instruction, static gas costs are summed -// across a basic block and flushed (checked + deducted) at block boundaries -// (jumps, dynamic-gas opcodes, halting opcodes). This eliminates one branch -// + one memory write per static-gas instruction in the hot loop. +// Static gas and stack requirements are checked once per precomputed basic +// block. Dynamic opcodes are block boundaries, preserving exact GAS/call +// semantics while avoiding per-opcode static gas and stack checks in the +// straight-line hot path. func (DefaultRunner) Run(interp *Interpreter, host Host) { bc := interp.Bytecode gas := &interp.Gas - var gasCounter uint64 + for bc.running { + if block := bc.BasicBlockAt(bc.pc); block != nil { + baseGas := block.baseGas(interp.ForkGas) + if gas.remaining < baseGas { + interp.HaltOOG() + return + } + sTop := interp.Stack.top + if sTop < int(block.StackRequired) { + gas.remaining -= baseGas + interp.HaltUnderflow() + return + } + if sTop+int(block.StackMaxGrowth) > StackLimit { + gas.remaining -= baseGas + interp.HaltOverflow() + return + } + gas.remaining -= baseGas + } + op := bc.code[bc.pc] + bc.pc++ + + switch op { + case opcode.STOP: + + interp.Halt(InstructionResultStop) + + case opcode.ADD: + s := interp.Stack + s.top-- + + s.data[s.top-1].Add(&s.data[s.top], &s.data[s.top-1]) + + case opcode.MUL: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + top.Mul(&a, top) + + case opcode.SUB: + s := interp.Stack + s.top-- + + s.data[s.top-1].Sub(&s.data[s.top], &s.data[s.top-1]) + + case opcode.DIV: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + if !top.IsZero() { + top.Div(&a, top) + } + + case opcode.SDIV: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + top.SDiv(&a, top) + + case opcode.MOD: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + if !top.IsZero() { + top.Mod(&a, top) + } + + case opcode.SMOD: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + top.SMod(&a, top) + + case opcode.ADDMOD: + s := interp.Stack + s.top -= 2 + + a := s.data[s.top+1] + b := s.data[s.top] + top := &s.data[s.top-1] + top.AddMod(&a, &b, top) + + case opcode.MULMOD: + s := interp.Stack + s.top -= 2 + + a := s.data[s.top+1] + b := s.data[s.top] + top := &s.data[s.top-1] + top.MulMod(&a, &b, top) + + case opcode.EXP: + opExp(interp) + case opcode.SIGNEXTEND: + s := interp.Stack + s.top-- + + ext := s.data[s.top] + top := &s.data[s.top-1] + top.ExtendSign(top, &ext) + + case opcode.LT: + s := interp.Stack + s.top-- + + if s.data[s.top].Lt(&s.data[s.top-1]) { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.GT: + s := interp.Stack + s.top-- + + if s.data[s.top].Gt(&s.data[s.top-1]) { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.SLT: + s := interp.Stack + s.top-- + + a := &s.data[s.top] + b := &s.data[s.top-1] + aNeg := a[3] >> 63 + bNeg := b[3] >> 63 + var lt bool + if aNeg != bNeg { + lt = aNeg > bNeg + } else { + lt = a.Lt(b) + } + if lt { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.SGT: + s := interp.Stack + s.top-- + + a := &s.data[s.top] + b := &s.data[s.top-1] + aNeg := a[3] >> 63 + bNeg := b[3] >> 63 + var gt bool + if aNeg != bNeg { + gt = bNeg > aNeg + } else { + gt = a.Gt(b) + } + if gt { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.EQ: + s := interp.Stack + s.top-- + + if s.data[s.top].Eq(&s.data[s.top-1]) { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.ISZERO: + s := interp.Stack + + if s.data[s.top-1].IsZero() { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.AND: + s := interp.Stack + s.top-- + + s.data[s.top-1].And(&s.data[s.top], &s.data[s.top-1]) + + case opcode.OR: + s := interp.Stack + s.top-- + + s.data[s.top-1].Or(&s.data[s.top], &s.data[s.top-1]) + + case opcode.XOR: + s := interp.Stack + s.top-- + + s.data[s.top-1].Xor(&s.data[s.top], &s.data[s.top-1]) + + case opcode.NOT: + s := interp.Stack + + s.data[s.top-1].Not(&s.data[s.top-1]) + + case opcode.BYTE: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + idx, overflow := a.Uint64WithOverflow() + if !overflow && idx < 32 { + index := uint256.Int{idx, 0, 0, 0} + top.Byte(&index) + } else { + *top = uint256.Int{} + } + + case opcode.SHL: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + s := interp.Stack + s.top-- + + shift := s.data[s.top] + top := &s.data[s.top-1] + sa, overflow := shift.Uint64WithOverflow() + if !overflow && sa < 256 { + top.Lsh(top, uint(sa)) + } else { + *top = uint256.Int{} + } + + } + case opcode.SHR: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + s := interp.Stack + s.top-- + + shift := s.data[s.top] + top := &s.data[s.top-1] + sa, overflow := shift.Uint64WithOverflow() + if !overflow && sa < 256 { + top.Rsh(top, uint(sa)) + } else { + *top = uint256.Int{} + } + + } + case opcode.SAR: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + s := interp.Stack + s.top-- + + shift := s.data[s.top] + top := &s.data[s.top-1] + sa, overflow := shift.Uint64WithOverflow() + if !overflow && sa < 256 { + top.SRsh(top, uint(sa)) + } else if top[3]&(1<<63) != 0 { + *top = uint256.Int{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)} + } else { + *top = uint256.Int{} + } + + } + case opcode.CLZ: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Osaka) { + interp.HaltNotActivated() + } else { + s := interp.Stack + + top := &s.data[s.top-1] + *top = uint256.Int{uint64(256 - top.BitLen()), 0, 0, 0} + + } + case opcode.KECCAK256: + opKeccak256(interp) + case opcode.ADDRESS: + s := interp.Stack + + s.data[s.top] = interp.Input.TargetAddress.ToU256() + s.top++ + + case opcode.BALANCE: + opBalance(interp, host) + case opcode.ORIGIN: + s := interp.Stack + + addr := host.Caller() + s.data[s.top] = addr.ToU256() + s.top++ + + case opcode.CALLER: + s := interp.Stack + + s.data[s.top] = interp.Input.CallerAddress.ToU256() + s.top++ + + case opcode.CALLVALUE: + s := interp.Stack + + s.data[s.top] = interp.Input.CallValue + s.top++ + + case opcode.CALLDATALOAD: + s := interp.Stack + + top := &s.data[s.top-1] + offset, overflow := top.Uint64WithOverflow() + if overflow { + offset = ^uint64(0) + } + input := interp.Input.Input + var word [32]byte + if offset < uint64(len(input)) { + src := input[offset:] + if len(src) >= 32 { + copy(word[:], src[:32]) + } else { + copy(word[:], src) + } + } + *top = *new(uint256.Int).SetBytes32((word)[:]) + + case opcode.CALLDATASIZE: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(len(interp.Input.Input)), 0, 0, 0} + s.top++ + + case opcode.CALLDATACOPY: + opCalldatacopy(interp) + case opcode.CODESIZE: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(bc.originalLen), 0, 0, 0} + s.top++ + + case opcode.CODECOPY: + opCodecopy(interp) + case opcode.GASPRICE: + s := interp.Stack + + s.data[s.top] = host.EffectiveGasPrice() + s.top++ + + case opcode.EXTCODESIZE: + opExtcodesize(interp, host) + case opcode.EXTCODECOPY: + opExtcodecopy(interp, host) + case opcode.RETURNDATASIZE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(len(interp.ReturnData)), 0, 0, 0} + s.top++ + + } + case opcode.RETURNDATACOPY: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + opReturndatacopy(interp) + } + case opcode.EXTCODEHASH: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + opExtcodehash(interp, host) + } + case opcode.BLOCKHASH: + s := interp.Stack + + top := &s.data[s.top-1] + hash := host.BlockHash(*top) + *top = hash.ToU256() + + case opcode.COINBASE: + s := interp.Stack + + addr := host.Beneficiary() + s.data[s.top] = addr.ToU256() + s.top++ + + case opcode.TIMESTAMP: + s := interp.Stack + + s.data[s.top] = host.Timestamp() + s.top++ + + case opcode.NUMBER: + s := interp.Stack + + s.data[s.top] = host.BlockNumber() + s.top++ + + case opcode.DIFFICULTY: + opDifficulty(interp, host) + case opcode.GASLIMIT: + s := interp.Stack + + s.data[s.top] = host.GasLimit() + s.top++ + + case opcode.CHAINID: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { + interp.HaltNotActivated() + } else { + opChainid(interp, host) + } + case opcode.SELFBALANCE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { + interp.HaltNotActivated() + } else { + opSelfbalance(interp, host) + } + case opcode.BASEFEE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.London) { + interp.HaltNotActivated() + } else { + opBasefee(interp, host) + } + case opcode.BLOBHASH: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opBlobhash(interp, host) + } + case opcode.BLOBBASEFEE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opBlobbasefee(interp, host) + } + case opcode.SLOTNUM: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opSlotnum(interp, host) + } + case opcode.POP: + + interp.Stack.top-- + + case opcode.MLOAD: + + s := interp.Stack + if s.top == 0 { + interp.HaltUnderflow() + return + } + top := &s.data[s.top-1] + if top[1]|top[2]|top[3] == 0 { + offset := int(top[0]) + if offset >= 0 && offset+32 <= interp.Memory.Len() { + *top = interp.Memory.GetU256(offset) + } else { + if interp.ResizeMemory(offset, 32) { + *top = interp.Memory.GetU256(offset) + } + } + } else { + interp.Halt(InstructionResultInvalidOperandOOG) + } + + case opcode.MSTORE: + + s := interp.Stack + if s.top < 2 { + interp.HaltUnderflow() + return + } + s.top -= 2 + offsetVal := s.data[s.top+1] + value := s.data[s.top] + if offsetVal[1]|offsetVal[2]|offsetVal[3] == 0 { + offset := int(offsetVal[0]) + if offset >= 0 && offset+32 <= interp.Memory.Len() { + interp.Memory.SetU256(offset, value) + } else { + if interp.ResizeMemory(offset, 32) { + interp.Memory.SetU256(offset, value) + } + } + } else { + interp.Halt(InstructionResultInvalidOperandOOG) + } + + case opcode.MSTORE8: + opMstore8(interp) + case opcode.SLOAD: + opSload(interp, host) + case opcode.SSTORE: + opSstore(interp, host) + case opcode.JUMP: + + s := interp.Stack + if s.top == 0 { + interp.HaltUnderflow() + return + } + s.top-- + target := s.data[s.top] + if target[1]|target[2]|target[3] != 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + dest := int(target[0]) + if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { + interp.Halt(InstructionResultInvalidJump) + return + } + if !bc.jumpTableReady { + bc.ensureJumpTable() + } + if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + bc.pc = dest + + case opcode.JUMPI: + + s := interp.Stack + if s.top < 2 { + interp.HaltUnderflow() + return + } + s.top -= 2 + cond := s.data[s.top] + target := s.data[s.top+1] + if !cond.IsZero() { + if target[1]|target[2]|target[3] != 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + dest := int(target[0]) + if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { + interp.Halt(InstructionResultInvalidJump) + return + } + if !bc.jumpTableReady { + bc.ensureJumpTable() + } + if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + bc.pc = dest + } + + case opcode.PC: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(bc.pc - 1), 0, 0, 0} + s.top++ + + case opcode.MSIZE: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(interp.Memory.Len()), 0, 0, 0} + s.top++ + + case opcode.GAS: + opGas(interp) + case opcode.JUMPDEST: + case opcode.TLOAD: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opTload(interp, host) + } + case opcode.TSTORE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opTstore(interp, host) + } + case opcode.MCOPY: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opMcopy(interp) + } + case opcode.DUPN: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opDupN(interp) + } + case opcode.SWAPN: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opSwapN(interp) + } + case opcode.EXCHANGE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opExchange(interp) + } + case opcode.CREATE: + opCreate(interp, host) + case opcode.CALL: + opCall(interp, host) + case opcode.CALLCODE: + opCallcode(interp, host) + case opcode.RETURN: + opReturn(interp) + case opcode.DELEGATECALL: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Homestead) { + interp.HaltNotActivated() + } else { + opDelegatecall(interp, host) + } + case opcode.CREATE2: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Petersburg) { + interp.HaltNotActivated() + } else { + opCreate2(interp, host) + } + case opcode.STATICCALL: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + opStaticcall(interp, host) + } + case opcode.REVERT: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + opRevert(interp) + } + case opcode.INVALID: + + interp.Halt(InstructionResultInvalidFEOpcode) + + case opcode.SELFDESTRUCT: + opSelfdestruct(interp, host) + case opcode.PUSH0: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Shanghai) { + interp.HaltNotActivated() + } else { + s := interp.Stack + + s.data[s.top] = uint256.Int{} + s.top++ + + } + case opcode.PUSH1: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(bc.code[bc.pc]), 0, 0, 0} + bc.pc++ + s.top++ + + case opcode.PUSH2: + s := interp.Stack + + v := uint64(bc.code[bc.pc])<<8 | uint64(bc.code[bc.pc+1]) + bc.pc += 2 + s.data[s.top] = uint256.Int{v, 0, 0, 0} + s.top++ + + case opcode.PUSH3: + s := interp.Stack + + c := bc.code + p := bc.pc + v := uint64(c[p])<<16 | uint64(c[p+1])<<8 | uint64(c[p+2]) + bc.pc = p + 3 + s.data[s.top] = uint256.Int{v, 0, 0, 0} + s.top++ + + case opcode.PUSH4: + s := interp.Stack + + c := bc.code + p := bc.pc + v := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) + bc.pc = p + 4 + s.data[s.top] = uint256.Int{v, 0, 0, 0} + s.top++ + + case opcode.PUSH20: + s := interp.Stack + + c := bc.code + p := bc.pc + // 20 bytes = limb2 (4 bytes) + limb1 (8 bytes) + limb0 (8 bytes) + l2 := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) + l1 := uint64(c[p+4])<<56 | uint64(c[p+5])<<48 | uint64(c[p+6])<<40 | uint64(c[p+7])<<32 | + uint64(c[p+8])<<24 | uint64(c[p+9])<<16 | uint64(c[p+10])<<8 | uint64(c[p+11]) + l0 := uint64(c[p+12])<<56 | uint64(c[p+13])<<48 | uint64(c[p+14])<<40 | uint64(c[p+15])<<32 | + uint64(c[p+16])<<24 | uint64(c[p+17])<<16 | uint64(c[p+18])<<8 | uint64(c[p+19]) + bc.pc = p + 20 + s.data[s.top] = uint256.Int{l0, l1, l2, 0} + s.top++ + + case opcode.PUSH32: + s := interp.Stack + + c := bc.code + p := bc.pc + l3 := uint64(c[p])<<56 | uint64(c[p+1])<<48 | uint64(c[p+2])<<40 | uint64(c[p+3])<<32 | + uint64(c[p+4])<<24 | uint64(c[p+5])<<16 | uint64(c[p+6])<<8 | uint64(c[p+7]) + l2 := uint64(c[p+8])<<56 | uint64(c[p+9])<<48 | uint64(c[p+10])<<40 | uint64(c[p+11])<<32 | + uint64(c[p+12])<<24 | uint64(c[p+13])<<16 | uint64(c[p+14])<<8 | uint64(c[p+15]) + l1 := uint64(c[p+16])<<56 | uint64(c[p+17])<<48 | uint64(c[p+18])<<40 | uint64(c[p+19])<<32 | + uint64(c[p+20])<<24 | uint64(c[p+21])<<16 | uint64(c[p+22])<<8 | uint64(c[p+23]) + l0 := uint64(c[p+24])<<56 | uint64(c[p+25])<<48 | uint64(c[p+26])<<40 | uint64(c[p+27])<<32 | + uint64(c[p+28])<<24 | uint64(c[p+29])<<16 | uint64(c[p+30])<<8 | uint64(c[p+31]) + bc.pc = p + 32 + s.data[s.top] = uint256.Int{l0, l1, l2, l3} + s.top++ + + case opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.PUSH17, opcode.PUSH18, opcode.PUSH19, opcode.PUSH21, opcode.PUSH22, opcode.PUSH23, opcode.PUSH24, opcode.PUSH25, opcode.PUSH26, opcode.PUSH27, opcode.PUSH28, opcode.PUSH29, opcode.PUSH30, opcode.PUSH31: + s := interp.Stack + n := int(op - opcode.PUSH0) + s.data[s.top] = *new(uint256.Int).SetBytes(bc.code[bc.pc : bc.pc+n]) + bc.pc += n + s.top++ + case opcode.DUP1: + s := interp.Stack + s.data[s.top] = s.data[s.top-1] + s.top++ + case opcode.DUP2: + s := interp.Stack + s.data[s.top] = s.data[s.top-2] + s.top++ + case opcode.DUP3: + s := interp.Stack + s.data[s.top] = s.data[s.top-3] + s.top++ + case opcode.DUP4: + s := interp.Stack + s.data[s.top] = s.data[s.top-4] + s.top++ + case opcode.DUP5: + s := interp.Stack + s.data[s.top] = s.data[s.top-5] + s.top++ + case opcode.DUP6: + s := interp.Stack + s.data[s.top] = s.data[s.top-6] + s.top++ + case opcode.DUP7: + s := interp.Stack + s.data[s.top] = s.data[s.top-7] + s.top++ + case opcode.DUP8: + s := interp.Stack + s.data[s.top] = s.data[s.top-8] + s.top++ + case opcode.DUP9: + s := interp.Stack + s.data[s.top] = s.data[s.top-9] + s.top++ + case opcode.DUP10: + s := interp.Stack + s.data[s.top] = s.data[s.top-10] + s.top++ + case opcode.DUP11: + s := interp.Stack + s.data[s.top] = s.data[s.top-11] + s.top++ + case opcode.DUP12: + s := interp.Stack + s.data[s.top] = s.data[s.top-12] + s.top++ + case opcode.DUP13: + s := interp.Stack + s.data[s.top] = s.data[s.top-13] + s.top++ + case opcode.DUP14: + s := interp.Stack + s.data[s.top] = s.data[s.top-14] + s.top++ + case opcode.DUP15: + s := interp.Stack + s.data[s.top] = s.data[s.top-15] + s.top++ + case opcode.DUP16: + s := interp.Stack + s.data[s.top] = s.data[s.top-16] + s.top++ + case opcode.SWAP1: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-1] = s.data[t-1], s.data[t] + case opcode.SWAP2: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-2] = s.data[t-2], s.data[t] + case opcode.SWAP3: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-3] = s.data[t-3], s.data[t] + case opcode.SWAP4: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-4] = s.data[t-4], s.data[t] + case opcode.SWAP5: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-5] = s.data[t-5], s.data[t] + case opcode.SWAP6: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-6] = s.data[t-6], s.data[t] + case opcode.SWAP7: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-7] = s.data[t-7], s.data[t] + case opcode.SWAP8: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-8] = s.data[t-8], s.data[t] + case opcode.SWAP9: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-9] = s.data[t-9], s.data[t] + case opcode.SWAP10: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-10] = s.data[t-10], s.data[t] + case opcode.SWAP11: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-11] = s.data[t-11], s.data[t] + case opcode.SWAP12: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-12] = s.data[t-12], s.data[t] + case opcode.SWAP13: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-13] = s.data[t-13], s.data[t] + case opcode.SWAP14: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-14] = s.data[t-14], s.data[t] + case opcode.SWAP15: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-15] = s.data[t-15], s.data[t] + case opcode.SWAP16: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-16] = s.data[t-16], s.data[t] + case opcode.LOG0: + logNImpl(interp, host, 0) + case opcode.LOG1: + logNImpl(interp, host, 1) + case opcode.LOG2: + logNImpl(interp, host, 2) + case opcode.LOG3: + logNImpl(interp, host, 3) + case opcode.LOG4: + logNImpl(interp, host, 4) + default: + interp.Halt(InstructionResultOpcodeNotFound) + } + } +} + +// Run executes bytecode with simple per-opcode gas and stack checks. +// It deliberately avoids the basic-block optimization and is intended for +// RPC/debug paths that prefer direct opcode-by-opcode behavior. +func (PlainRunner) Run(interp *Interpreter, host Host) { + bc := interp.Bytecode + gas := &interp.Gas for bc.running { op := bc.code[bc.pc] bc.pc++ switch op { case opcode.STOP: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.Halt(InstructionResultStop) case opcode.ADD: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -58,15 +1149,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MUL: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -77,15 +1166,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.SUB: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -94,15 +1181,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.DIV: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -115,15 +1200,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.SDIV: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -134,15 +1217,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MOD: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -155,15 +1236,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.SMOD: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -174,15 +1253,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.ADDMOD: - gasCounter += spec.GasMid + if gas.remaining < spec.GasMid { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasMid s := interp.Stack if s.top < 3 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top -= 2 @@ -194,15 +1271,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MULMOD: - gasCounter += spec.GasMid + if gas.remaining < spec.GasMid { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasMid s := interp.Stack if s.top < 3 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top -= 2 @@ -214,24 +1289,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.EXP: - gasCounter += spec.GasHigh - if gas.remaining < gasCounter { + if gas.remaining < spec.GasHigh { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasHigh opExp(interp) case opcode.SIGNEXTEND: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -242,15 +1313,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.LT: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -263,15 +1332,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.GT: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -284,15 +1351,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.SLT: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -315,15 +1380,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.SGT: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -346,15 +1409,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.EQ: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -367,15 +1428,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.ISZERO: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top == 0 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { @@ -387,15 +1446,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.AND: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -404,15 +1461,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.OR: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -421,15 +1476,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.XOR: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -438,15 +1491,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.NOT: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top == 0 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { @@ -454,15 +1505,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.BYTE: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -479,24 +1528,16 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.SHL: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -513,24 +1554,16 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } } case opcode.SHR: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -547,24 +1580,16 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } } case opcode.SAR: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -583,24 +1608,16 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } } case opcode.CLZ: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Osaka) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top == 0 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { @@ -610,24 +1627,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } } case opcode.KECCAK256: - gasCounter += spec.GasKeccak256 - if gas.remaining < gasCounter { + if gas.remaining < spec.GasKeccak256 { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasKeccak256 opKeccak256(interp) case opcode.ADDRESS: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -636,24 +1649,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.BALANCE: - gasCounter += interp.ForkGas.Balance - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Balance { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Balance opBalance(interp, host) case opcode.ORIGIN: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -663,15 +1672,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLER: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -680,15 +1687,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLVALUE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -697,15 +1702,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLDATALOAD: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top == 0 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { @@ -728,15 +1731,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLDATASIZE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -745,24 +1746,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLDATACOPY: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow opCalldatacopy(interp) case opcode.CODESIZE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -771,24 +1768,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CODECOPY: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow opCodecopy(interp) case opcode.GASPRICE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -797,42 +1790,30 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.EXTCODESIZE: - gasCounter += interp.ForkGas.ExtCodeSize - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.ExtCodeSize { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.ExtCodeSize opExtcodesize(interp, host) case opcode.EXTCODECOPY: - gasCounter += interp.ForkGas.ExtCodeSize - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.ExtCodeSize { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.ExtCodeSize opExtcodecopy(interp, host) case opcode.RETURNDATASIZE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -842,41 +1823,35 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } } case opcode.RETURNDATACOPY: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { interp.HaltNotActivated() } else { opReturndatacopy(interp) } case opcode.EXTCODEHASH: - gasCounter += interp.ForkGas.ExtCodeHash - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.ExtCodeHash { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.ExtCodeHash if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { interp.HaltNotActivated() } else { opExtcodehash(interp, host) } case opcode.BLOCKHASH: - gasCounter += spec.GasBlockhash + if gas.remaining < spec.GasBlockhash { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBlockhash s := interp.Stack if s.top == 0 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { @@ -886,15 +1861,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.COINBASE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -904,15 +1877,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.TIMESTAMP: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -921,15 +1892,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.NUMBER: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -938,24 +1907,20 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.DIFFICULTY: - gasCounter += spec.GasBase - if gas.remaining < gasCounter { + if gas.remaining < spec.GasBase { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasBase opDifficulty(interp, host) case opcode.GASLIMIT: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -964,147 +1929,103 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.CHAINID: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { opChainid(interp, host) } } case opcode.SELFBALANCE: - gasCounter += spec.GasLow + if gas.remaining < spec.GasLow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasLow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { opSelfbalance(interp, host) } } case opcode.BASEFEE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.London) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { opBasefee(interp, host) } } case opcode.BLOBHASH: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opBlobhash(interp, host) } case opcode.BLOBBASEFEE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { opBlobbasefee(interp, host) } } case opcode.SLOTNUM: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { opSlotnum(interp, host) } } case opcode.POP: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if interp.Stack.top == 0 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltUnderflow() } else { @@ -1112,13 +2033,11 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MLOAD: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top == 0 { @@ -1140,13 +2059,11 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MSTORE: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 { @@ -1170,39 +2087,27 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MSTORE8: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow opMstore8(interp) case opcode.SLOAD: - gasCounter += interp.ForkGas.Sload - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Sload { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Sload opSload(interp, host) case opcode.SSTORE: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 opSstore(interp, host) case opcode.JUMP: - gasCounter += spec.GasMid - if gas.remaining < gasCounter { + if gas.remaining < spec.GasMid { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasMid s := interp.Stack if s.top == 0 { @@ -1230,13 +2135,11 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { bc.pc = dest case opcode.JUMPI: - gasCounter += spec.GasHigh - if gas.remaining < gasCounter { + if gas.remaining < spec.GasHigh { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasHigh s := interp.Stack if s.top < 2 { @@ -1267,15 +2170,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PC: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1284,15 +2185,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.MSIZE: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1301,222 +2200,158 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.GAS: - gasCounter += spec.GasBase - if gas.remaining < gasCounter { + if gas.remaining < spec.GasBase { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasBase opGas(interp) case opcode.JUMPDEST: - gasCounter += spec.GasJumpdest - if gas.remaining < gasCounter { + if gas.remaining < spec.GasJumpdest { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasJumpdest case opcode.TLOAD: - gasCounter += spec.GasWarmStorageReadCost - if gas.remaining < gasCounter { + if gas.remaining < spec.GasWarmStorageReadCost { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasWarmStorageReadCost if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opTload(interp, host) } case opcode.TSTORE: - gasCounter += spec.GasWarmStorageReadCost - if gas.remaining < gasCounter { + if gas.remaining < spec.GasWarmStorageReadCost { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasWarmStorageReadCost if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opTstore(interp, host) } case opcode.MCOPY: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opMcopy(interp) } case opcode.DUPN: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { interp.HaltNotActivated() } else { opDupN(interp) } case opcode.SWAPN: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { interp.HaltNotActivated() } else { opSwapN(interp) } case opcode.EXCHANGE: - gasCounter += spec.GasVerylow - if gas.remaining < gasCounter { + if gas.remaining < spec.GasVerylow { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { interp.HaltNotActivated() } else { opExchange(interp) } case opcode.CREATE: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 opCreate(interp, host) case opcode.CALL: - gasCounter += interp.ForkGas.Call - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Call { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Call opCall(interp, host) case opcode.CALLCODE: - gasCounter += interp.ForkGas.Call - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Call { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Call opCallcode(interp, host) case opcode.RETURN: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 opReturn(interp) case opcode.DELEGATECALL: - gasCounter += interp.ForkGas.Call - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Call { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Call if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Homestead) { interp.HaltNotActivated() } else { opDelegatecall(interp, host) } case opcode.CREATE2: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Petersburg) { interp.HaltNotActivated() } else { opCreate2(interp, host) } case opcode.STATICCALL: - gasCounter += interp.ForkGas.Call - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Call { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Call if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { interp.HaltNotActivated() } else { opStaticcall(interp, host) } case opcode.REVERT: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { interp.HaltNotActivated() } else { opRevert(interp) } case opcode.INVALID: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.Halt(InstructionResultInvalidFEOpcode) case opcode.SELFDESTRUCT: - gasCounter += interp.ForkGas.Selfdestruct - if gas.remaining < gasCounter { + if gas.remaining < interp.ForkGas.Selfdestruct { interp.HaltOOG() return } - gas.remaining -= gasCounter - gasCounter = 0 + gas.remaining -= interp.ForkGas.Selfdestruct opSelfdestruct(interp, host) case opcode.PUSH0: - gasCounter += spec.GasBase + if gas.remaining < spec.GasBase { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Shanghai) { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1526,15 +2361,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } } case opcode.PUSH1: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1544,15 +2377,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH2: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1563,15 +2394,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH3: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1584,15 +2413,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH4: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1605,15 +2432,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH20: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1631,15 +2456,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH32: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { @@ -1659,15 +2482,13 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.PUSH17, opcode.PUSH18, opcode.PUSH19, opcode.PUSH21, opcode.PUSH22, opcode.PUSH23, opcode.PUSH24, opcode.PUSH25, opcode.PUSH26, opcode.PUSH27, opcode.PUSH28, opcode.PUSH29, opcode.PUSH30, opcode.PUSH31: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { n := int(op - opcode.PUSH0) @@ -1676,532 +2497,432 @@ func (DefaultRunner) Run(interp *Interpreter, host Host) { s.top++ } case opcode.DUP1: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 1 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-1] s.top++ } case opcode.DUP2: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 2 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-2] s.top++ } case opcode.DUP3: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 3 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-3] s.top++ } case opcode.DUP4: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 4 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-4] s.top++ } case opcode.DUP5: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 5 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-5] s.top++ } case opcode.DUP6: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 6 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-6] s.top++ } case opcode.DUP7: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 7 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-7] s.top++ } case opcode.DUP8: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 8 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-8] s.top++ } case opcode.DUP9: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 9 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-9] s.top++ } case opcode.DUP10: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 10 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-10] s.top++ } case opcode.DUP11: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 11 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-11] s.top++ } case opcode.DUP12: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 12 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-12] s.top++ } case opcode.DUP13: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 13 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-13] s.top++ } case opcode.DUP14: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 14 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-14] s.top++ } case opcode.DUP15: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 15 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-15] s.top++ } case opcode.DUP16: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack if s.top < 16 || s.top >= StackLimit { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-16] s.top++ } case opcode.SWAP1: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 1 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-1] = s.data[t-1], s.data[t] } case opcode.SWAP2: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 2 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-2] = s.data[t-2], s.data[t] } case opcode.SWAP3: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 3 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-3] = s.data[t-3], s.data[t] } case opcode.SWAP4: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 4 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-4] = s.data[t-4], s.data[t] } case opcode.SWAP5: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 5 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-5] = s.data[t-5], s.data[t] } case opcode.SWAP6: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 6 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-6] = s.data[t-6], s.data[t] } case opcode.SWAP7: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 7 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-7] = s.data[t-7], s.data[t] } case opcode.SWAP8: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 8 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-8] = s.data[t-8], s.data[t] } case opcode.SWAP9: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 9 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-9] = s.data[t-9], s.data[t] } case opcode.SWAP10: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 10 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-10] = s.data[t-10], s.data[t] } case opcode.SWAP11: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 11 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-11] = s.data[t-11], s.data[t] } case opcode.SWAP12: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 12 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-12] = s.data[t-12], s.data[t] } case opcode.SWAP13: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 13 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-13] = s.data[t-13], s.data[t] } case opcode.SWAP14: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 14 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-14] = s.data[t-14], s.data[t] } case opcode.SWAP15: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 15 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-15] = s.data[t-15], s.data[t] } case opcode.SWAP16: - gasCounter += spec.GasVerylow + if gas.remaining < spec.GasVerylow { + interp.HaltOOG() + return + } + gas.remaining -= spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 16 { - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-16] = s.data[t-16], s.data[t] } case opcode.LOG0: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 logNImpl(interp, host, 0) case opcode.LOG1: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 logNImpl(interp, host, 1) case opcode.LOG2: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 logNImpl(interp, host, 2) case opcode.LOG3: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 logNImpl(interp, host, 3) case opcode.LOG4: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 logNImpl(interp, host, 4) default: - if gas.remaining < gasCounter { - interp.HaltOOG() - return - } - gas.remaining -= gasCounter - gasCounter = 0 interp.Halt(InstructionResultOpcodeNotFound) } } From d725878565fc9717ff1a98cf454a82c5f67d51bf Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 15 May 2026 20:25:06 +0200 Subject: [PATCH 2/8] Add runner mode benchmarks --- vm/runner_benchmark_test.go | 112 ++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 vm/runner_benchmark_test.go diff --git a/vm/runner_benchmark_test.go b/vm/runner_benchmark_test.go new file mode 100644 index 00000000..bbfedf49 --- /dev/null +++ b/vm/runner_benchmark_test.go @@ -0,0 +1,112 @@ +package vm + +import ( + "testing" + + "github.com/Giulio2002/gevm/opcode" + "github.com/Giulio2002/gevm/spec" +) + +var ( + benchmarkRunnerGasSink uint64 + benchmarkOpcodeSink uint64 +) + +func BenchmarkRunnerStraightLine(b *testing.B) { + code := makeStraightLineBenchCode(256) + benchmarkRunnerModes(b, code) +} + +func BenchmarkRunnerBlockBoundaries(b *testing.B) { + code := makeBlockBoundaryBenchCode(256) + benchmarkRunnerModes(b, code) +} + +func benchmarkRunnerModes(b *testing.B, code []byte) { + b.Helper() + + b.Run("Default", func(b *testing.B) { + benchmarkRunner(b, DefaultRunner{}, code) + }) + b.Run("Plain", func(b *testing.B) { + benchmarkRunner(b, PlainRunner{}, code) + }) + b.Run("TracingNoOpcodeHook", func(b *testing.B) { + hooks := &Hooks{OnExit: func(int, []byte, uint64, error, bool) {}} + benchmarkRunner(b, NewTracingRunner(hooks, spec.Prague), code) + }) + b.Run("TracingOpcodeHook", func(b *testing.B) { + hooks := &Hooks{ + OnOpcode: func(uint64, byte, uint64, uint64, OpContext, []byte, int, error) { + benchmarkOpcodeSink++ + }, + } + benchmarkRunner(b, NewTracingRunner(hooks, spec.Prague), code) + }) +} + +func benchmarkRunner(b *testing.B, runner Runner, code []byte) { + b.Helper() + + const gasLimit = uint64(100_000_000) + bytecode := NewBytecode(code) + interp := NewInterpreter(NewMemory(), bytecode, Inputs{}, false, spec.Prague, gasLimit) + + resetBenchmarkInterpreter(interp, gasLimit) + runner.Run(interp, nil) + if interp.HaltResult != InstructionResultStop { + b.Fatalf("warmup halt result: got %v, want %v", interp.HaltResult, InstructionResultStop) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + resetBenchmarkInterpreter(interp, gasLimit) + runner.Run(interp, nil) + if interp.HaltResult != InstructionResultStop { + b.Fatalf("halt result: got %v, want %v", interp.HaltResult, InstructionResultStop) + } + } + b.StopTimer() + + benchmarkRunnerGasSink += interp.Gas.Remaining() + uint64(interp.StackLen()) +} + +func resetBenchmarkInterpreter(interp *Interpreter, gasLimit uint64) { + interp.Bytecode.pc = 0 + interp.Bytecode.running = true + interp.Gas = NewGas(gasLimit) + interp.Stack.Clear() + interp.ReturnData = nil + interp.Memory.Reset() + interp.HasAction = false + interp.HaltResult = 0 +} + +func makeStraightLineBenchCode(repetitions int) []byte { + code := make([]byte, 0, repetitions*6+1) + for i := 0; i < repetitions; i++ { + code = append(code, + byte(opcode.PUSH1), byte(i), + byte(opcode.PUSH1), byte(i+1), + byte(opcode.ADD), + byte(opcode.POP), + ) + } + code = append(code, byte(opcode.STOP)) + return code +} + +func makeBlockBoundaryBenchCode(repetitions int) []byte { + code := make([]byte, 0, repetitions*4+1) + for i := 0; i < repetitions; i++ { + code = append(code, + byte(opcode.PUSH1), byte(i), + byte(opcode.POP), + byte(opcode.GAS), + byte(opcode.POP), + ) + } + code = append(code, byte(opcode.STOP)) + return code +} From b8e11a7c6721d53215fcf543ff93a2ad7f993747 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 15 May 2026 21:24:17 +0200 Subject: [PATCH 3/8] Speed up default runner block dispatch --- vm/bytecode.go | 2 + vm/gen/main.go | 49 ++- vm/table_gen.go | 934 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 968 insertions(+), 17 deletions(-) diff --git a/vm/bytecode.go b/vm/bytecode.go index 7490f2ab..d9628b81 100644 --- a/vm/bytecode.go +++ b/vm/bytecode.go @@ -37,6 +37,7 @@ type Bytecode struct { // bytecode block. Fork-varying gas is stored as small counters. type BasicBlockInfo struct { ConstGas uint64 + EndPC int32 StackRequired int16 StackMaxGrowth int16 BalanceOps uint16 @@ -219,6 +220,7 @@ func (b *Bytecode) ensureBasicBlocks() { return } b.basicBlocks = append(b.basicBlocks, block) + b.basicBlocks[len(b.basicBlocks)-1].EndPC = int32(pc) b.blockStarts[start] = uint16(len(b.basicBlocks)) if pc == start { pc++ diff --git a/vm/gen/main.go b/vm/gen/main.go index 8e87a7a9..8236174b 100644 --- a/vm/gen/main.go +++ b/vm/gen/main.go @@ -656,8 +656,54 @@ func (e *emitter) emitRunFunc() { func (DefaultRunner) Run(interp *Interpreter, host Host) { bc := interp.Bytecode gas := &interp.Gas +bc.ensureBasicBlocks() +if len(bc.basicBlocks) > 1 && bc.originalLen/len(bc.basicBlocks) < 8 { + PlainRunner{}.Run(interp, host) + return +} +if len(bc.basicBlocks) == 1 { + block := &bc.basicBlocks[0] + baseGas := block.baseGas(interp.ForkGas) + if gas.remaining < baseGas { + interp.HaltOOG() + return + } + sTop := interp.Stack.top + if sTop < int(block.StackRequired) { + gas.remaining -= baseGas + interp.HaltUnderflow() + return + } + if sTop+int(block.StackMaxGrowth) > StackLimit { + gas.remaining -= baseGas + interp.HaltOverflow() + return + } + gas.remaining -= baseGas + for bc.running { + op := bc.code[bc.pc] + bc.pc++ + + switch op { +`) + e.emitAllCases() + e.p(` } + } + return +} +blockStartPC := -1 +blockEndPC := 0 for bc.running { -if block := bc.BasicBlockAt(bc.pc); block != nil { +if bc.pc < blockStartPC || bc.pc >= blockEndPC { + block := bc.BasicBlockAt(bc.pc) + if block != nil { + blockStartPC = bc.pc + blockEndPC = int(block.EndPC) + } else { + blockStartPC = bc.pc + blockEndPC = bc.pc + 1 + } + if block != nil { baseGas := block.baseGas(interp.ForkGas) if gas.remaining < baseGas { interp.HaltOOG() @@ -675,6 +721,7 @@ if block := bc.BasicBlockAt(bc.pc); block != nil { return } gas.remaining -= baseGas + } } op := bc.code[bc.pc] bc.pc++ diff --git a/vm/table_gen.go b/vm/table_gen.go index 76a6abea..000c4b26 100644 --- a/vm/table_gen.go +++ b/vm/table_gen.go @@ -233,25 +233,927 @@ func blockInstructionInfo(op byte) blockOpcodeInfo { func (DefaultRunner) Run(interp *Interpreter, host Host) { bc := interp.Bytecode gas := &interp.Gas - for bc.running { - if block := bc.BasicBlockAt(bc.pc); block != nil { - baseGas := block.baseGas(interp.ForkGas) - if gas.remaining < baseGas { - interp.HaltOOG() - return - } - sTop := interp.Stack.top - if sTop < int(block.StackRequired) { - gas.remaining -= baseGas - interp.HaltUnderflow() - return + bc.ensureBasicBlocks() + if len(bc.basicBlocks) > 1 && bc.originalLen/len(bc.basicBlocks) < 8 { + PlainRunner{}.Run(interp, host) + return + } + if len(bc.basicBlocks) == 1 { + block := &bc.basicBlocks[0] + baseGas := block.baseGas(interp.ForkGas) + if gas.remaining < baseGas { + interp.HaltOOG() + return + } + sTop := interp.Stack.top + if sTop < int(block.StackRequired) { + gas.remaining -= baseGas + interp.HaltUnderflow() + return + } + if sTop+int(block.StackMaxGrowth) > StackLimit { + gas.remaining -= baseGas + interp.HaltOverflow() + return + } + gas.remaining -= baseGas + for bc.running { + op := bc.code[bc.pc] + bc.pc++ + + switch op { + case opcode.STOP: + + interp.Halt(InstructionResultStop) + + case opcode.ADD: + s := interp.Stack + s.top-- + + s.data[s.top-1].Add(&s.data[s.top], &s.data[s.top-1]) + + case opcode.MUL: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + top.Mul(&a, top) + + case opcode.SUB: + s := interp.Stack + s.top-- + + s.data[s.top-1].Sub(&s.data[s.top], &s.data[s.top-1]) + + case opcode.DIV: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + if !top.IsZero() { + top.Div(&a, top) + } + + case opcode.SDIV: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + top.SDiv(&a, top) + + case opcode.MOD: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + if !top.IsZero() { + top.Mod(&a, top) + } + + case opcode.SMOD: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + top.SMod(&a, top) + + case opcode.ADDMOD: + s := interp.Stack + s.top -= 2 + + a := s.data[s.top+1] + b := s.data[s.top] + top := &s.data[s.top-1] + top.AddMod(&a, &b, top) + + case opcode.MULMOD: + s := interp.Stack + s.top -= 2 + + a := s.data[s.top+1] + b := s.data[s.top] + top := &s.data[s.top-1] + top.MulMod(&a, &b, top) + + case opcode.EXP: + opExp(interp) + case opcode.SIGNEXTEND: + s := interp.Stack + s.top-- + + ext := s.data[s.top] + top := &s.data[s.top-1] + top.ExtendSign(top, &ext) + + case opcode.LT: + s := interp.Stack + s.top-- + + if s.data[s.top].Lt(&s.data[s.top-1]) { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.GT: + s := interp.Stack + s.top-- + + if s.data[s.top].Gt(&s.data[s.top-1]) { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.SLT: + s := interp.Stack + s.top-- + + a := &s.data[s.top] + b := &s.data[s.top-1] + aNeg := a[3] >> 63 + bNeg := b[3] >> 63 + var lt bool + if aNeg != bNeg { + lt = aNeg > bNeg + } else { + lt = a.Lt(b) + } + if lt { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.SGT: + s := interp.Stack + s.top-- + + a := &s.data[s.top] + b := &s.data[s.top-1] + aNeg := a[3] >> 63 + bNeg := b[3] >> 63 + var gt bool + if aNeg != bNeg { + gt = bNeg > aNeg + } else { + gt = a.Gt(b) + } + if gt { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.EQ: + s := interp.Stack + s.top-- + + if s.data[s.top].Eq(&s.data[s.top-1]) { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.ISZERO: + s := interp.Stack + + if s.data[s.top-1].IsZero() { + s.data[s.top-1] = uint256.Int{1, 0, 0, 0} + } else { + s.data[s.top-1] = uint256.Int{} + } + + case opcode.AND: + s := interp.Stack + s.top-- + + s.data[s.top-1].And(&s.data[s.top], &s.data[s.top-1]) + + case opcode.OR: + s := interp.Stack + s.top-- + + s.data[s.top-1].Or(&s.data[s.top], &s.data[s.top-1]) + + case opcode.XOR: + s := interp.Stack + s.top-- + + s.data[s.top-1].Xor(&s.data[s.top], &s.data[s.top-1]) + + case opcode.NOT: + s := interp.Stack + + s.data[s.top-1].Not(&s.data[s.top-1]) + + case opcode.BYTE: + s := interp.Stack + s.top-- + + a := s.data[s.top] + top := &s.data[s.top-1] + idx, overflow := a.Uint64WithOverflow() + if !overflow && idx < 32 { + index := uint256.Int{idx, 0, 0, 0} + top.Byte(&index) + } else { + *top = uint256.Int{} + } + + case opcode.SHL: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + s := interp.Stack + s.top-- + + shift := s.data[s.top] + top := &s.data[s.top-1] + sa, overflow := shift.Uint64WithOverflow() + if !overflow && sa < 256 { + top.Lsh(top, uint(sa)) + } else { + *top = uint256.Int{} + } + + } + case opcode.SHR: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + s := interp.Stack + s.top-- + + shift := s.data[s.top] + top := &s.data[s.top-1] + sa, overflow := shift.Uint64WithOverflow() + if !overflow && sa < 256 { + top.Rsh(top, uint(sa)) + } else { + *top = uint256.Int{} + } + + } + case opcode.SAR: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + s := interp.Stack + s.top-- + + shift := s.data[s.top] + top := &s.data[s.top-1] + sa, overflow := shift.Uint64WithOverflow() + if !overflow && sa < 256 { + top.SRsh(top, uint(sa)) + } else if top[3]&(1<<63) != 0 { + *top = uint256.Int{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)} + } else { + *top = uint256.Int{} + } + + } + case opcode.CLZ: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Osaka) { + interp.HaltNotActivated() + } else { + s := interp.Stack + + top := &s.data[s.top-1] + *top = uint256.Int{uint64(256 - top.BitLen()), 0, 0, 0} + + } + case opcode.KECCAK256: + opKeccak256(interp) + case opcode.ADDRESS: + s := interp.Stack + + s.data[s.top] = interp.Input.TargetAddress.ToU256() + s.top++ + + case opcode.BALANCE: + opBalance(interp, host) + case opcode.ORIGIN: + s := interp.Stack + + addr := host.Caller() + s.data[s.top] = addr.ToU256() + s.top++ + + case opcode.CALLER: + s := interp.Stack + + s.data[s.top] = interp.Input.CallerAddress.ToU256() + s.top++ + + case opcode.CALLVALUE: + s := interp.Stack + + s.data[s.top] = interp.Input.CallValue + s.top++ + + case opcode.CALLDATALOAD: + s := interp.Stack + + top := &s.data[s.top-1] + offset, overflow := top.Uint64WithOverflow() + if overflow { + offset = ^uint64(0) + } + input := interp.Input.Input + var word [32]byte + if offset < uint64(len(input)) { + src := input[offset:] + if len(src) >= 32 { + copy(word[:], src[:32]) + } else { + copy(word[:], src) + } + } + *top = *new(uint256.Int).SetBytes32((word)[:]) + + case opcode.CALLDATASIZE: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(len(interp.Input.Input)), 0, 0, 0} + s.top++ + + case opcode.CALLDATACOPY: + opCalldatacopy(interp) + case opcode.CODESIZE: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(bc.originalLen), 0, 0, 0} + s.top++ + + case opcode.CODECOPY: + opCodecopy(interp) + case opcode.GASPRICE: + s := interp.Stack + + s.data[s.top] = host.EffectiveGasPrice() + s.top++ + + case opcode.EXTCODESIZE: + opExtcodesize(interp, host) + case opcode.EXTCODECOPY: + opExtcodecopy(interp, host) + case opcode.RETURNDATASIZE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(len(interp.ReturnData)), 0, 0, 0} + s.top++ + + } + case opcode.RETURNDATACOPY: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + opReturndatacopy(interp) + } + case opcode.EXTCODEHASH: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + interp.HaltNotActivated() + } else { + opExtcodehash(interp, host) + } + case opcode.BLOCKHASH: + s := interp.Stack + + top := &s.data[s.top-1] + hash := host.BlockHash(*top) + *top = hash.ToU256() + + case opcode.COINBASE: + s := interp.Stack + + addr := host.Beneficiary() + s.data[s.top] = addr.ToU256() + s.top++ + + case opcode.TIMESTAMP: + s := interp.Stack + + s.data[s.top] = host.Timestamp() + s.top++ + + case opcode.NUMBER: + s := interp.Stack + + s.data[s.top] = host.BlockNumber() + s.top++ + + case opcode.DIFFICULTY: + opDifficulty(interp, host) + case opcode.GASLIMIT: + s := interp.Stack + + s.data[s.top] = host.GasLimit() + s.top++ + + case opcode.CHAINID: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { + interp.HaltNotActivated() + } else { + opChainid(interp, host) + } + case opcode.SELFBALANCE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { + interp.HaltNotActivated() + } else { + opSelfbalance(interp, host) + } + case opcode.BASEFEE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.London) { + interp.HaltNotActivated() + } else { + opBasefee(interp, host) + } + case opcode.BLOBHASH: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opBlobhash(interp, host) + } + case opcode.BLOBBASEFEE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opBlobbasefee(interp, host) + } + case opcode.SLOTNUM: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opSlotnum(interp, host) + } + case opcode.POP: + + interp.Stack.top-- + + case opcode.MLOAD: + + s := interp.Stack + if s.top == 0 { + interp.HaltUnderflow() + return + } + top := &s.data[s.top-1] + if top[1]|top[2]|top[3] == 0 { + offset := int(top[0]) + if offset >= 0 && offset+32 <= interp.Memory.Len() { + *top = interp.Memory.GetU256(offset) + } else { + if interp.ResizeMemory(offset, 32) { + *top = interp.Memory.GetU256(offset) + } + } + } else { + interp.Halt(InstructionResultInvalidOperandOOG) + } + + case opcode.MSTORE: + + s := interp.Stack + if s.top < 2 { + interp.HaltUnderflow() + return + } + s.top -= 2 + offsetVal := s.data[s.top+1] + value := s.data[s.top] + if offsetVal[1]|offsetVal[2]|offsetVal[3] == 0 { + offset := int(offsetVal[0]) + if offset >= 0 && offset+32 <= interp.Memory.Len() { + interp.Memory.SetU256(offset, value) + } else { + if interp.ResizeMemory(offset, 32) { + interp.Memory.SetU256(offset, value) + } + } + } else { + interp.Halt(InstructionResultInvalidOperandOOG) + } + + case opcode.MSTORE8: + opMstore8(interp) + case opcode.SLOAD: + opSload(interp, host) + case opcode.SSTORE: + opSstore(interp, host) + case opcode.JUMP: + + s := interp.Stack + if s.top == 0 { + interp.HaltUnderflow() + return + } + s.top-- + target := s.data[s.top] + if target[1]|target[2]|target[3] != 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + dest := int(target[0]) + if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { + interp.Halt(InstructionResultInvalidJump) + return + } + if !bc.jumpTableReady { + bc.ensureJumpTable() + } + if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + bc.pc = dest + + case opcode.JUMPI: + + s := interp.Stack + if s.top < 2 { + interp.HaltUnderflow() + return + } + s.top -= 2 + cond := s.data[s.top] + target := s.data[s.top+1] + if !cond.IsZero() { + if target[1]|target[2]|target[3] != 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + dest := int(target[0]) + if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { + interp.Halt(InstructionResultInvalidJump) + return + } + if !bc.jumpTableReady { + bc.ensureJumpTable() + } + if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { + interp.Halt(InstructionResultInvalidJump) + return + } + bc.pc = dest + } + + case opcode.PC: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(bc.pc - 1), 0, 0, 0} + s.top++ + + case opcode.MSIZE: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(interp.Memory.Len()), 0, 0, 0} + s.top++ + + case opcode.GAS: + opGas(interp) + case opcode.JUMPDEST: + case opcode.TLOAD: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opTload(interp, host) + } + case opcode.TSTORE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opTstore(interp, host) + } + case opcode.MCOPY: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + interp.HaltNotActivated() + } else { + opMcopy(interp) + } + case opcode.DUPN: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opDupN(interp) + } + case opcode.SWAPN: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opSwapN(interp) + } + case opcode.EXCHANGE: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + interp.HaltNotActivated() + } else { + opExchange(interp) + } + case opcode.CREATE: + opCreate(interp, host) + case opcode.CALL: + opCall(interp, host) + case opcode.CALLCODE: + opCallcode(interp, host) + case opcode.RETURN: + opReturn(interp) + case opcode.DELEGATECALL: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Homestead) { + interp.HaltNotActivated() + } else { + opDelegatecall(interp, host) + } + case opcode.CREATE2: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Petersburg) { + interp.HaltNotActivated() + } else { + opCreate2(interp, host) + } + case opcode.STATICCALL: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + opStaticcall(interp, host) + } + case opcode.REVERT: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + interp.HaltNotActivated() + } else { + opRevert(interp) + } + case opcode.INVALID: + + interp.Halt(InstructionResultInvalidFEOpcode) + + case opcode.SELFDESTRUCT: + opSelfdestruct(interp, host) + case opcode.PUSH0: + if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Shanghai) { + interp.HaltNotActivated() + } else { + s := interp.Stack + + s.data[s.top] = uint256.Int{} + s.top++ + + } + case opcode.PUSH1: + s := interp.Stack + + s.data[s.top] = uint256.Int{uint64(bc.code[bc.pc]), 0, 0, 0} + bc.pc++ + s.top++ + + case opcode.PUSH2: + s := interp.Stack + + v := uint64(bc.code[bc.pc])<<8 | uint64(bc.code[bc.pc+1]) + bc.pc += 2 + s.data[s.top] = uint256.Int{v, 0, 0, 0} + s.top++ + + case opcode.PUSH3: + s := interp.Stack + + c := bc.code + p := bc.pc + v := uint64(c[p])<<16 | uint64(c[p+1])<<8 | uint64(c[p+2]) + bc.pc = p + 3 + s.data[s.top] = uint256.Int{v, 0, 0, 0} + s.top++ + + case opcode.PUSH4: + s := interp.Stack + + c := bc.code + p := bc.pc + v := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) + bc.pc = p + 4 + s.data[s.top] = uint256.Int{v, 0, 0, 0} + s.top++ + + case opcode.PUSH20: + s := interp.Stack + + c := bc.code + p := bc.pc + // 20 bytes = limb2 (4 bytes) + limb1 (8 bytes) + limb0 (8 bytes) + l2 := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) + l1 := uint64(c[p+4])<<56 | uint64(c[p+5])<<48 | uint64(c[p+6])<<40 | uint64(c[p+7])<<32 | + uint64(c[p+8])<<24 | uint64(c[p+9])<<16 | uint64(c[p+10])<<8 | uint64(c[p+11]) + l0 := uint64(c[p+12])<<56 | uint64(c[p+13])<<48 | uint64(c[p+14])<<40 | uint64(c[p+15])<<32 | + uint64(c[p+16])<<24 | uint64(c[p+17])<<16 | uint64(c[p+18])<<8 | uint64(c[p+19]) + bc.pc = p + 20 + s.data[s.top] = uint256.Int{l0, l1, l2, 0} + s.top++ + + case opcode.PUSH32: + s := interp.Stack + + c := bc.code + p := bc.pc + l3 := uint64(c[p])<<56 | uint64(c[p+1])<<48 | uint64(c[p+2])<<40 | uint64(c[p+3])<<32 | + uint64(c[p+4])<<24 | uint64(c[p+5])<<16 | uint64(c[p+6])<<8 | uint64(c[p+7]) + l2 := uint64(c[p+8])<<56 | uint64(c[p+9])<<48 | uint64(c[p+10])<<40 | uint64(c[p+11])<<32 | + uint64(c[p+12])<<24 | uint64(c[p+13])<<16 | uint64(c[p+14])<<8 | uint64(c[p+15]) + l1 := uint64(c[p+16])<<56 | uint64(c[p+17])<<48 | uint64(c[p+18])<<40 | uint64(c[p+19])<<32 | + uint64(c[p+20])<<24 | uint64(c[p+21])<<16 | uint64(c[p+22])<<8 | uint64(c[p+23]) + l0 := uint64(c[p+24])<<56 | uint64(c[p+25])<<48 | uint64(c[p+26])<<40 | uint64(c[p+27])<<32 | + uint64(c[p+28])<<24 | uint64(c[p+29])<<16 | uint64(c[p+30])<<8 | uint64(c[p+31]) + bc.pc = p + 32 + s.data[s.top] = uint256.Int{l0, l1, l2, l3} + s.top++ + + case opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.PUSH17, opcode.PUSH18, opcode.PUSH19, opcode.PUSH21, opcode.PUSH22, opcode.PUSH23, opcode.PUSH24, opcode.PUSH25, opcode.PUSH26, opcode.PUSH27, opcode.PUSH28, opcode.PUSH29, opcode.PUSH30, opcode.PUSH31: + s := interp.Stack + n := int(op - opcode.PUSH0) + s.data[s.top] = *new(uint256.Int).SetBytes(bc.code[bc.pc : bc.pc+n]) + bc.pc += n + s.top++ + case opcode.DUP1: + s := interp.Stack + s.data[s.top] = s.data[s.top-1] + s.top++ + case opcode.DUP2: + s := interp.Stack + s.data[s.top] = s.data[s.top-2] + s.top++ + case opcode.DUP3: + s := interp.Stack + s.data[s.top] = s.data[s.top-3] + s.top++ + case opcode.DUP4: + s := interp.Stack + s.data[s.top] = s.data[s.top-4] + s.top++ + case opcode.DUP5: + s := interp.Stack + s.data[s.top] = s.data[s.top-5] + s.top++ + case opcode.DUP6: + s := interp.Stack + s.data[s.top] = s.data[s.top-6] + s.top++ + case opcode.DUP7: + s := interp.Stack + s.data[s.top] = s.data[s.top-7] + s.top++ + case opcode.DUP8: + s := interp.Stack + s.data[s.top] = s.data[s.top-8] + s.top++ + case opcode.DUP9: + s := interp.Stack + s.data[s.top] = s.data[s.top-9] + s.top++ + case opcode.DUP10: + s := interp.Stack + s.data[s.top] = s.data[s.top-10] + s.top++ + case opcode.DUP11: + s := interp.Stack + s.data[s.top] = s.data[s.top-11] + s.top++ + case opcode.DUP12: + s := interp.Stack + s.data[s.top] = s.data[s.top-12] + s.top++ + case opcode.DUP13: + s := interp.Stack + s.data[s.top] = s.data[s.top-13] + s.top++ + case opcode.DUP14: + s := interp.Stack + s.data[s.top] = s.data[s.top-14] + s.top++ + case opcode.DUP15: + s := interp.Stack + s.data[s.top] = s.data[s.top-15] + s.top++ + case opcode.DUP16: + s := interp.Stack + s.data[s.top] = s.data[s.top-16] + s.top++ + case opcode.SWAP1: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-1] = s.data[t-1], s.data[t] + case opcode.SWAP2: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-2] = s.data[t-2], s.data[t] + case opcode.SWAP3: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-3] = s.data[t-3], s.data[t] + case opcode.SWAP4: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-4] = s.data[t-4], s.data[t] + case opcode.SWAP5: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-5] = s.data[t-5], s.data[t] + case opcode.SWAP6: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-6] = s.data[t-6], s.data[t] + case opcode.SWAP7: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-7] = s.data[t-7], s.data[t] + case opcode.SWAP8: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-8] = s.data[t-8], s.data[t] + case opcode.SWAP9: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-9] = s.data[t-9], s.data[t] + case opcode.SWAP10: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-10] = s.data[t-10], s.data[t] + case opcode.SWAP11: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-11] = s.data[t-11], s.data[t] + case opcode.SWAP12: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-12] = s.data[t-12], s.data[t] + case opcode.SWAP13: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-13] = s.data[t-13], s.data[t] + case opcode.SWAP14: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-14] = s.data[t-14], s.data[t] + case opcode.SWAP15: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-15] = s.data[t-15], s.data[t] + case opcode.SWAP16: + s := interp.Stack + t := s.top - 1 + s.data[t], s.data[t-16] = s.data[t-16], s.data[t] + case opcode.LOG0: + logNImpl(interp, host, 0) + case opcode.LOG1: + logNImpl(interp, host, 1) + case opcode.LOG2: + logNImpl(interp, host, 2) + case opcode.LOG3: + logNImpl(interp, host, 3) + case opcode.LOG4: + logNImpl(interp, host, 4) + default: + interp.Halt(InstructionResultOpcodeNotFound) } - if sTop+int(block.StackMaxGrowth) > StackLimit { + } + return + } + blockStartPC := -1 + blockEndPC := 0 + for bc.running { + if bc.pc < blockStartPC || bc.pc >= blockEndPC { + block := bc.BasicBlockAt(bc.pc) + if block != nil { + blockStartPC = bc.pc + blockEndPC = int(block.EndPC) + } else { + blockStartPC = bc.pc + blockEndPC = bc.pc + 1 + } + if block != nil { + baseGas := block.baseGas(interp.ForkGas) + if gas.remaining < baseGas { + interp.HaltOOG() + return + } + sTop := interp.Stack.top + if sTop < int(block.StackRequired) { + gas.remaining -= baseGas + interp.HaltUnderflow() + return + } + if sTop+int(block.StackMaxGrowth) > StackLimit { + gas.remaining -= baseGas + interp.HaltOverflow() + return + } gas.remaining -= baseGas - interp.HaltOverflow() - return } - gas.remaining -= baseGas } op := bc.code[bc.pc] bc.pc++ From b3353d59ed1ccafcbb2b8f37db3e7fba9dac33e9 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 15 May 2026 21:42:10 +0200 Subject: [PATCH 4/8] Download spec fixtures for make test --- Makefile | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cf5ea2eb..767fce9e 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ EEST_FIXTURES := $(EEST_DIR)/fixtures EEST_VERSION := v5.4.0 GOLANGCI_LINT := $(shell command -v golangci-lint 2>/dev/null || printf "%s/bin/golangci-lint" "$$(go env GOPATH)") -.PHONY: all test test-unit test-spec lint download-lint eest-fixtures +.PHONY: all test test-unit test-spec lint download-lint ethereum-tests-fixtures eest-fixtures # Run all tests, including EEST fixtures. all: test @@ -29,17 +29,28 @@ download-lint: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest # Run all ethereum spec fixture tests -test-spec: eest-fixtures +test-spec: ethereum-tests-fixtures eest-fixtures GEVM_TESTS_DIR=$(FIXTURES_DIR)/GeneralStateTests \ GEVM_BLOCKCHAIN_TESTS_DIR=$(FIXTURES_DIR)/BlockchainTests \ GEVM_TRANSACTION_TESTS_DIR=$(FIXTURES_DIR)/TransactionTests \ GEVM_EEST_DIR=$(EEST_FIXTURES)/state_tests \ go test ./tests/spec/... -count=1 -timeout=30m -failfast +# Download ethereum/tests fixtures with a sparse checkout. +ethereum-tests-fixtures: + @if [ ! -d "$(FIXTURES_DIR)/GeneralStateTests" ] || [ ! -d "$(FIXTURES_DIR)/BlockchainTests" ] || [ ! -d "$(FIXTURES_DIR)/TransactionTests" ]; then \ + echo "Downloading ethereum/tests fixtures..."; \ + rm -rf "$(FIXTURES_DIR)"; \ + mkdir -p "$(dir $(FIXTURES_DIR))"; \ + git clone --depth=1 --filter=blob:none --sparse https://github.com/ethereum/tests.git "$(FIXTURES_DIR)"; \ + cd "$(FIXTURES_DIR)" && git sparse-checkout set GeneralStateTests BlockchainTests TransactionTests; \ + fi + # Download EEST fixtures from GitHub release eest-fixtures: @if [ ! -d "$(EEST_FIXTURES)/state_tests" ]; then \ echo "Downloading EEST fixtures $(EEST_VERSION)..."; \ + mkdir -p "$(EEST_DIR)"; \ curl -sL https://github.com/ethereum/execution-spec-tests/releases/download/$(EEST_VERSION)/fixtures_stable.tar.gz | \ tar xz -C $(EEST_DIR); \ echo "EEST fixtures extracted to $(EEST_FIXTURES)"; \ From f6e18a174d2d0e9fa01b86753b497bd9c0ec79c4 Mon Sep 17 00:00:00 2001 From: Giulio Rebuffo Date: Fri, 15 May 2026 21:54:54 +0200 Subject: [PATCH 5/8] Exercise spec tests through opcode runner --- Makefile | 3 ++- tests/spec/blockchain_runner.go | 8 ++++++-- tests/spec/debug_poststate_test.go | 2 +- tests/spec/outcome.go | 2 +- tests/spec/runner.go | 2 +- tests/spec/runner_mode.go | 31 ++++++++++++++++++++++++++++++ tests/spec/sstore_refund_test.go | 4 ++-- 7 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 tests/spec/runner_mode.go diff --git a/Makefile b/Makefile index 767fce9e..bcadb123 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ FIXTURES_DIR := $(CURDIR)/tests/fixtures/ethereum-tests EEST_DIR := $(CURDIR)/tests/fixtures/execution-spec-tests EEST_FIXTURES := $(EEST_DIR)/fixtures EEST_VERSION := v5.4.0 +ETHEREUM_TESTS_VERSION := v17.1 GOLANGCI_LINT := $(shell command -v golangci-lint 2>/dev/null || printf "%s/bin/golangci-lint" "$$(go env GOPATH)") .PHONY: all test test-unit test-spec lint download-lint ethereum-tests-fixtures eest-fixtures @@ -42,7 +43,7 @@ ethereum-tests-fixtures: echo "Downloading ethereum/tests fixtures..."; \ rm -rf "$(FIXTURES_DIR)"; \ mkdir -p "$(dir $(FIXTURES_DIR))"; \ - git clone --depth=1 --filter=blob:none --sparse https://github.com/ethereum/tests.git "$(FIXTURES_DIR)"; \ + git clone --depth=1 --filter=blob:none --sparse --branch "$(ETHEREUM_TESTS_VERSION)" https://github.com/ethereum/tests.git "$(FIXTURES_DIR)"; \ cd "$(FIXTURES_DIR)" && git sparse-checkout set GeneralStateTests BlockchainTests TransactionTests; \ fi diff --git a/tests/spec/blockchain_runner.go b/tests/spec/blockchain_runner.go index 6883b2f7..13d67362 100644 --- a/tests/spec/blockchain_runner.go +++ b/tests/spec/blockchain_runner.go @@ -135,7 +135,7 @@ func executeBlockchainTest(filePath, testName string, tc *BlockchainTestCase) (r cfgEnv := host.CfgEnv{ ChainId: *uint256.NewInt(1), // mainnet } - evm := host.NewEvm(db, forkID, genesisBlockEnv, cfgEnv) + evm := newTestEVM(db, forkID, genesisBlockEnv, cfgEnv) // Determine canonical chain: trace back from lastblockhash to genesis // to find which blocks belong to the canonical chain. @@ -452,7 +452,11 @@ func executeSystemCall(evm *host.Evm, target types.Address, data []byte) { rootMemory := vm.AcquireMemory() defer vm.ReleaseMemory(rootMemory) handler := host.NewHandler(hostEnv, rootMemory) - handler.Runner = vm.DefaultRunner{} + if runner := testRunner(evm.ForkID); runner != nil { + handler.Runner = runner + } else { + handler.Runner = vm.DefaultRunner{} + } // Warm the precompile addresses for _, addr := range handler.Precompiles.WarmAddresses() { diff --git a/tests/spec/debug_poststate_test.go b/tests/spec/debug_poststate_test.go index bf97fda1..98fd9913 100644 --- a/tests/spec/debug_poststate_test.go +++ b/tests/spec/debug_poststate_test.go @@ -138,7 +138,7 @@ func TestDebugClearReturnBuffer(t *testing.T) { db := BuildMemDB(unit.Pre) blockEnv := BuildBlockEnv(unit, forkID) cfgEnv := host.CfgEnv{ChainId: unit.ChainId()} - evm := host.NewEvm(db, forkID, blockEnv, cfgEnv) + evm := newTestEVM(db, forkID, blockEnv, cfgEnv) execResult := evm.Transact(&tx) if execResult.ValidationError { diff --git a/tests/spec/outcome.go b/tests/spec/outcome.go index af5d2b90..70cf0c47 100644 --- a/tests/spec/outcome.go +++ b/tests/spec/outcome.go @@ -135,7 +135,7 @@ func executeTestOutcome( cfgEnv := host.CfgEnv{ChainId: unit.ChainId()} // Execute - evm := host.NewEvm(db, forkID, blockEnv, cfgEnv) + evm := newTestEVM(db, forkID, blockEnv, cfgEnv) execResult := evm.Transact(&tx) // Capture results diff --git a/tests/spec/runner.go b/tests/spec/runner.go index d753ddef..9bb6b95f 100644 --- a/tests/spec/runner.go +++ b/tests/spec/runner.go @@ -220,7 +220,7 @@ func executeSingleTest( } // Create EVM and execute - evm := host.NewEvm(db, forkID, blockEnv, cfgEnv) + evm := newTestEVM(db, forkID, blockEnv, cfgEnv) execResult := evm.Transact(&tx) // --- Validation --- diff --git a/tests/spec/runner_mode.go b/tests/spec/runner_mode.go new file mode 100644 index 00000000..3ab9f9c0 --- /dev/null +++ b/tests/spec/runner_mode.go @@ -0,0 +1,31 @@ +package spec + +import ( + "os" + + "github.com/Giulio2002/gevm/host" + gevmspec "github.com/Giulio2002/gevm/spec" + "github.com/Giulio2002/gevm/state" + "github.com/Giulio2002/gevm/vm" +) + +func newTestEVM(db state.Database, forkID gevmspec.ForkID, block host.BlockEnv, cfg host.CfgEnv) *host.Evm { + evm := host.NewEvm(db, forkID, block, cfg) + if runner := testRunner(forkID); runner != nil { + evm.Set(runner) + } + return evm +} + +func testRunner(forkID gevmspec.ForkID) vm.Runner { + switch os.Getenv("GEVM_TEST_RUNNER") { + case "", "default": + return nil + case "opcode": + return vm.NewTracingRunner(&vm.Hooks{ + OnOpcode: func(uint64, byte, uint64, uint64, vm.OpContext, []byte, int, error) {}, + }, forkID) + default: + panic("unknown GEVM_TEST_RUNNER: " + os.Getenv("GEVM_TEST_RUNNER")) + } +} diff --git a/tests/spec/sstore_refund_test.go b/tests/spec/sstore_refund_test.go index fdf9b341..261562d5 100644 --- a/tests/spec/sstore_refund_test.go +++ b/tests/spec/sstore_refund_test.go @@ -42,7 +42,7 @@ func TestSstoreRefundBasic(t *testing.T) { } cfgEnv := host.CfgEnv{ChainId: *uint256.NewInt(1)} - evm := host.NewEvm(db, forkID, blockEnv, cfgEnv) + evm := newTestEVM(db, forkID, blockEnv, cfgEnv) tx := host.Transaction{ Kind: host.TxKindCall, TxType: host.TxTypeLegacy, @@ -112,7 +112,7 @@ func TestSstoreRefundAfterSubcall(t *testing.T) { Code: calleeCode, }, nil) - evm := host.NewEvm(db, gevmspec.Cancun, host.BlockEnv{ + evm := newTestEVM(db, gevmspec.Cancun, host.BlockEnv{ Number: *uint256.NewInt(1), GasLimit: *uint256.NewInt(1_000_000), BaseFee: *uint256.NewInt(1), From 82b2691548731062a314e9153ed4f6633530dba3 Mon Sep 17 00:00:00 2001 From: Giulio Rebuffo Date: Fri, 15 May 2026 21:58:03 +0200 Subject: [PATCH 6/8] Run fixture tests in CI --- .github/workflows/go.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index cae38ffa..65b78662 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -25,5 +25,8 @@ jobs: - name: Build run: go build -v ./... - - name: Test - run: go test -v ./... + - name: Test default runner + run: make test + + - name: Test opcode runner + run: GEVM_TEST_RUNNER=opcode make test From c4c477159f4a2cb492f188e188eb6928874afa24 Mon Sep 17 00:00:00 2001 From: Giulio Rebuffo Date: Fri, 15 May 2026 22:51:21 +0200 Subject: [PATCH 7/8] Keep default runner unchanged --- vm/bytecode.go | 128 -- vm/gen/main.go | 298 +-- vm/runner.go | 6 +- vm/runner_benchmark_test.go | 3 - vm/runner_modes_test.go | 54 +- vm/table_gen.go | 3393 +++++++++-------------------------- 6 files changed, 934 insertions(+), 2948 deletions(-) diff --git a/vm/bytecode.go b/vm/bytecode.go index d9628b81..bd57ff10 100644 --- a/vm/bytecode.go +++ b/vm/bytecode.go @@ -2,7 +2,6 @@ package vm import ( "encoding/binary" - "math" "github.com/Giulio2002/gevm/types" ) @@ -25,39 +24,10 @@ type Bytecode struct { jumpTableReady bool // Whether the jump table was set externally (must not be mutated by ensureJumpTable). jumpTableExternal bool - // Basic block metadata for the optimized interpreter. - basicBlocks []BasicBlockInfo - blockStarts []uint16 - blocksReady bool // The hash of the bytecode, lazily computed. hash *types.B256 } -// BasicBlockInfo holds precomputed static requirements for a straight-line -// bytecode block. Fork-varying gas is stored as small counters. -type BasicBlockInfo struct { - ConstGas uint64 - EndPC int32 - StackRequired int16 - StackMaxGrowth int16 - BalanceOps uint16 - ExtCodeSizeOps uint16 - ExtCodeHashOps uint16 - SloadOps uint16 - CallOps uint16 - SelfdestructOps uint16 -} - -func (b *BasicBlockInfo) baseGas(fg ForkGas) uint64 { - return b.ConstGas + - uint64(b.BalanceOps)*fg.Balance + - uint64(b.ExtCodeSizeOps)*fg.ExtCodeSize + - uint64(b.ExtCodeHashOps)*fg.ExtCodeHash + - uint64(b.SloadOps)*fg.Sload + - uint64(b.CallOps)*fg.Call + - uint64(b.SelfdestructOps)*fg.Selfdestruct -} - // bytecodeEndPadding is the number of zero bytes appended after bytecode. // Must be >= 33 to safely read PUSH32 immediates (1 opcode + 32 data bytes) // and ReadU16 (2 bytes) at any position without bounds checking. @@ -85,7 +55,6 @@ func NewBytecode(code []byte) *Bytecode { running: true, } bc.ensureJumpTable() - bc.ensureBasicBlocks() return bc } @@ -107,7 +76,6 @@ func (b *Bytecode) ResetWithHash(code []byte, hash types.B256) { b.pc = 0 b.running = true // jumpTable + jumpTableReady still valid from previous Reset - // basicBlocks + blockStarts still valid from previous Reset return } @@ -135,7 +103,6 @@ func (b *Bytecode) Reset(code []byte) { b.running = true b.hash = nil b.jumpTableReady = false // defer analysis until first IsValidJump - b.blocksReady = false } // SetJumpTable sets an externally-provided jump table, skipping analysis. @@ -158,101 +125,6 @@ func (b *Bytecode) GetJumpTable() []byte { return b.jumpTable } -func (b *Bytecode) BasicBlockAt(pc int) *BasicBlockInfo { - b.ensureBasicBlocks() - if pc < 0 || pc >= len(b.blockStarts) { - return nil - } - idx := b.blockStarts[pc] - if idx == 0 { - return nil - } - return &b.basicBlocks[idx-1] -} - -func (b *Bytecode) ensureBasicBlocks() { - if b.blocksReady { - return - } - b.blocksReady = true - if b.originalLen == 0 { - b.basicBlocks = b.basicBlocks[:0] - if cap(b.blockStarts) > 0 { - b.blockStarts = b.blockStarts[:0] - } - return - } - if cap(b.blockStarts) >= b.originalLen { - b.blockStarts = b.blockStarts[:b.originalLen] - clear(b.blockStarts) - } else { - b.blockStarts = make([]uint16, b.originalLen) - } - b.basicBlocks = b.basicBlocks[:0] - for pc := 0; pc < b.originalLen; { - start := pc - block := BasicBlockInfo{} - var stackChange int16 - for pc < b.originalLen { - op := b.code[pc] - info := blockInstructionInfo(op) - if pc != start && info.startsBlock { - break - } - block.addGas(info) - required := info.stackRequired - stackChange - if required > block.StackRequired { - block.StackRequired = required - } - stackChange += info.stackChange - if stackChange > block.StackMaxGrowth { - block.StackMaxGrowth = stackChange - } - pc += instructionLen(op) - if info.endsBlock { - break - } - } - if len(b.basicBlocks) == math.MaxUint16 { - // Extremely fragmented bytecode should still execute correctly. - // Stop indexing further blocks; the optimized runner falls back to - // per-op checks for unindexed PCs only when it cannot find metadata. - return - } - b.basicBlocks = append(b.basicBlocks, block) - b.basicBlocks[len(b.basicBlocks)-1].EndPC = int32(pc) - b.blockStarts[start] = uint16(len(b.basicBlocks)) - if pc == start { - pc++ - } - } -} - -func (b *BasicBlockInfo) addGas(info blockOpcodeInfo) { - b.ConstGas += info.constGas - switch info.forkGas { - case blockGasBalance: - b.BalanceOps++ - case blockGasExtCodeSize: - b.ExtCodeSizeOps++ - case blockGasExtCodeHash: - b.ExtCodeHashOps++ - case blockGasSload: - b.SloadOps++ - case blockGasCall: - b.CallOps++ - case blockGasSelfdestruct: - b.SelfdestructOps++ - } -} - -func instructionLen(op byte) int { - if op >= 0x60 && op <= 0x7f { - return int(op-0x5f) + 1 - } - return 1 -} - // ensureJumpTable builds the jump table if not yet built for the current code. func (b *Bytecode) ensureJumpTable() { if b.jumpTableReady { diff --git a/vm/gen/main.go b/vm/gen/main.go index 8236174b..21b3b074 100644 --- a/vm/gen/main.go +++ b/vm/gen/main.go @@ -260,10 +260,9 @@ func substituteLocals(body string) string { // --------------------------------------------------------------------------- type emitter struct { - buf *bytes.Buffer - bodies map[string]string - immediateGas bool // true → per-opcode gas deduction; false → optimized block charging - optimized bool + buf *bytes.Buffer + bodies map[string]string + tracing bool // true → per-opcode gas deduction, hooks; false → gas accumulator } func (e *emitter) p(format string, args ...interface{}) { @@ -272,12 +271,9 @@ func (e *emitter) p(format string, args ...interface{}) { // emitGas emits gas charging for a single opcode. // In accumulator mode: gasCounter += expr. -// In immediate mode: immediate check + deduction from gas.remaining. +// In tracing mode: immediate check + deduction from gas.remaining. func (e *emitter) emitGas(gasExpr string) { - if e.optimized { - return - } - if e.immediateGas { + if e.tracing { e.p("if gas.remaining < %s {\n", gasExpr) e.p("interp.HaltOOG()\n") e.p("return\n") @@ -289,9 +285,9 @@ func (e *emitter) emitGas(gasExpr string) { } // emitFlush emits the gasCounter flush check + deduction. -// No-op in immediate/optimized mode. +// No-op in tracing mode (gas is deducted per-opcode). func (e *emitter) emitFlush() { - if e.immediateGas || e.optimized { + if e.tracing { return } e.p("if gas.remaining < gasCounter {\n") @@ -303,9 +299,9 @@ func (e *emitter) emitFlush() { } // emitFlushOnError emits flush-on-error inside a stack check failure branch. -// No-op in immediate/optimized mode. +// No-op in tracing mode (gas already deducted). func (e *emitter) emitFlushOnError() { - if e.immediateGas || e.optimized { + if e.tracing { return } e.p("if gas.remaining < gasCounter {\n") @@ -410,10 +406,6 @@ func (e *emitter) emitAccumulateForkGated(op opDef) { // emitShapedBody emits the stack check + body for shaped opcodes. func (e *emitter) emitShapedBody(op opDef) { - if e.optimized { - e.emitUncheckedShapedBody(op) - return - } switch op.shape { case shapeBinaryOp: e.p("s := interp.Stack\n") @@ -469,43 +461,10 @@ func (e *emitter) emitShapedBody(op opDef) { } } -func (e *emitter) emitUncheckedShapedBody(op opDef) { - switch op.shape { - case shapeBinaryOp: - e.p("s := interp.Stack\n") - e.p("s.top--\n") - e.emitBody(op) - case shapeUnaryOp: - if op.inline && op.funcName != "" { - e.p("s := interp.Stack\n") - } - e.emitBody(op) - case shapeTernaryOp: - e.p("s := interp.Stack\n") - e.p("s.top -= 2\n") - e.emitBody(op) - case shapePushVal: - if op.inline && op.funcName != "" { - e.p("s := interp.Stack\n") - } - e.emitBody(op) - case shapePop1: - e.emitBody(op) - case shapeCustom: - e.emitBody(op) - } -} - // emitDup emits a DUP case (n=1..16). func (e *emitter) emitDup(n int) { e.p("case opcode.DUP%d:\n", n) e.emitGas("spec.GasVerylow") - if e.optimized { - e.p("s := interp.Stack\n") - e.p("s.data[s.top] = s.data[s.top-%d]\n", n) - e.p("s.top++\n") - return - } e.p("s := interp.Stack\n") e.p("if s.top < %d || s.top >= StackLimit {\n", n) e.emitFlushOnError() @@ -520,12 +479,6 @@ func (e *emitter) emitDup(n int) { func (e *emitter) emitSwap(n int) { e.p("case opcode.SWAP%d:\n", n) e.emitGas("spec.GasVerylow") - if e.optimized { - e.p("s := interp.Stack\n") - e.p("t := s.top - 1\n") - e.p("s.data[t], s.data[t-%d] = s.data[t-%d], s.data[t]\n", n, n) - return - } e.p("s := interp.Stack\n") e.p("t := s.top - 1\n") e.p("if t < %d {\n", n) @@ -562,14 +515,6 @@ func (e *emitter) emitPushGeneric() { } e.p(":\n") e.emitGas("spec.GasVerylow") - if e.optimized { - e.p("s := interp.Stack\n") - e.p("n := int(op - opcode.PUSH0)\n") - e.p("s.data[s.top] = *new(uint256.Int).SetBytes(bc.code[bc.pc : bc.pc+n])\n") - e.p("bc.pc += n\n") - e.p("s.top++\n") - return - } e.p("s := interp.Stack\n") e.p("if s.top >= StackLimit {\n") e.emitFlushOnError() @@ -592,14 +537,6 @@ func (e *emitter) emitPush(op opDef) { e.p("interp.HaltNotActivated()\n") e.p("} else {\n") } - if e.optimized { - e.p("s := interp.Stack\n") - e.emitInlineBody(op) - if op.fork != "" { - e.p("}\n") - } - return - } e.p("s := interp.Stack\n") e.p("if s.top >= StackLimit {\n") e.emitFlushOnError() @@ -646,83 +583,18 @@ func (e *emitter) emitAllCases() { // --------------------------------------------------------------------------- func (e *emitter) emitRunFunc() { - e.immediateGas = false - e.optimized = true + e.tracing = false e.p(`// Run executes bytecode until halted using direct switch dispatch. -// Static gas and stack requirements are checked once per precomputed basic -// block. Dynamic opcodes are block boundaries, preserving exact GAS/call -// semantics while avoiding per-opcode static gas and stack checks in the -// straight-line hot path. +// Static gas is accumulated in a local gasCounter variable. Instead of +// checking and deducting gas per-instruction, static gas costs are summed +// across a basic block and flushed (checked + deducted) at block boundaries +// (jumps, dynamic-gas opcodes, halting opcodes). This eliminates one branch +// + one memory write per static-gas instruction in the hot loop. func (DefaultRunner) Run(interp *Interpreter, host Host) { bc := interp.Bytecode gas := &interp.Gas -bc.ensureBasicBlocks() -if len(bc.basicBlocks) > 1 && bc.originalLen/len(bc.basicBlocks) < 8 { - PlainRunner{}.Run(interp, host) - return -} -if len(bc.basicBlocks) == 1 { - block := &bc.basicBlocks[0] - baseGas := block.baseGas(interp.ForkGas) - if gas.remaining < baseGas { - interp.HaltOOG() - return - } - sTop := interp.Stack.top - if sTop < int(block.StackRequired) { - gas.remaining -= baseGas - interp.HaltUnderflow() - return - } - if sTop+int(block.StackMaxGrowth) > StackLimit { - gas.remaining -= baseGas - interp.HaltOverflow() - return - } - gas.remaining -= baseGas - for bc.running { - op := bc.code[bc.pc] - bc.pc++ - - switch op { -`) - e.emitAllCases() - e.p(` } - } - return -} -blockStartPC := -1 -blockEndPC := 0 +var gasCounter uint64 for bc.running { -if bc.pc < blockStartPC || bc.pc >= blockEndPC { - block := bc.BasicBlockAt(bc.pc) - if block != nil { - blockStartPC = bc.pc - blockEndPC = int(block.EndPC) - } else { - blockStartPC = bc.pc - blockEndPC = bc.pc + 1 - } - if block != nil { - baseGas := block.baseGas(interp.ForkGas) - if gas.remaining < baseGas { - interp.HaltOOG() - return - } - sTop := interp.Stack.top - if sTop < int(block.StackRequired) { - gas.remaining -= baseGas - interp.HaltUnderflow() - return - } - if sTop+int(block.StackMaxGrowth) > StackLimit { - gas.remaining -= baseGas - interp.HaltOverflow() - return - } - gas.remaining -= baseGas - } -} op := bc.code[bc.pc] bc.pc++ @@ -732,29 +604,6 @@ switch op { e.p("}\n") // switch e.p("}\n") // for e.p("}\n") // func - e.optimized = false -} - -func (e *emitter) emitPlainRunFunc() { - e.immediateGas = true - e.optimized = false - e.p(`// Run executes bytecode with simple per-opcode gas and stack checks. -// It deliberately avoids the basic-block optimization and is intended for -// RPC/debug paths that prefer direct opcode-by-opcode behavior. -func (PlainRunner) Run(interp *Interpreter, host Host) { -bc := interp.Bytecode -gas := &interp.Gas -for bc.running { -op := bc.code[bc.pc] -bc.pc++ - -switch op { -`) - e.emitAllCases() - e.p("}\n") - e.p("}\n") - e.p("}\n") - e.immediateGas = false } // --------------------------------------------------------------------------- @@ -762,8 +611,7 @@ switch op { // --------------------------------------------------------------------------- func (e *emitter) emitRunWithTracingFunc() { - e.immediateGas = true - e.optimized = false + e.tracing = true e.p(`// Run executes bytecode with per-opcode tracing hooks. // Unlike DefaultRunner.Run, there is no gas accumulator — each opcode // deducts its static gas immediately, so the tracer receives accurate @@ -810,7 +658,7 @@ switch op { e.p("}\n") // for e.p("}\n") // func - e.immediateGas = false + e.tracing = false } // --------------------------------------------------------------------------- @@ -826,112 +674,6 @@ func gasToTableExpr(gas string) string { return gas } -func blockGasKind(gas string) string { - switch gas { - case "interp.ForkGas.Balance": - return "blockGasBalance" - case "interp.ForkGas.ExtCodeSize": - return "blockGasExtCodeSize" - case "interp.ForkGas.ExtCodeHash": - return "blockGasExtCodeHash" - case "interp.ForkGas.Sload": - return "blockGasSload" - case "interp.ForkGas.Call": - return "blockGasCall" - case "interp.ForkGas.Selfdestruct": - return "blockGasSelfdestruct" - default: - return "blockGasNone" - } -} - -func stackEffectForShape(s shape) (required, change int) { - switch s { - case shapeBinaryOp: - return 2, -1 - case shapeUnaryOp: - return 1, 0 - case shapeTernaryOp: - return 3, -2 - case shapePushVal: - return 0, 1 - case shapePop1: - return 1, -1 - default: - return 0, 0 - } -} - -func endsBasicBlock(op opDef) bool { - if op.mode == modeFlush && op.name != "JUMPDEST" { - return true - } - switch op.name { - case "STOP", "JUMP", "JUMPI", "RETURN", "REVERT", "INVALID", "SELFDESTRUCT": - return true - default: - return false - } -} - -func (e *emitter) emitBlockInstructionInfo() { - e.p("type blockGasKind uint8\n\n") - e.p("const (\n") - e.p("blockGasNone blockGasKind = iota\n") - e.p("blockGasBalance\n") - e.p("blockGasExtCodeSize\n") - e.p("blockGasExtCodeHash\n") - e.p("blockGasSload\n") - e.p("blockGasCall\n") - e.p("blockGasSelfdestruct\n") - e.p(")\n\n") - e.p("type blockOpcodeInfo struct {\n") - e.p("constGas uint64\n") - e.p("forkGas blockGasKind\n") - e.p("stackRequired int16\n") - e.p("stackChange int16\n") - e.p("startsBlock bool\n") - e.p("endsBlock bool\n") - e.p("}\n\n") - e.p("func blockInstructionInfo(op byte) blockOpcodeInfo {\n") - e.p("switch op {\n") - for _, op := range opcodes { - req, change := stackEffectForShape(op.shape) - starts := op.name == "JUMPDEST" || (op.mode == modeFlush && op.name != "JUMPDEST") - ends := endsBasicBlock(op) - constGas := "0" - forkGas := "blockGasNone" - if op.gas != "" { - if strings.HasPrefix(op.gas, "interp.ForkGas.") { - forkGas = blockGasKind(op.gas) - } else { - constGas = op.gas - } - } - e.p("case opcode.%s:\n", op.name) - e.p("return blockOpcodeInfo{constGas: %s, forkGas: %s, stackRequired: %d, stackChange: %d, startsBlock: %t, endsBlock: %t}\n", - constGas, forkGas, req, change, starts, ends) - } - e.p("case opcode.PUSH0:\n") - e.p("return blockOpcodeInfo{constGas: spec.GasBase, stackChange: 1}\n") - e.p("}\n") - e.p("switch {\n") - e.p("case op >= opcode.PUSH1 && op <= opcode.PUSH32:\n") - e.p("return blockOpcodeInfo{constGas: spec.GasVerylow, stackChange: 1}\n") - e.p("case op >= opcode.DUP1 && op <= opcode.DUP16:\n") - e.p("n := int16(op - opcode.DUP1 + 1)\n") - e.p("return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n, stackChange: 1}\n") - e.p("case op >= opcode.SWAP1 && op <= opcode.SWAP16:\n") - e.p("n := int16(op - opcode.SWAP1 + 2)\n") - e.p("return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n}\n") - e.p("case op >= opcode.LOG0 && op <= opcode.LOG4:\n") - e.p("return blockOpcodeInfo{startsBlock: true, endsBlock: true}\n") - e.p("default:\n") - e.p("return blockOpcodeInfo{endsBlock: true}\n") - e.p("}\n") - e.p("}\n\n") -} - func (e *emitter) emitDebugGasTable() { e.p("// buildDebugGasTable constructs a per-opcode static gas cost table.\n") e.p("// Used by the tracer to report cost in OnOpcode/OnFault hooks.\n") @@ -1009,12 +751,8 @@ func main() { var buf bytes.Buffer e := &emitter{buf: &buf, bodies: bodies} e.emitHeader() - e.emitBlockInstructionInfo() - e.p("\n") e.emitRunFunc() e.p("\n") - e.emitPlainRunFunc() - e.p("\n") e.emitRunWithTracingFunc() e.p("\n") e.emitDebugGasTable() diff --git a/vm/runner.go b/vm/runner.go index c51a9445..3b2c8687 100644 --- a/vm/runner.go +++ b/vm/runner.go @@ -10,14 +10,10 @@ type Runner interface { Run(interp *Interpreter, host Host) } -// DefaultRunner is the fast path with basic-block gas and stack checks. +// DefaultRunner is the fast-path runner with gas accumulator and zero tracing overhead. // Its Run method is generated in table_gen.go. type DefaultRunner struct{} -// PlainRunner is the simple RPC/debug runner with per-opcode gas and stack checks. -// Its Run method is generated in table_gen.go. -type PlainRunner struct{} - // TracingRunner executes with per-opcode gas deduction and tracing hooks. // Its Run method is generated in table_gen.go. // If Hooks.OnOpcode is nil, Run delegates to DefaultRunner for the fast path. diff --git a/vm/runner_benchmark_test.go b/vm/runner_benchmark_test.go index bbfedf49..1021bd90 100644 --- a/vm/runner_benchmark_test.go +++ b/vm/runner_benchmark_test.go @@ -28,9 +28,6 @@ func benchmarkRunnerModes(b *testing.B, code []byte) { b.Run("Default", func(b *testing.B) { benchmarkRunner(b, DefaultRunner{}, code) }) - b.Run("Plain", func(b *testing.B) { - benchmarkRunner(b, PlainRunner{}, code) - }) b.Run("TracingNoOpcodeHook", func(b *testing.B) { hooks := &Hooks{OnExit: func(int, []byte, uint64, error, bool) {}} benchmarkRunner(b, NewTracingRunner(hooks, spec.Prague), code) diff --git a/vm/runner_modes_test.go b/vm/runner_modes_test.go index 878cff2a..915ed975 100644 --- a/vm/runner_modes_test.go +++ b/vm/runner_modes_test.go @@ -13,27 +13,27 @@ func runWithRunner(runner Runner, code []byte, gasLimit uint64) *Interpreter { return interp } -func TestDefaultRunnerMatchesPlainRunnerForSimpleBlock(t *testing.T) { +func TestDefaultRunnerMatchesTracingRunnerForSimpleBlock(t *testing.T) { code := []byte{byte(opcode.PUSH1), 1, byte(opcode.PUSH1), 2, byte(opcode.ADD), byte(opcode.STOP)} fast := runWithRunner(DefaultRunner{}, code, 100) - plain := runWithRunner(PlainRunner{}, code, 100) + trace := runWithRunner(NewTracingRunner(opcodeHooks(), spec.Prague), code, 100) - if fast.HaltResult != plain.HaltResult { - t.Fatalf("halt mismatch: fast=%v plain=%v", fast.HaltResult, plain.HaltResult) + if fast.HaltResult != trace.HaltResult { + t.Fatalf("halt mismatch: fast=%v trace=%v", fast.HaltResult, trace.HaltResult) } - if fast.Gas.Remaining() != plain.Gas.Remaining() { - t.Fatalf("gas mismatch: fast=%d plain=%d", fast.Gas.Remaining(), plain.Gas.Remaining()) + if fast.Gas.Remaining() != trace.Gas.Remaining() { + t.Fatalf("gas mismatch: fast=%d trace=%d", fast.Gas.Remaining(), trace.Gas.Remaining()) } - if fast.StackLen() != 1 || plain.StackLen() != 1 { - t.Fatalf("stack len mismatch: fast=%d plain=%d", fast.StackLen(), plain.StackLen()) + if fast.StackLen() != 1 || trace.StackLen() != 1 { + t.Fatalf("stack len mismatch: fast=%d trace=%d", fast.StackLen(), trace.StackLen()) } - if fast.Stack.data[0].Uint64() != 3 || plain.Stack.data[0].Uint64() != 3 { - t.Fatalf("stack value mismatch: fast=%d plain=%d", fast.Stack.data[0].Uint64(), plain.Stack.data[0].Uint64()) + if fast.Stack.data[0].Uint64() != 3 || trace.Stack.data[0].Uint64() != 3 { + t.Fatalf("stack value mismatch: fast=%d trace=%d", fast.Stack.data[0].Uint64(), trace.Stack.data[0].Uint64()) } } -func TestDefaultRunnerMatchesPlainRunnerFailures(t *testing.T) { +func TestDefaultRunnerMatchesTracingRunnerFailures(t *testing.T) { for _, tc := range []struct { name string code []byte @@ -44,31 +44,31 @@ func TestDefaultRunnerMatchesPlainRunnerFailures(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { fast := runWithRunner(DefaultRunner{}, tc.code, tc.gas) - plain := runWithRunner(PlainRunner{}, tc.code, tc.gas) - if fast.HaltResult != plain.HaltResult { - t.Fatalf("halt mismatch: fast=%v plain=%v", fast.HaltResult, plain.HaltResult) + trace := runWithRunner(NewTracingRunner(opcodeHooks(), spec.Prague), tc.code, tc.gas) + if fast.HaltResult != trace.HaltResult { + t.Fatalf("halt mismatch: fast=%v trace=%v", fast.HaltResult, trace.HaltResult) } - if fast.Gas.Remaining() != plain.Gas.Remaining() { - t.Fatalf("gas mismatch: fast=%d plain=%d", fast.Gas.Remaining(), plain.Gas.Remaining()) + if fast.Gas.Remaining() != trace.Gas.Remaining() { + t.Fatalf("gas mismatch: fast=%d trace=%d", fast.Gas.Remaining(), trace.Gas.Remaining()) } }) } } -func TestDefaultRunnerGasOpcodeMatchesPlainRunner(t *testing.T) { +func TestDefaultRunnerGasOpcodeMatchesTracingRunner(t *testing.T) { code := []byte{byte(opcode.GAS), byte(opcode.STOP)} fast := runWithRunner(DefaultRunner{}, code, 10) - plain := runWithRunner(PlainRunner{}, code, 10) + trace := runWithRunner(NewTracingRunner(opcodeHooks(), spec.Prague), code, 10) - if fast.HaltResult != plain.HaltResult { - t.Fatalf("halt mismatch: fast=%v plain=%v", fast.HaltResult, plain.HaltResult) + if fast.HaltResult != trace.HaltResult { + t.Fatalf("halt mismatch: fast=%v trace=%v", fast.HaltResult, trace.HaltResult) } - if fast.StackLen() != 1 || plain.StackLen() != 1 { - t.Fatalf("stack len mismatch: fast=%d plain=%d", fast.StackLen(), plain.StackLen()) + if fast.StackLen() != 1 || trace.StackLen() != 1 { + t.Fatalf("stack len mismatch: fast=%d trace=%d", fast.StackLen(), trace.StackLen()) } - if fast.Stack.data[0] != plain.Stack.data[0] { - t.Fatalf("GAS opcode mismatch: fast=%d plain=%d", fast.Stack.data[0].Uint64(), plain.Stack.data[0].Uint64()) + if fast.Stack.data[0] != trace.Stack.data[0] { + t.Fatalf("GAS opcode mismatch: fast=%d trace=%d", fast.Stack.data[0].Uint64(), trace.Stack.data[0].Uint64()) } } @@ -89,3 +89,9 @@ func TestTracingRunnerWithoutOpcodeHookUsesDefaultPath(t *testing.T) { t.Fatalf("stack len mismatch: fast=%d tracing=%d", fast.StackLen(), tracing.StackLen()) } } + +func opcodeHooks() *Hooks { + return &Hooks{ + OnOpcode: func(uint64, byte, uint64, uint64, OpContext, []byte, int, error) {}, + } +} diff --git a/vm/table_gen.go b/vm/table_gen.go index 000c4b26..36f92e82 100644 --- a/vm/table_gen.go +++ b/vm/table_gen.go @@ -15,2034 +15,41 @@ var ( _ = opcode.STOP ) -type blockGasKind uint8 - -const ( - blockGasNone blockGasKind = iota - blockGasBalance - blockGasExtCodeSize - blockGasExtCodeHash - blockGasSload - blockGasCall - blockGasSelfdestruct -) - -type blockOpcodeInfo struct { - constGas uint64 - forkGas blockGasKind - stackRequired int16 - stackChange int16 - startsBlock bool - endsBlock bool -} - -func blockInstructionInfo(op byte) blockOpcodeInfo { - switch op { - case opcode.STOP: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.ADD: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.MUL: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SUB: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.DIV: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SDIV: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.MOD: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SMOD: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.ADDMOD: - return blockOpcodeInfo{constGas: spec.GasMid, forkGas: blockGasNone, stackRequired: 3, stackChange: -2, startsBlock: false, endsBlock: false} - case opcode.MULMOD: - return blockOpcodeInfo{constGas: spec.GasMid, forkGas: blockGasNone, stackRequired: 3, stackChange: -2, startsBlock: false, endsBlock: false} - case opcode.EXP: - return blockOpcodeInfo{constGas: spec.GasHigh, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.SIGNEXTEND: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.LT: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.GT: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SLT: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SGT: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.EQ: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.ISZERO: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} - case opcode.AND: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.OR: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.XOR: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.NOT: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} - case opcode.BYTE: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SHL: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SHR: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.SAR: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 2, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.CLZ: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} - case opcode.KECCAK256: - return blockOpcodeInfo{constGas: spec.GasKeccak256, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.ADDRESS: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.BALANCE: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasBalance, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.ORIGIN: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.CALLER: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.CALLVALUE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.CALLDATALOAD: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} - case opcode.CALLDATASIZE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.CALLDATACOPY: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.CODESIZE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.CODECOPY: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.GASPRICE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.EXTCODESIZE: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasExtCodeSize, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.EXTCODECOPY: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasExtCodeSize, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.RETURNDATASIZE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.RETURNDATACOPY: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.EXTCODEHASH: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasExtCodeHash, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.BLOCKHASH: - return blockOpcodeInfo{constGas: spec.GasBlockhash, forkGas: blockGasNone, stackRequired: 1, stackChange: 0, startsBlock: false, endsBlock: false} - case opcode.COINBASE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.TIMESTAMP: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.NUMBER: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.DIFFICULTY: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.GASLIMIT: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.CHAINID: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.SELFBALANCE: - return blockOpcodeInfo{constGas: spec.GasLow, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.BASEFEE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.BLOBHASH: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.BLOBBASEFEE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.SLOTNUM: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.POP: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 1, stackChange: -1, startsBlock: false, endsBlock: false} - case opcode.MLOAD: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.MSTORE: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.MSTORE8: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.SLOAD: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasSload, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.SSTORE: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.JUMP: - return blockOpcodeInfo{constGas: spec.GasMid, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.JUMPI: - return blockOpcodeInfo{constGas: spec.GasHigh, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.PC: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.MSIZE: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 1, startsBlock: false, endsBlock: false} - case opcode.GAS: - return blockOpcodeInfo{constGas: spec.GasBase, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.JUMPDEST: - return blockOpcodeInfo{constGas: spec.GasJumpdest, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: false} - case opcode.TLOAD: - return blockOpcodeInfo{constGas: spec.GasWarmStorageReadCost, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.TSTORE: - return blockOpcodeInfo{constGas: spec.GasWarmStorageReadCost, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.MCOPY: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.DUPN: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.SWAPN: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.EXCHANGE: - return blockOpcodeInfo{constGas: spec.GasVerylow, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.CREATE: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.CALL: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.CALLCODE: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.RETURN: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.DELEGATECALL: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.CREATE2: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.STATICCALL: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasCall, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.REVERT: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.INVALID: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasNone, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.SELFDESTRUCT: - return blockOpcodeInfo{constGas: 0, forkGas: blockGasSelfdestruct, stackRequired: 0, stackChange: 0, startsBlock: true, endsBlock: true} - case opcode.PUSH0: - return blockOpcodeInfo{constGas: spec.GasBase, stackChange: 1} - } - switch { - case op >= opcode.PUSH1 && op <= opcode.PUSH32: - return blockOpcodeInfo{constGas: spec.GasVerylow, stackChange: 1} - case op >= opcode.DUP1 && op <= opcode.DUP16: - n := int16(op - opcode.DUP1 + 1) - return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n, stackChange: 1} - case op >= opcode.SWAP1 && op <= opcode.SWAP16: - n := int16(op - opcode.SWAP1 + 2) - return blockOpcodeInfo{constGas: spec.GasVerylow, stackRequired: n} - case op >= opcode.LOG0 && op <= opcode.LOG4: - return blockOpcodeInfo{startsBlock: true, endsBlock: true} - default: - return blockOpcodeInfo{endsBlock: true} - } -} - -// Run executes bytecode until halted using direct switch dispatch. -// Static gas and stack requirements are checked once per precomputed basic -// block. Dynamic opcodes are block boundaries, preserving exact GAS/call -// semantics while avoiding per-opcode static gas and stack checks in the -// straight-line hot path. -func (DefaultRunner) Run(interp *Interpreter, host Host) { - bc := interp.Bytecode - gas := &interp.Gas - bc.ensureBasicBlocks() - if len(bc.basicBlocks) > 1 && bc.originalLen/len(bc.basicBlocks) < 8 { - PlainRunner{}.Run(interp, host) - return - } - if len(bc.basicBlocks) == 1 { - block := &bc.basicBlocks[0] - baseGas := block.baseGas(interp.ForkGas) - if gas.remaining < baseGas { - interp.HaltOOG() - return - } - sTop := interp.Stack.top - if sTop < int(block.StackRequired) { - gas.remaining -= baseGas - interp.HaltUnderflow() - return - } - if sTop+int(block.StackMaxGrowth) > StackLimit { - gas.remaining -= baseGas - interp.HaltOverflow() - return - } - gas.remaining -= baseGas - for bc.running { - op := bc.code[bc.pc] - bc.pc++ - - switch op { - case opcode.STOP: - - interp.Halt(InstructionResultStop) - - case opcode.ADD: - s := interp.Stack - s.top-- - - s.data[s.top-1].Add(&s.data[s.top], &s.data[s.top-1]) - - case opcode.MUL: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - top.Mul(&a, top) - - case opcode.SUB: - s := interp.Stack - s.top-- - - s.data[s.top-1].Sub(&s.data[s.top], &s.data[s.top-1]) - - case opcode.DIV: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - if !top.IsZero() { - top.Div(&a, top) - } - - case opcode.SDIV: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - top.SDiv(&a, top) - - case opcode.MOD: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - if !top.IsZero() { - top.Mod(&a, top) - } - - case opcode.SMOD: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - top.SMod(&a, top) - - case opcode.ADDMOD: - s := interp.Stack - s.top -= 2 - - a := s.data[s.top+1] - b := s.data[s.top] - top := &s.data[s.top-1] - top.AddMod(&a, &b, top) - - case opcode.MULMOD: - s := interp.Stack - s.top -= 2 - - a := s.data[s.top+1] - b := s.data[s.top] - top := &s.data[s.top-1] - top.MulMod(&a, &b, top) - - case opcode.EXP: - opExp(interp) - case opcode.SIGNEXTEND: - s := interp.Stack - s.top-- - - ext := s.data[s.top] - top := &s.data[s.top-1] - top.ExtendSign(top, &ext) - - case opcode.LT: - s := interp.Stack - s.top-- - - if s.data[s.top].Lt(&s.data[s.top-1]) { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.GT: - s := interp.Stack - s.top-- - - if s.data[s.top].Gt(&s.data[s.top-1]) { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.SLT: - s := interp.Stack - s.top-- - - a := &s.data[s.top] - b := &s.data[s.top-1] - aNeg := a[3] >> 63 - bNeg := b[3] >> 63 - var lt bool - if aNeg != bNeg { - lt = aNeg > bNeg - } else { - lt = a.Lt(b) - } - if lt { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.SGT: - s := interp.Stack - s.top-- - - a := &s.data[s.top] - b := &s.data[s.top-1] - aNeg := a[3] >> 63 - bNeg := b[3] >> 63 - var gt bool - if aNeg != bNeg { - gt = bNeg > aNeg - } else { - gt = a.Gt(b) - } - if gt { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.EQ: - s := interp.Stack - s.top-- - - if s.data[s.top].Eq(&s.data[s.top-1]) { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.ISZERO: - s := interp.Stack - - if s.data[s.top-1].IsZero() { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.AND: - s := interp.Stack - s.top-- - - s.data[s.top-1].And(&s.data[s.top], &s.data[s.top-1]) - - case opcode.OR: - s := interp.Stack - s.top-- - - s.data[s.top-1].Or(&s.data[s.top], &s.data[s.top-1]) - - case opcode.XOR: - s := interp.Stack - s.top-- - - s.data[s.top-1].Xor(&s.data[s.top], &s.data[s.top-1]) - - case opcode.NOT: - s := interp.Stack - - s.data[s.top-1].Not(&s.data[s.top-1]) - - case opcode.BYTE: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - idx, overflow := a.Uint64WithOverflow() - if !overflow && idx < 32 { - index := uint256.Int{idx, 0, 0, 0} - top.Byte(&index) - } else { - *top = uint256.Int{} - } - - case opcode.SHL: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - s := interp.Stack - s.top-- - - shift := s.data[s.top] - top := &s.data[s.top-1] - sa, overflow := shift.Uint64WithOverflow() - if !overflow && sa < 256 { - top.Lsh(top, uint(sa)) - } else { - *top = uint256.Int{} - } - - } - case opcode.SHR: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - s := interp.Stack - s.top-- - - shift := s.data[s.top] - top := &s.data[s.top-1] - sa, overflow := shift.Uint64WithOverflow() - if !overflow && sa < 256 { - top.Rsh(top, uint(sa)) - } else { - *top = uint256.Int{} - } - - } - case opcode.SAR: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - s := interp.Stack - s.top-- - - shift := s.data[s.top] - top := &s.data[s.top-1] - sa, overflow := shift.Uint64WithOverflow() - if !overflow && sa < 256 { - top.SRsh(top, uint(sa)) - } else if top[3]&(1<<63) != 0 { - *top = uint256.Int{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)} - } else { - *top = uint256.Int{} - } - - } - case opcode.CLZ: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Osaka) { - interp.HaltNotActivated() - } else { - s := interp.Stack - - top := &s.data[s.top-1] - *top = uint256.Int{uint64(256 - top.BitLen()), 0, 0, 0} - - } - case opcode.KECCAK256: - opKeccak256(interp) - case opcode.ADDRESS: - s := interp.Stack - - s.data[s.top] = interp.Input.TargetAddress.ToU256() - s.top++ - - case opcode.BALANCE: - opBalance(interp, host) - case opcode.ORIGIN: - s := interp.Stack - - addr := host.Caller() - s.data[s.top] = addr.ToU256() - s.top++ - - case opcode.CALLER: - s := interp.Stack - - s.data[s.top] = interp.Input.CallerAddress.ToU256() - s.top++ - - case opcode.CALLVALUE: - s := interp.Stack - - s.data[s.top] = interp.Input.CallValue - s.top++ - - case opcode.CALLDATALOAD: - s := interp.Stack - - top := &s.data[s.top-1] - offset, overflow := top.Uint64WithOverflow() - if overflow { - offset = ^uint64(0) - } - input := interp.Input.Input - var word [32]byte - if offset < uint64(len(input)) { - src := input[offset:] - if len(src) >= 32 { - copy(word[:], src[:32]) - } else { - copy(word[:], src) - } - } - *top = *new(uint256.Int).SetBytes32((word)[:]) - - case opcode.CALLDATASIZE: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(len(interp.Input.Input)), 0, 0, 0} - s.top++ - - case opcode.CALLDATACOPY: - opCalldatacopy(interp) - case opcode.CODESIZE: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(bc.originalLen), 0, 0, 0} - s.top++ - - case opcode.CODECOPY: - opCodecopy(interp) - case opcode.GASPRICE: - s := interp.Stack - - s.data[s.top] = host.EffectiveGasPrice() - s.top++ - - case opcode.EXTCODESIZE: - opExtcodesize(interp, host) - case opcode.EXTCODECOPY: - opExtcodecopy(interp, host) - case opcode.RETURNDATASIZE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(len(interp.ReturnData)), 0, 0, 0} - s.top++ - - } - case opcode.RETURNDATACOPY: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - opReturndatacopy(interp) - } - case opcode.EXTCODEHASH: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - opExtcodehash(interp, host) - } - case opcode.BLOCKHASH: - s := interp.Stack - - top := &s.data[s.top-1] - hash := host.BlockHash(*top) - *top = hash.ToU256() - - case opcode.COINBASE: - s := interp.Stack - - addr := host.Beneficiary() - s.data[s.top] = addr.ToU256() - s.top++ - - case opcode.TIMESTAMP: - s := interp.Stack - - s.data[s.top] = host.Timestamp() - s.top++ - - case opcode.NUMBER: - s := interp.Stack - - s.data[s.top] = host.BlockNumber() - s.top++ - - case opcode.DIFFICULTY: - opDifficulty(interp, host) - case opcode.GASLIMIT: - s := interp.Stack - - s.data[s.top] = host.GasLimit() - s.top++ - - case opcode.CHAINID: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { - interp.HaltNotActivated() - } else { - opChainid(interp, host) - } - case opcode.SELFBALANCE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { - interp.HaltNotActivated() - } else { - opSelfbalance(interp, host) - } - case opcode.BASEFEE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.London) { - interp.HaltNotActivated() - } else { - opBasefee(interp, host) - } - case opcode.BLOBHASH: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opBlobhash(interp, host) - } - case opcode.BLOBBASEFEE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opBlobbasefee(interp, host) - } - case opcode.SLOTNUM: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opSlotnum(interp, host) - } - case opcode.POP: - - interp.Stack.top-- - - case opcode.MLOAD: - - s := interp.Stack - if s.top == 0 { - interp.HaltUnderflow() - return - } - top := &s.data[s.top-1] - if top[1]|top[2]|top[3] == 0 { - offset := int(top[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - *top = interp.Memory.GetU256(offset) - } else { - if interp.ResizeMemory(offset, 32) { - *top = interp.Memory.GetU256(offset) - } - } - } else { - interp.Halt(InstructionResultInvalidOperandOOG) - } - - case opcode.MSTORE: - - s := interp.Stack - if s.top < 2 { - interp.HaltUnderflow() - return - } - s.top -= 2 - offsetVal := s.data[s.top+1] - value := s.data[s.top] - if offsetVal[1]|offsetVal[2]|offsetVal[3] == 0 { - offset := int(offsetVal[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - interp.Memory.SetU256(offset, value) - } else { - if interp.ResizeMemory(offset, 32) { - interp.Memory.SetU256(offset, value) - } - } - } else { - interp.Halt(InstructionResultInvalidOperandOOG) - } - - case opcode.MSTORE8: - opMstore8(interp) - case opcode.SLOAD: - opSload(interp, host) - case opcode.SSTORE: - opSstore(interp, host) - case opcode.JUMP: - - s := interp.Stack - if s.top == 0 { - interp.HaltUnderflow() - return - } - s.top-- - target := s.data[s.top] - if target[1]|target[2]|target[3] != 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - dest := int(target[0]) - if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { - interp.Halt(InstructionResultInvalidJump) - return - } - if !bc.jumpTableReady { - bc.ensureJumpTable() - } - if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - bc.pc = dest - - case opcode.JUMPI: - - s := interp.Stack - if s.top < 2 { - interp.HaltUnderflow() - return - } - s.top -= 2 - cond := s.data[s.top] - target := s.data[s.top+1] - if !cond.IsZero() { - if target[1]|target[2]|target[3] != 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - dest := int(target[0]) - if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { - interp.Halt(InstructionResultInvalidJump) - return - } - if !bc.jumpTableReady { - bc.ensureJumpTable() - } - if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - bc.pc = dest - } - - case opcode.PC: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(bc.pc - 1), 0, 0, 0} - s.top++ - - case opcode.MSIZE: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(interp.Memory.Len()), 0, 0, 0} - s.top++ - - case opcode.GAS: - opGas(interp) - case opcode.JUMPDEST: - case opcode.TLOAD: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opTload(interp, host) - } - case opcode.TSTORE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opTstore(interp, host) - } - case opcode.MCOPY: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opMcopy(interp) - } - case opcode.DUPN: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opDupN(interp) - } - case opcode.SWAPN: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opSwapN(interp) - } - case opcode.EXCHANGE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opExchange(interp) - } - case opcode.CREATE: - opCreate(interp, host) - case opcode.CALL: - opCall(interp, host) - case opcode.CALLCODE: - opCallcode(interp, host) - case opcode.RETURN: - opReturn(interp) - case opcode.DELEGATECALL: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Homestead) { - interp.HaltNotActivated() - } else { - opDelegatecall(interp, host) - } - case opcode.CREATE2: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Petersburg) { - interp.HaltNotActivated() - } else { - opCreate2(interp, host) - } - case opcode.STATICCALL: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - opStaticcall(interp, host) - } - case opcode.REVERT: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - opRevert(interp) - } - case opcode.INVALID: - - interp.Halt(InstructionResultInvalidFEOpcode) - - case opcode.SELFDESTRUCT: - opSelfdestruct(interp, host) - case opcode.PUSH0: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Shanghai) { - interp.HaltNotActivated() - } else { - s := interp.Stack - - s.data[s.top] = uint256.Int{} - s.top++ - - } - case opcode.PUSH1: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(bc.code[bc.pc]), 0, 0, 0} - bc.pc++ - s.top++ - - case opcode.PUSH2: - s := interp.Stack - - v := uint64(bc.code[bc.pc])<<8 | uint64(bc.code[bc.pc+1]) - bc.pc += 2 - s.data[s.top] = uint256.Int{v, 0, 0, 0} - s.top++ - - case opcode.PUSH3: - s := interp.Stack - - c := bc.code - p := bc.pc - v := uint64(c[p])<<16 | uint64(c[p+1])<<8 | uint64(c[p+2]) - bc.pc = p + 3 - s.data[s.top] = uint256.Int{v, 0, 0, 0} - s.top++ - - case opcode.PUSH4: - s := interp.Stack - - c := bc.code - p := bc.pc - v := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) - bc.pc = p + 4 - s.data[s.top] = uint256.Int{v, 0, 0, 0} - s.top++ - - case opcode.PUSH20: - s := interp.Stack - - c := bc.code - p := bc.pc - // 20 bytes = limb2 (4 bytes) + limb1 (8 bytes) + limb0 (8 bytes) - l2 := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) - l1 := uint64(c[p+4])<<56 | uint64(c[p+5])<<48 | uint64(c[p+6])<<40 | uint64(c[p+7])<<32 | - uint64(c[p+8])<<24 | uint64(c[p+9])<<16 | uint64(c[p+10])<<8 | uint64(c[p+11]) - l0 := uint64(c[p+12])<<56 | uint64(c[p+13])<<48 | uint64(c[p+14])<<40 | uint64(c[p+15])<<32 | - uint64(c[p+16])<<24 | uint64(c[p+17])<<16 | uint64(c[p+18])<<8 | uint64(c[p+19]) - bc.pc = p + 20 - s.data[s.top] = uint256.Int{l0, l1, l2, 0} - s.top++ - - case opcode.PUSH32: - s := interp.Stack - - c := bc.code - p := bc.pc - l3 := uint64(c[p])<<56 | uint64(c[p+1])<<48 | uint64(c[p+2])<<40 | uint64(c[p+3])<<32 | - uint64(c[p+4])<<24 | uint64(c[p+5])<<16 | uint64(c[p+6])<<8 | uint64(c[p+7]) - l2 := uint64(c[p+8])<<56 | uint64(c[p+9])<<48 | uint64(c[p+10])<<40 | uint64(c[p+11])<<32 | - uint64(c[p+12])<<24 | uint64(c[p+13])<<16 | uint64(c[p+14])<<8 | uint64(c[p+15]) - l1 := uint64(c[p+16])<<56 | uint64(c[p+17])<<48 | uint64(c[p+18])<<40 | uint64(c[p+19])<<32 | - uint64(c[p+20])<<24 | uint64(c[p+21])<<16 | uint64(c[p+22])<<8 | uint64(c[p+23]) - l0 := uint64(c[p+24])<<56 | uint64(c[p+25])<<48 | uint64(c[p+26])<<40 | uint64(c[p+27])<<32 | - uint64(c[p+28])<<24 | uint64(c[p+29])<<16 | uint64(c[p+30])<<8 | uint64(c[p+31]) - bc.pc = p + 32 - s.data[s.top] = uint256.Int{l0, l1, l2, l3} - s.top++ - - case opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.PUSH17, opcode.PUSH18, opcode.PUSH19, opcode.PUSH21, opcode.PUSH22, opcode.PUSH23, opcode.PUSH24, opcode.PUSH25, opcode.PUSH26, opcode.PUSH27, opcode.PUSH28, opcode.PUSH29, opcode.PUSH30, opcode.PUSH31: - s := interp.Stack - n := int(op - opcode.PUSH0) - s.data[s.top] = *new(uint256.Int).SetBytes(bc.code[bc.pc : bc.pc+n]) - bc.pc += n - s.top++ - case opcode.DUP1: - s := interp.Stack - s.data[s.top] = s.data[s.top-1] - s.top++ - case opcode.DUP2: - s := interp.Stack - s.data[s.top] = s.data[s.top-2] - s.top++ - case opcode.DUP3: - s := interp.Stack - s.data[s.top] = s.data[s.top-3] - s.top++ - case opcode.DUP4: - s := interp.Stack - s.data[s.top] = s.data[s.top-4] - s.top++ - case opcode.DUP5: - s := interp.Stack - s.data[s.top] = s.data[s.top-5] - s.top++ - case opcode.DUP6: - s := interp.Stack - s.data[s.top] = s.data[s.top-6] - s.top++ - case opcode.DUP7: - s := interp.Stack - s.data[s.top] = s.data[s.top-7] - s.top++ - case opcode.DUP8: - s := interp.Stack - s.data[s.top] = s.data[s.top-8] - s.top++ - case opcode.DUP9: - s := interp.Stack - s.data[s.top] = s.data[s.top-9] - s.top++ - case opcode.DUP10: - s := interp.Stack - s.data[s.top] = s.data[s.top-10] - s.top++ - case opcode.DUP11: - s := interp.Stack - s.data[s.top] = s.data[s.top-11] - s.top++ - case opcode.DUP12: - s := interp.Stack - s.data[s.top] = s.data[s.top-12] - s.top++ - case opcode.DUP13: - s := interp.Stack - s.data[s.top] = s.data[s.top-13] - s.top++ - case opcode.DUP14: - s := interp.Stack - s.data[s.top] = s.data[s.top-14] - s.top++ - case opcode.DUP15: - s := interp.Stack - s.data[s.top] = s.data[s.top-15] - s.top++ - case opcode.DUP16: - s := interp.Stack - s.data[s.top] = s.data[s.top-16] - s.top++ - case opcode.SWAP1: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-1] = s.data[t-1], s.data[t] - case opcode.SWAP2: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-2] = s.data[t-2], s.data[t] - case opcode.SWAP3: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-3] = s.data[t-3], s.data[t] - case opcode.SWAP4: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-4] = s.data[t-4], s.data[t] - case opcode.SWAP5: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-5] = s.data[t-5], s.data[t] - case opcode.SWAP6: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-6] = s.data[t-6], s.data[t] - case opcode.SWAP7: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-7] = s.data[t-7], s.data[t] - case opcode.SWAP8: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-8] = s.data[t-8], s.data[t] - case opcode.SWAP9: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-9] = s.data[t-9], s.data[t] - case opcode.SWAP10: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-10] = s.data[t-10], s.data[t] - case opcode.SWAP11: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-11] = s.data[t-11], s.data[t] - case opcode.SWAP12: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-12] = s.data[t-12], s.data[t] - case opcode.SWAP13: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-13] = s.data[t-13], s.data[t] - case opcode.SWAP14: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-14] = s.data[t-14], s.data[t] - case opcode.SWAP15: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-15] = s.data[t-15], s.data[t] - case opcode.SWAP16: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-16] = s.data[t-16], s.data[t] - case opcode.LOG0: - logNImpl(interp, host, 0) - case opcode.LOG1: - logNImpl(interp, host, 1) - case opcode.LOG2: - logNImpl(interp, host, 2) - case opcode.LOG3: - logNImpl(interp, host, 3) - case opcode.LOG4: - logNImpl(interp, host, 4) - default: - interp.Halt(InstructionResultOpcodeNotFound) - } - } - return - } - blockStartPC := -1 - blockEndPC := 0 - for bc.running { - if bc.pc < blockStartPC || bc.pc >= blockEndPC { - block := bc.BasicBlockAt(bc.pc) - if block != nil { - blockStartPC = bc.pc - blockEndPC = int(block.EndPC) - } else { - blockStartPC = bc.pc - blockEndPC = bc.pc + 1 - } - if block != nil { - baseGas := block.baseGas(interp.ForkGas) - if gas.remaining < baseGas { - interp.HaltOOG() - return - } - sTop := interp.Stack.top - if sTop < int(block.StackRequired) { - gas.remaining -= baseGas - interp.HaltUnderflow() - return - } - if sTop+int(block.StackMaxGrowth) > StackLimit { - gas.remaining -= baseGas - interp.HaltOverflow() - return - } - gas.remaining -= baseGas - } - } - op := bc.code[bc.pc] - bc.pc++ - - switch op { - case opcode.STOP: - - interp.Halt(InstructionResultStop) - - case opcode.ADD: - s := interp.Stack - s.top-- - - s.data[s.top-1].Add(&s.data[s.top], &s.data[s.top-1]) - - case opcode.MUL: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - top.Mul(&a, top) - - case opcode.SUB: - s := interp.Stack - s.top-- - - s.data[s.top-1].Sub(&s.data[s.top], &s.data[s.top-1]) - - case opcode.DIV: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - if !top.IsZero() { - top.Div(&a, top) - } - - case opcode.SDIV: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - top.SDiv(&a, top) - - case opcode.MOD: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - if !top.IsZero() { - top.Mod(&a, top) - } - - case opcode.SMOD: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - top.SMod(&a, top) - - case opcode.ADDMOD: - s := interp.Stack - s.top -= 2 - - a := s.data[s.top+1] - b := s.data[s.top] - top := &s.data[s.top-1] - top.AddMod(&a, &b, top) - - case opcode.MULMOD: - s := interp.Stack - s.top -= 2 - - a := s.data[s.top+1] - b := s.data[s.top] - top := &s.data[s.top-1] - top.MulMod(&a, &b, top) - - case opcode.EXP: - opExp(interp) - case opcode.SIGNEXTEND: - s := interp.Stack - s.top-- - - ext := s.data[s.top] - top := &s.data[s.top-1] - top.ExtendSign(top, &ext) - - case opcode.LT: - s := interp.Stack - s.top-- - - if s.data[s.top].Lt(&s.data[s.top-1]) { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.GT: - s := interp.Stack - s.top-- - - if s.data[s.top].Gt(&s.data[s.top-1]) { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.SLT: - s := interp.Stack - s.top-- - - a := &s.data[s.top] - b := &s.data[s.top-1] - aNeg := a[3] >> 63 - bNeg := b[3] >> 63 - var lt bool - if aNeg != bNeg { - lt = aNeg > bNeg - } else { - lt = a.Lt(b) - } - if lt { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.SGT: - s := interp.Stack - s.top-- - - a := &s.data[s.top] - b := &s.data[s.top-1] - aNeg := a[3] >> 63 - bNeg := b[3] >> 63 - var gt bool - if aNeg != bNeg { - gt = bNeg > aNeg - } else { - gt = a.Gt(b) - } - if gt { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.EQ: - s := interp.Stack - s.top-- - - if s.data[s.top].Eq(&s.data[s.top-1]) { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.ISZERO: - s := interp.Stack - - if s.data[s.top-1].IsZero() { - s.data[s.top-1] = uint256.Int{1, 0, 0, 0} - } else { - s.data[s.top-1] = uint256.Int{} - } - - case opcode.AND: - s := interp.Stack - s.top-- - - s.data[s.top-1].And(&s.data[s.top], &s.data[s.top-1]) - - case opcode.OR: - s := interp.Stack - s.top-- - - s.data[s.top-1].Or(&s.data[s.top], &s.data[s.top-1]) - - case opcode.XOR: - s := interp.Stack - s.top-- - - s.data[s.top-1].Xor(&s.data[s.top], &s.data[s.top-1]) - - case opcode.NOT: - s := interp.Stack - - s.data[s.top-1].Not(&s.data[s.top-1]) - - case opcode.BYTE: - s := interp.Stack - s.top-- - - a := s.data[s.top] - top := &s.data[s.top-1] - idx, overflow := a.Uint64WithOverflow() - if !overflow && idx < 32 { - index := uint256.Int{idx, 0, 0, 0} - top.Byte(&index) - } else { - *top = uint256.Int{} - } - - case opcode.SHL: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - s := interp.Stack - s.top-- - - shift := s.data[s.top] - top := &s.data[s.top-1] - sa, overflow := shift.Uint64WithOverflow() - if !overflow && sa < 256 { - top.Lsh(top, uint(sa)) - } else { - *top = uint256.Int{} - } - - } - case opcode.SHR: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - s := interp.Stack - s.top-- - - shift := s.data[s.top] - top := &s.data[s.top-1] - sa, overflow := shift.Uint64WithOverflow() - if !overflow && sa < 256 { - top.Rsh(top, uint(sa)) - } else { - *top = uint256.Int{} - } - - } - case opcode.SAR: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - s := interp.Stack - s.top-- - - shift := s.data[s.top] - top := &s.data[s.top-1] - sa, overflow := shift.Uint64WithOverflow() - if !overflow && sa < 256 { - top.SRsh(top, uint(sa)) - } else if top[3]&(1<<63) != 0 { - *top = uint256.Int{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)} - } else { - *top = uint256.Int{} - } - - } - case opcode.CLZ: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Osaka) { - interp.HaltNotActivated() - } else { - s := interp.Stack - - top := &s.data[s.top-1] - *top = uint256.Int{uint64(256 - top.BitLen()), 0, 0, 0} - - } - case opcode.KECCAK256: - opKeccak256(interp) - case opcode.ADDRESS: - s := interp.Stack - - s.data[s.top] = interp.Input.TargetAddress.ToU256() - s.top++ - - case opcode.BALANCE: - opBalance(interp, host) - case opcode.ORIGIN: - s := interp.Stack - - addr := host.Caller() - s.data[s.top] = addr.ToU256() - s.top++ - - case opcode.CALLER: - s := interp.Stack - - s.data[s.top] = interp.Input.CallerAddress.ToU256() - s.top++ - - case opcode.CALLVALUE: - s := interp.Stack - - s.data[s.top] = interp.Input.CallValue - s.top++ - - case opcode.CALLDATALOAD: - s := interp.Stack - - top := &s.data[s.top-1] - offset, overflow := top.Uint64WithOverflow() - if overflow { - offset = ^uint64(0) - } - input := interp.Input.Input - var word [32]byte - if offset < uint64(len(input)) { - src := input[offset:] - if len(src) >= 32 { - copy(word[:], src[:32]) - } else { - copy(word[:], src) - } - } - *top = *new(uint256.Int).SetBytes32((word)[:]) - - case opcode.CALLDATASIZE: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(len(interp.Input.Input)), 0, 0, 0} - s.top++ - - case opcode.CALLDATACOPY: - opCalldatacopy(interp) - case opcode.CODESIZE: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(bc.originalLen), 0, 0, 0} - s.top++ - - case opcode.CODECOPY: - opCodecopy(interp) - case opcode.GASPRICE: - s := interp.Stack - - s.data[s.top] = host.EffectiveGasPrice() - s.top++ - - case opcode.EXTCODESIZE: - opExtcodesize(interp, host) - case opcode.EXTCODECOPY: - opExtcodecopy(interp, host) - case opcode.RETURNDATASIZE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(len(interp.ReturnData)), 0, 0, 0} - s.top++ - - } - case opcode.RETURNDATACOPY: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - opReturndatacopy(interp) - } - case opcode.EXTCODEHASH: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { - interp.HaltNotActivated() - } else { - opExtcodehash(interp, host) - } - case opcode.BLOCKHASH: - s := interp.Stack - - top := &s.data[s.top-1] - hash := host.BlockHash(*top) - *top = hash.ToU256() - - case opcode.COINBASE: - s := interp.Stack - - addr := host.Beneficiary() - s.data[s.top] = addr.ToU256() - s.top++ - - case opcode.TIMESTAMP: - s := interp.Stack - - s.data[s.top] = host.Timestamp() - s.top++ - - case opcode.NUMBER: - s := interp.Stack - - s.data[s.top] = host.BlockNumber() - s.top++ - - case opcode.DIFFICULTY: - opDifficulty(interp, host) - case opcode.GASLIMIT: - s := interp.Stack - - s.data[s.top] = host.GasLimit() - s.top++ - - case opcode.CHAINID: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { - interp.HaltNotActivated() - } else { - opChainid(interp, host) - } - case opcode.SELFBALANCE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { - interp.HaltNotActivated() - } else { - opSelfbalance(interp, host) - } - case opcode.BASEFEE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.London) { - interp.HaltNotActivated() - } else { - opBasefee(interp, host) - } - case opcode.BLOBHASH: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opBlobhash(interp, host) - } - case opcode.BLOBBASEFEE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opBlobbasefee(interp, host) - } - case opcode.SLOTNUM: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opSlotnum(interp, host) - } - case opcode.POP: - - interp.Stack.top-- - - case opcode.MLOAD: - - s := interp.Stack - if s.top == 0 { - interp.HaltUnderflow() - return - } - top := &s.data[s.top-1] - if top[1]|top[2]|top[3] == 0 { - offset := int(top[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - *top = interp.Memory.GetU256(offset) - } else { - if interp.ResizeMemory(offset, 32) { - *top = interp.Memory.GetU256(offset) - } - } - } else { - interp.Halt(InstructionResultInvalidOperandOOG) - } - - case opcode.MSTORE: - - s := interp.Stack - if s.top < 2 { - interp.HaltUnderflow() - return - } - s.top -= 2 - offsetVal := s.data[s.top+1] - value := s.data[s.top] - if offsetVal[1]|offsetVal[2]|offsetVal[3] == 0 { - offset := int(offsetVal[0]) - if offset >= 0 && offset+32 <= interp.Memory.Len() { - interp.Memory.SetU256(offset, value) - } else { - if interp.ResizeMemory(offset, 32) { - interp.Memory.SetU256(offset, value) - } - } - } else { - interp.Halt(InstructionResultInvalidOperandOOG) - } - - case opcode.MSTORE8: - opMstore8(interp) - case opcode.SLOAD: - opSload(interp, host) - case opcode.SSTORE: - opSstore(interp, host) - case opcode.JUMP: - - s := interp.Stack - if s.top == 0 { - interp.HaltUnderflow() - return - } - s.top-- - target := s.data[s.top] - if target[1]|target[2]|target[3] != 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - dest := int(target[0]) - if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { - interp.Halt(InstructionResultInvalidJump) - return - } - if !bc.jumpTableReady { - bc.ensureJumpTable() - } - if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - bc.pc = dest - - case opcode.JUMPI: - - s := interp.Stack - if s.top < 2 { - interp.HaltUnderflow() - return - } - s.top -= 2 - cond := s.data[s.top] - target := s.data[s.top+1] - if !cond.IsZero() { - if target[1]|target[2]|target[3] != 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - dest := int(target[0]) - if dest >= bc.originalLen || bc.code[dest] != opcode.JUMPDEST { - interp.Halt(InstructionResultInvalidJump) - return - } - if !bc.jumpTableReady { - bc.ensureJumpTable() - } - if bc.jumpTable[dest/8]&(1<<(uint(dest)%8)) == 0 { - interp.Halt(InstructionResultInvalidJump) - return - } - bc.pc = dest - } - - case opcode.PC: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(bc.pc - 1), 0, 0, 0} - s.top++ - - case opcode.MSIZE: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(interp.Memory.Len()), 0, 0, 0} - s.top++ - - case opcode.GAS: - opGas(interp) - case opcode.JUMPDEST: - case opcode.TLOAD: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opTload(interp, host) - } - case opcode.TSTORE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opTstore(interp, host) - } - case opcode.MCOPY: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { - interp.HaltNotActivated() - } else { - opMcopy(interp) - } - case opcode.DUPN: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opDupN(interp) - } - case opcode.SWAPN: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opSwapN(interp) - } - case opcode.EXCHANGE: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { - interp.HaltNotActivated() - } else { - opExchange(interp) - } - case opcode.CREATE: - opCreate(interp, host) - case opcode.CALL: - opCall(interp, host) - case opcode.CALLCODE: - opCallcode(interp, host) - case opcode.RETURN: - opReturn(interp) - case opcode.DELEGATECALL: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Homestead) { - interp.HaltNotActivated() - } else { - opDelegatecall(interp, host) - } - case opcode.CREATE2: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Petersburg) { - interp.HaltNotActivated() - } else { - opCreate2(interp, host) - } - case opcode.STATICCALL: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - opStaticcall(interp, host) - } - case opcode.REVERT: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { - interp.HaltNotActivated() - } else { - opRevert(interp) - } - case opcode.INVALID: - - interp.Halt(InstructionResultInvalidFEOpcode) - - case opcode.SELFDESTRUCT: - opSelfdestruct(interp, host) - case opcode.PUSH0: - if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Shanghai) { - interp.HaltNotActivated() - } else { - s := interp.Stack - - s.data[s.top] = uint256.Int{} - s.top++ - - } - case opcode.PUSH1: - s := interp.Stack - - s.data[s.top] = uint256.Int{uint64(bc.code[bc.pc]), 0, 0, 0} - bc.pc++ - s.top++ - - case opcode.PUSH2: - s := interp.Stack - - v := uint64(bc.code[bc.pc])<<8 | uint64(bc.code[bc.pc+1]) - bc.pc += 2 - s.data[s.top] = uint256.Int{v, 0, 0, 0} - s.top++ - - case opcode.PUSH3: - s := interp.Stack - - c := bc.code - p := bc.pc - v := uint64(c[p])<<16 | uint64(c[p+1])<<8 | uint64(c[p+2]) - bc.pc = p + 3 - s.data[s.top] = uint256.Int{v, 0, 0, 0} - s.top++ - - case opcode.PUSH4: - s := interp.Stack - - c := bc.code - p := bc.pc - v := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) - bc.pc = p + 4 - s.data[s.top] = uint256.Int{v, 0, 0, 0} - s.top++ - - case opcode.PUSH20: - s := interp.Stack - - c := bc.code - p := bc.pc - // 20 bytes = limb2 (4 bytes) + limb1 (8 bytes) + limb0 (8 bytes) - l2 := uint64(c[p])<<24 | uint64(c[p+1])<<16 | uint64(c[p+2])<<8 | uint64(c[p+3]) - l1 := uint64(c[p+4])<<56 | uint64(c[p+5])<<48 | uint64(c[p+6])<<40 | uint64(c[p+7])<<32 | - uint64(c[p+8])<<24 | uint64(c[p+9])<<16 | uint64(c[p+10])<<8 | uint64(c[p+11]) - l0 := uint64(c[p+12])<<56 | uint64(c[p+13])<<48 | uint64(c[p+14])<<40 | uint64(c[p+15])<<32 | - uint64(c[p+16])<<24 | uint64(c[p+17])<<16 | uint64(c[p+18])<<8 | uint64(c[p+19]) - bc.pc = p + 20 - s.data[s.top] = uint256.Int{l0, l1, l2, 0} - s.top++ - - case opcode.PUSH32: - s := interp.Stack - - c := bc.code - p := bc.pc - l3 := uint64(c[p])<<56 | uint64(c[p+1])<<48 | uint64(c[p+2])<<40 | uint64(c[p+3])<<32 | - uint64(c[p+4])<<24 | uint64(c[p+5])<<16 | uint64(c[p+6])<<8 | uint64(c[p+7]) - l2 := uint64(c[p+8])<<56 | uint64(c[p+9])<<48 | uint64(c[p+10])<<40 | uint64(c[p+11])<<32 | - uint64(c[p+12])<<24 | uint64(c[p+13])<<16 | uint64(c[p+14])<<8 | uint64(c[p+15]) - l1 := uint64(c[p+16])<<56 | uint64(c[p+17])<<48 | uint64(c[p+18])<<40 | uint64(c[p+19])<<32 | - uint64(c[p+20])<<24 | uint64(c[p+21])<<16 | uint64(c[p+22])<<8 | uint64(c[p+23]) - l0 := uint64(c[p+24])<<56 | uint64(c[p+25])<<48 | uint64(c[p+26])<<40 | uint64(c[p+27])<<32 | - uint64(c[p+28])<<24 | uint64(c[p+29])<<16 | uint64(c[p+30])<<8 | uint64(c[p+31]) - bc.pc = p + 32 - s.data[s.top] = uint256.Int{l0, l1, l2, l3} - s.top++ - - case opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.PUSH17, opcode.PUSH18, opcode.PUSH19, opcode.PUSH21, opcode.PUSH22, opcode.PUSH23, opcode.PUSH24, opcode.PUSH25, opcode.PUSH26, opcode.PUSH27, opcode.PUSH28, opcode.PUSH29, opcode.PUSH30, opcode.PUSH31: - s := interp.Stack - n := int(op - opcode.PUSH0) - s.data[s.top] = *new(uint256.Int).SetBytes(bc.code[bc.pc : bc.pc+n]) - bc.pc += n - s.top++ - case opcode.DUP1: - s := interp.Stack - s.data[s.top] = s.data[s.top-1] - s.top++ - case opcode.DUP2: - s := interp.Stack - s.data[s.top] = s.data[s.top-2] - s.top++ - case opcode.DUP3: - s := interp.Stack - s.data[s.top] = s.data[s.top-3] - s.top++ - case opcode.DUP4: - s := interp.Stack - s.data[s.top] = s.data[s.top-4] - s.top++ - case opcode.DUP5: - s := interp.Stack - s.data[s.top] = s.data[s.top-5] - s.top++ - case opcode.DUP6: - s := interp.Stack - s.data[s.top] = s.data[s.top-6] - s.top++ - case opcode.DUP7: - s := interp.Stack - s.data[s.top] = s.data[s.top-7] - s.top++ - case opcode.DUP8: - s := interp.Stack - s.data[s.top] = s.data[s.top-8] - s.top++ - case opcode.DUP9: - s := interp.Stack - s.data[s.top] = s.data[s.top-9] - s.top++ - case opcode.DUP10: - s := interp.Stack - s.data[s.top] = s.data[s.top-10] - s.top++ - case opcode.DUP11: - s := interp.Stack - s.data[s.top] = s.data[s.top-11] - s.top++ - case opcode.DUP12: - s := interp.Stack - s.data[s.top] = s.data[s.top-12] - s.top++ - case opcode.DUP13: - s := interp.Stack - s.data[s.top] = s.data[s.top-13] - s.top++ - case opcode.DUP14: - s := interp.Stack - s.data[s.top] = s.data[s.top-14] - s.top++ - case opcode.DUP15: - s := interp.Stack - s.data[s.top] = s.data[s.top-15] - s.top++ - case opcode.DUP16: - s := interp.Stack - s.data[s.top] = s.data[s.top-16] - s.top++ - case opcode.SWAP1: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-1] = s.data[t-1], s.data[t] - case opcode.SWAP2: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-2] = s.data[t-2], s.data[t] - case opcode.SWAP3: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-3] = s.data[t-3], s.data[t] - case opcode.SWAP4: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-4] = s.data[t-4], s.data[t] - case opcode.SWAP5: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-5] = s.data[t-5], s.data[t] - case opcode.SWAP6: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-6] = s.data[t-6], s.data[t] - case opcode.SWAP7: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-7] = s.data[t-7], s.data[t] - case opcode.SWAP8: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-8] = s.data[t-8], s.data[t] - case opcode.SWAP9: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-9] = s.data[t-9], s.data[t] - case opcode.SWAP10: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-10] = s.data[t-10], s.data[t] - case opcode.SWAP11: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-11] = s.data[t-11], s.data[t] - case opcode.SWAP12: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-12] = s.data[t-12], s.data[t] - case opcode.SWAP13: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-13] = s.data[t-13], s.data[t] - case opcode.SWAP14: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-14] = s.data[t-14], s.data[t] - case opcode.SWAP15: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-15] = s.data[t-15], s.data[t] - case opcode.SWAP16: - s := interp.Stack - t := s.top - 1 - s.data[t], s.data[t-16] = s.data[t-16], s.data[t] - case opcode.LOG0: - logNImpl(interp, host, 0) - case opcode.LOG1: - logNImpl(interp, host, 1) - case opcode.LOG2: - logNImpl(interp, host, 2) - case opcode.LOG3: - logNImpl(interp, host, 3) - case opcode.LOG4: - logNImpl(interp, host, 4) - default: - interp.Halt(InstructionResultOpcodeNotFound) - } - } -} - -// Run executes bytecode with simple per-opcode gas and stack checks. -// It deliberately avoids the basic-block optimization and is intended for -// RPC/debug paths that prefer direct opcode-by-opcode behavior. -func (PlainRunner) Run(interp *Interpreter, host Host) { +// Run executes bytecode until halted using direct switch dispatch. +// Static gas is accumulated in a local gasCounter variable. Instead of +// checking and deducting gas per-instruction, static gas costs are summed +// across a basic block and flushed (checked + deducted) at block boundaries +// (jumps, dynamic-gas opcodes, halting opcodes). This eliminates one branch +// + one memory write per static-gas instruction in the hot loop. +func (DefaultRunner) Run(interp *Interpreter, host Host) { bc := interp.Bytecode gas := &interp.Gas + var gasCounter uint64 for bc.running { op := bc.code[bc.pc] bc.pc++ switch op { case opcode.STOP: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.Halt(InstructionResultStop) case opcode.ADD: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2051,13 +58,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MUL: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2068,13 +77,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.SUB: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2083,13 +94,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.DIV: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2102,13 +115,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.SDIV: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2119,13 +134,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MOD: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2135,16 +152,18 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { if !top.IsZero() { top.Mod(&a, top) } - - } - case opcode.SMOD: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return + } - gas.remaining -= spec.GasLow + case opcode.SMOD: + gasCounter += spec.GasLow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2155,13 +174,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.ADDMOD: - if gas.remaining < spec.GasMid { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasMid + gasCounter += spec.GasMid s := interp.Stack if s.top < 3 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top -= 2 @@ -2173,13 +194,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MULMOD: - if gas.remaining < spec.GasMid { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasMid + gasCounter += spec.GasMid s := interp.Stack if s.top < 3 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top -= 2 @@ -2191,20 +214,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.EXP: - if gas.remaining < spec.GasHigh { + gasCounter += spec.GasHigh + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasHigh + gas.remaining -= gasCounter + gasCounter = 0 opExp(interp) case opcode.SIGNEXTEND: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2215,13 +242,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.LT: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2234,13 +263,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.GT: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2253,13 +284,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.SLT: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2282,13 +315,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.SGT: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2311,13 +346,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.EQ: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2330,13 +367,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.ISZERO: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top == 0 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { @@ -2348,13 +387,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.AND: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2363,13 +404,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.OR: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2378,13 +421,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.XOR: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2393,13 +438,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.NOT: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top == 0 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { @@ -2407,13 +454,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.BYTE: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2430,16 +479,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.SHL: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2456,16 +513,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } } case opcode.SHR: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2482,16 +547,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } } case opcode.SAR: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { s.top-- @@ -2510,16 +583,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } } case opcode.CLZ: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Osaka) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top == 0 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { @@ -2529,20 +610,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } } case opcode.KECCAK256: - if gas.remaining < spec.GasKeccak256 { + gasCounter += spec.GasKeccak256 + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasKeccak256 + gas.remaining -= gasCounter + gasCounter = 0 opKeccak256(interp) case opcode.ADDRESS: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2551,20 +636,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.BALANCE: - if gas.remaining < interp.ForkGas.Balance { + gasCounter += interp.ForkGas.Balance + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Balance + gas.remaining -= gasCounter + gasCounter = 0 opBalance(interp, host) case opcode.ORIGIN: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2574,13 +663,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLER: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2589,13 +680,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLVALUE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2604,13 +697,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLDATALOAD: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top == 0 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { @@ -2633,13 +728,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLDATASIZE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2648,20 +745,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CALLDATACOPY: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 opCalldatacopy(interp) case opcode.CODESIZE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2670,20 +771,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CODECOPY: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 opCodecopy(interp) case opcode.GASPRICE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2692,30 +797,42 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.EXTCODESIZE: - if gas.remaining < interp.ForkGas.ExtCodeSize { + gasCounter += interp.ForkGas.ExtCodeSize + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.ExtCodeSize + gas.remaining -= gasCounter + gasCounter = 0 opExtcodesize(interp, host) case opcode.EXTCODECOPY: - if gas.remaining < interp.ForkGas.ExtCodeSize { + gasCounter += interp.ForkGas.ExtCodeSize + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.ExtCodeSize + gas.remaining -= gasCounter + gasCounter = 0 opExtcodecopy(interp, host) case opcode.RETURNDATASIZE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2725,35 +842,41 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } } case opcode.RETURNDATACOPY: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { interp.HaltNotActivated() } else { opReturndatacopy(interp) } case opcode.EXTCODEHASH: - if gas.remaining < interp.ForkGas.ExtCodeHash { + gasCounter += interp.ForkGas.ExtCodeHash + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.ExtCodeHash + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Constantinople) { interp.HaltNotActivated() } else { opExtcodehash(interp, host) } case opcode.BLOCKHASH: - if gas.remaining < spec.GasBlockhash { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBlockhash + gasCounter += spec.GasBlockhash s := interp.Stack if s.top == 0 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { @@ -2763,13 +886,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.COINBASE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2779,13 +904,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.TIMESTAMP: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2794,13 +921,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.NUMBER: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2809,20 +938,24 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.DIFFICULTY: - if gas.remaining < spec.GasBase { + gasCounter += spec.GasBase + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasBase + gas.remaining -= gasCounter + gasCounter = 0 opDifficulty(interp, host) case opcode.GASLIMIT: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -2831,103 +964,147 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.CHAINID: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { opChainid(interp, host) } } case opcode.SELFBALANCE: - if gas.remaining < spec.GasLow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasLow + gasCounter += spec.GasLow if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Istanbul) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { opSelfbalance(interp, host) } } case opcode.BASEFEE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.London) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { opBasefee(interp, host) } } case opcode.BLOBHASH: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opBlobhash(interp, host) } case opcode.BLOBBASEFEE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { opBlobbasefee(interp, host) } } case opcode.SLOTNUM: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { opSlotnum(interp, host) } } case opcode.POP: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if interp.Stack.top == 0 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltUnderflow() } else { @@ -2935,11 +1112,13 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MLOAD: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 s := interp.Stack if s.top == 0 { @@ -2961,11 +1140,13 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MSTORE: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 s := interp.Stack if s.top < 2 { @@ -2989,27 +1170,39 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MSTORE8: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 opMstore8(interp) case opcode.SLOAD: - if gas.remaining < interp.ForkGas.Sload { + gasCounter += interp.ForkGas.Sload + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Sload + gas.remaining -= gasCounter + gasCounter = 0 opSload(interp, host) case opcode.SSTORE: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 opSstore(interp, host) case opcode.JUMP: - if gas.remaining < spec.GasMid { + gasCounter += spec.GasMid + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasMid + gas.remaining -= gasCounter + gasCounter = 0 s := interp.Stack if s.top == 0 { @@ -3037,11 +1230,13 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { bc.pc = dest case opcode.JUMPI: - if gas.remaining < spec.GasHigh { + gasCounter += spec.GasHigh + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasHigh + gas.remaining -= gasCounter + gasCounter = 0 s := interp.Stack if s.top < 2 { @@ -3072,13 +1267,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PC: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3087,13 +1284,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.MSIZE: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3102,158 +1301,222 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.GAS: - if gas.remaining < spec.GasBase { + gasCounter += spec.GasBase + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasBase + gas.remaining -= gasCounter + gasCounter = 0 opGas(interp) case opcode.JUMPDEST: - if gas.remaining < spec.GasJumpdest { + gasCounter += spec.GasJumpdest + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasJumpdest + gas.remaining -= gasCounter + gasCounter = 0 case opcode.TLOAD: - if gas.remaining < spec.GasWarmStorageReadCost { + gasCounter += spec.GasWarmStorageReadCost + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasWarmStorageReadCost + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opTload(interp, host) } case opcode.TSTORE: - if gas.remaining < spec.GasWarmStorageReadCost { + gasCounter += spec.GasWarmStorageReadCost + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasWarmStorageReadCost + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opTstore(interp, host) } case opcode.MCOPY: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Cancun) { interp.HaltNotActivated() } else { opMcopy(interp) } case opcode.DUPN: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { interp.HaltNotActivated() } else { opDupN(interp) } case opcode.SWAPN: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { interp.HaltNotActivated() } else { opSwapN(interp) } case opcode.EXCHANGE: - if gas.remaining < spec.GasVerylow { + gasCounter += spec.GasVerylow + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= spec.GasVerylow + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Amsterdam) { interp.HaltNotActivated() } else { opExchange(interp) } case opcode.CREATE: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 opCreate(interp, host) case opcode.CALL: - if gas.remaining < interp.ForkGas.Call { + gasCounter += interp.ForkGas.Call + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Call + gas.remaining -= gasCounter + gasCounter = 0 opCall(interp, host) case opcode.CALLCODE: - if gas.remaining < interp.ForkGas.Call { + gasCounter += interp.ForkGas.Call + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Call + gas.remaining -= gasCounter + gasCounter = 0 opCallcode(interp, host) case opcode.RETURN: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 opReturn(interp) case opcode.DELEGATECALL: - if gas.remaining < interp.ForkGas.Call { + gasCounter += interp.ForkGas.Call + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Call + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Homestead) { interp.HaltNotActivated() } else { opDelegatecall(interp, host) } case opcode.CREATE2: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Petersburg) { interp.HaltNotActivated() } else { opCreate2(interp, host) } case opcode.STATICCALL: - if gas.remaining < interp.ForkGas.Call { + gasCounter += interp.ForkGas.Call + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Call + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { interp.HaltNotActivated() } else { opStaticcall(interp, host) } case opcode.REVERT: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Byzantium) { interp.HaltNotActivated() } else { opRevert(interp) } case opcode.INVALID: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.Halt(InstructionResultInvalidFEOpcode) case opcode.SELFDESTRUCT: - if gas.remaining < interp.ForkGas.Selfdestruct { + gasCounter += interp.ForkGas.Selfdestruct + if gas.remaining < gasCounter { interp.HaltOOG() return } - gas.remaining -= interp.ForkGas.Selfdestruct + gas.remaining -= gasCounter + gasCounter = 0 opSelfdestruct(interp, host) case opcode.PUSH0: - if gas.remaining < spec.GasBase { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasBase + gasCounter += spec.GasBase if !interp.RuntimeFlag.ForkID.IsEnabledIn(spec.Shanghai) { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltNotActivated() } else { s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3263,13 +1526,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } } case opcode.PUSH1: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3279,13 +1544,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH2: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3296,13 +1563,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH3: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3315,13 +1584,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH4: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3334,13 +1605,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH20: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3358,13 +1631,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH32: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { @@ -3384,13 +1659,15 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { } case opcode.PUSH5, opcode.PUSH6, opcode.PUSH7, opcode.PUSH8, opcode.PUSH9, opcode.PUSH10, opcode.PUSH11, opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16, opcode.PUSH17, opcode.PUSH18, opcode.PUSH19, opcode.PUSH21, opcode.PUSH22, opcode.PUSH23, opcode.PUSH24, opcode.PUSH25, opcode.PUSH26, opcode.PUSH27, opcode.PUSH28, opcode.PUSH29, opcode.PUSH30, opcode.PUSH31: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { n := int(op - opcode.PUSH0) @@ -3399,432 +1676,532 @@ func (PlainRunner) Run(interp *Interpreter, host Host) { s.top++ } case opcode.DUP1: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 1 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-1] s.top++ } case opcode.DUP2: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 2 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-2] - s.top++ - } - case opcode.DUP3: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return + s.top++ } - gas.remaining -= spec.GasVerylow + case opcode.DUP3: + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 3 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-3] s.top++ } case opcode.DUP4: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 4 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-4] s.top++ } case opcode.DUP5: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 5 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-5] s.top++ } case opcode.DUP6: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 6 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-6] s.top++ } case opcode.DUP7: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 7 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-7] s.top++ } case opcode.DUP8: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 8 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-8] s.top++ } case opcode.DUP9: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 9 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-9] s.top++ } case opcode.DUP10: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 10 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-10] s.top++ } case opcode.DUP11: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 11 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-11] s.top++ } case opcode.DUP12: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 12 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-12] s.top++ } case opcode.DUP13: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 13 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-13] s.top++ } case opcode.DUP14: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 14 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-14] s.top++ } case opcode.DUP15: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 15 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-15] s.top++ } case opcode.DUP16: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack if s.top < 16 || s.top >= StackLimit { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[s.top] = s.data[s.top-16] s.top++ } case opcode.SWAP1: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 1 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-1] = s.data[t-1], s.data[t] } case opcode.SWAP2: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 2 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-2] = s.data[t-2], s.data[t] } case opcode.SWAP3: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 3 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-3] = s.data[t-3], s.data[t] } case opcode.SWAP4: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 4 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-4] = s.data[t-4], s.data[t] } case opcode.SWAP5: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 5 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-5] = s.data[t-5], s.data[t] } case opcode.SWAP6: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 6 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-6] = s.data[t-6], s.data[t] } case opcode.SWAP7: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 7 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-7] = s.data[t-7], s.data[t] } case opcode.SWAP8: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 8 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-8] = s.data[t-8], s.data[t] } case opcode.SWAP9: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 9 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-9] = s.data[t-9], s.data[t] } case opcode.SWAP10: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 10 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-10] = s.data[t-10], s.data[t] } case opcode.SWAP11: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 11 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-11] = s.data[t-11], s.data[t] } case opcode.SWAP12: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 12 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-12] = s.data[t-12], s.data[t] } case opcode.SWAP13: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 13 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-13] = s.data[t-13], s.data[t] } case opcode.SWAP14: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 14 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-14] = s.data[t-14], s.data[t] } case opcode.SWAP15: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 15 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-15] = s.data[t-15], s.data[t] } case opcode.SWAP16: - if gas.remaining < spec.GasVerylow { - interp.HaltOOG() - return - } - gas.remaining -= spec.GasVerylow + gasCounter += spec.GasVerylow s := interp.Stack t := s.top - 1 if t < 16 { + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.HaltOverflow() } else { s.data[t], s.data[t-16] = s.data[t-16], s.data[t] } case opcode.LOG0: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 logNImpl(interp, host, 0) case opcode.LOG1: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 logNImpl(interp, host, 1) case opcode.LOG2: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 logNImpl(interp, host, 2) case opcode.LOG3: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 logNImpl(interp, host, 3) case opcode.LOG4: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 logNImpl(interp, host, 4) default: + if gas.remaining < gasCounter { + interp.HaltOOG() + return + } + gas.remaining -= gasCounter + gasCounter = 0 interp.Halt(InstructionResultOpcodeNotFound) } } From 7621aa9af0eb2569ad177b655a0c57caf3f1b875 Mon Sep 17 00:00:00 2001 From: Giulio Rebuffo Date: Fri, 15 May 2026 23:16:41 +0200 Subject: [PATCH 8/8] Verify tracing runner uses unbatched gas --- vm/gen/main.go | 7 ++++--- vm/runner_modes_test.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/vm/gen/main.go b/vm/gen/main.go index 21b3b074..86d9ffb9 100644 --- a/vm/gen/main.go +++ b/vm/gen/main.go @@ -4,10 +4,11 @@ // // The emitter is parameterised by a `tracing` flag. When false it produces // Run() — the fast path with a gas-counter accumulator and zero tracing -// overhead. When true it produces RunWithTracing() — per-opcode gas -// deduction, OnOpcode/OnFault hooks, and a DebugGasTable lookup for cost. +// overhead. When true it produces TracingRunner.Run() — the opcode path with +// immediate per-opcode gas deduction, OnOpcode/OnFault hooks, and a +// DebugGasTable lookup for cost. // -// Usage: go generate ./internal/vm/... +// Usage: go generate ./vm package main import ( diff --git a/vm/runner_modes_test.go b/vm/runner_modes_test.go index 915ed975..c69cdf75 100644 --- a/vm/runner_modes_test.go +++ b/vm/runner_modes_test.go @@ -1,6 +1,7 @@ package vm import ( + "reflect" "testing" "github.com/Giulio2002/gevm/opcode" @@ -90,6 +91,26 @@ func TestTracingRunnerWithoutOpcodeHookUsesDefaultPath(t *testing.T) { } } +func TestTracingRunnerReportsUnbatchedOpcodeGas(t *testing.T) { + code := []byte{byte(opcode.PUSH1), 1, byte(opcode.PUSH1), 2, byte(opcode.ADD), byte(opcode.STOP)} + var got []uint64 + hooks := &Hooks{ + OnOpcode: func(_ uint64, _ byte, gas uint64, _ uint64, _ OpContext, _ []byte, _ int, _ error) { + got = append(got, gas) + }, + } + + trace := runWithRunner(NewTracingRunner(hooks, spec.Prague), code, 100) + if trace.HaltResult != InstructionResultStop { + t.Fatalf("halt result: got %v, want %v", trace.HaltResult, InstructionResultStop) + } + + want := []uint64{100, 97, 94, 91} + if !reflect.DeepEqual(got, want) { + t.Fatalf("opcode gas mismatch: got %v, want %v", got, want) + } +} + func opcodeHooks() *Hooks { return &Hooks{ OnOpcode: func(uint64, byte, uint64, uint64, OpContext, []byte, int, error) {},