diff --git a/core/vm/evm.go b/core/vm/evm.go index 18aa50a8c8..b47adc8ed5 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -131,6 +131,11 @@ type EVM struct { returnData []byte // Last CALL's return data for subsequent reuse arena *stackArena + + // forceTableLoop is a test-only switch. The differential test sets it so + // that Run uses the table loop (execTraced) for the whole call tree, which + // lets the test compare it against the generated dispatch. + forceTableLoop bool } // NewEVM constructs an EVM instance with the supplied block context, state diff --git a/core/vm/gen/main.go b/core/vm/gen/main.go new file mode 100644 index 0000000000..2e7c0b45a3 --- /dev/null +++ b/core/vm/gen/main.go @@ -0,0 +1,497 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Command gen generates core/vm/interp_gen.go, the EVM interpreter's untraced +// fast-path dispatch. The output is a switch over the opcode byte that: +// +// - inlines the hot, fork-stable opcodes (arithmetic / comparison / bitwise / +// PUSH / DUP / SWAP / POP / JUMP / JUMPI / PC / MSIZE / JUMPDEST) by +// splicing the existing opXxx handler bodies from instructions.go and +// eips.go, with their static gas and stack bounds baked in as constants +// derived from the per-fork instruction tables via vm.GenForks. +// +// - calls the fork-invariant cold ops (KECCAK256 / MLOAD / MSTORE / MSTORE8, +// see directCold) directly by name, skipping the table's function +// pointers, which Go cannot inline through. +// +// - dispatches everything fork-varying (CALL / CREATE / SSTORE / SLOAD / LOG / +// the COPY family and so on) through the active per-fork JumpTable in the +// default case, exactly as the legacy loop did, so volatile gas and opcode +// logic stays shared rather than restated. +// +// The generated file is committed and a CI test asserts it matches `go generate` +// output. Do not hand-edit interp_gen.go. +// +// Usage: go generate ./core/vm/... +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/printer" + "go/token" + "os" + "path/filepath" + "regexp" + "runtime" + "strings" + + "github.com/ethereum/go-ethereum/core/vm" +) + +const stackLimit = 1024 // params.StackLimit + +// inlineHandler maps an opcode byte to the opXxx handler whose body is spliced +// inline for that opcode. These are the hot, fork-stable opcodes with no dynamic +// gas. Opcodes not listed here (or in directCold, or PUSH3-32 / DUP1-16, which +// are handled specially) fall through to the default case, which dispatches via +// the per-fork table. +var inlineHandler = map[byte]string{ + 0x01: "opAdd", 0x02: "opMul", 0x03: "opSub", 0x04: "opDiv", 0x05: "opSdiv", + 0x06: "opMod", 0x07: "opSmod", 0x08: "opAddmod", 0x09: "opMulmod", 0x0b: "opSignExtend", + 0x10: "opLt", 0x11: "opGt", 0x12: "opSlt", 0x13: "opSgt", 0x14: "opEq", 0x15: "opIszero", + 0x16: "opAnd", 0x17: "opOr", 0x18: "opXor", 0x19: "opNot", 0x1a: "opByte", + 0x1b: "opSHL", 0x1c: "opSHR", 0x1d: "opSAR", 0x1e: "opCLZ", + 0x50: "opPop", 0x56: "opJump", 0x57: "opJumpi", 0x58: "opPc", 0x59: "opMsize", 0x5b: "opJumpdest", + 0x5f: "opPush0", 0x60: "opPush1", 0x61: "opPush2", + 0x90: "opSwap1", 0x91: "opSwap2", 0x92: "opSwap3", 0x93: "opSwap4", + 0x94: "opSwap5", 0x95: "opSwap6", 0x96: "opSwap7", 0x97: "opSwap8", + 0x98: "opSwap9", 0x99: "opSwap10", 0x9a: "opSwap11", 0x9b: "opSwap12", + 0x9c: "opSwap13", 0x9d: "opSwap14", 0x9e: "opSwap15", 0x9f: "opSwap16", +} + +// directCold lists cold opcodes (dynamic gas, not inlined) whose handler, +// dynamic-gas, and memory-size functions are the same across every fork +// (verified: untouched by any enableXxx). They are emitted as direct calls to +// those functions by name instead of the indirect operation.* pointer calls +// in the default case. Go inlines the plain functions, and the var-aliased gas +// funcs (gasMLoad and friends alias pureMemoryGascost) at least skip the +// table load. Measured at ~3.4% on snailtracer (p=0.000). Fork-varying cold ops +// (CALL/SSTORE/SLOAD and friends) do not qualify and stay on the per-fork +// table in the default case. +// +// opcode → {handler, dynamicGas, memorySize} +// +// Limited to the memory/hash ops that appear in hot loops. Adding more (e.g. +// CALLDATACOPY/RETURN, typically once per call) grows the generated function +// and regresses tiny benchmarks through code layout, for negligible gain. +var directCold = map[byte][3]string{ + 0x20: {"opKeccak256", "gasKeccak256", "memoryKeccak256"}, // KECCAK256 + 0x51: {"opMload", "gasMLoad", "memoryMLoad"}, // MLOAD + 0x52: {"opMstore", "gasMStore", "memoryMStore"}, // MSTORE + 0x53: {"opMstore8", "gasMStore8", "memoryMStore8"}, // MSTORE8 +} + +// opMeta is the per-opcode metadata derived from the per-fork tables. +type opMeta struct { + defined bool + name string // opcode mnemonic, e.g. "ADD" + introF string // params.Rules field activating it, empty for Frontier (always on) + constGas uint64 + minStack int + maxStack int +} + +type generator struct { + fset *token.FileSet + handlers map[string]*ast.FuncDecl + meta [256]opMeta + buf *bytes.Buffer +} + +func (g *generator) p(format string, args ...any) { fmt.Fprintf(g.buf, format, args...) } + +// --------------------------------------------------------------------------- +// Handler parsing + body splicing +// --------------------------------------------------------------------------- + +// parseHandlers parses instructions.go and eips.go and returns every top-level +// opXxx function declaration by name. +func parseHandlers(vmDir string) (*token.FileSet, map[string]*ast.FuncDecl) { + fset := token.NewFileSet() + handlers := map[string]*ast.FuncDecl{} + for _, name := range []string{"instructions.go", "eips.go"} { + path := filepath.Join(vmDir, name) + f, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + if err != nil { + fatalf("parse %s: %v", path, err) + } + for _, decl := range f.Decls { + fn, ok := decl.(*ast.FuncDecl) + if !ok || fn.Recv != nil || fn.Body == nil { + continue + } + handlers[fn.Name.Name] = fn + } + } + return fset, handlers +} + +var returnRe = regexp.MustCompile(`^(\s*)return\s+([^,]+),\s*(.+)$`) + +// inlineBody returns the source of handler's body, rewritten so it can be +// spliced into the dispatch loop: the `*pc` dereference becomes the loop's `pc` +// local, and each `return r0, r1` becomes loop control flow. Success +// (r1 == nil) advances pc and continues, an error sets err and breaks. +func (g *generator) inlineBody(handler string) string { + fn := g.handlers[handler] + if fn == nil { + fatalf("no handler %q to inline", handler) + } + var raw bytes.Buffer + cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} + for _, stmt := range fn.Body.List { + if err := cfg.Fprint(&raw, g.fset, stmt); err != nil { + fatalf("print %s body: %v", handler, err) + } + raw.WriteByte('\n') + } + src := strings.ReplaceAll(raw.String(), "*pc", "pc") + + var out strings.Builder + for _, line := range strings.Split(src, "\n") { + if m := returnRe.FindStringSubmatch(line); m != nil { + indent, r0, r1 := m[1], strings.TrimSpace(m[2]), strings.TrimSpace(m[3]) + // The error and halt path must overwrite res and err. Otherwise a + // halting inlined op (JUMPI on an invalid jump, say) returns stale + // res from an earlier res-setting op such as a DELEGATECALL. The + // fuzzer caught exactly that bug. The success path advances pc and + // continues without writing res or err: stale res on a continuing + // op is always overwritten by whichever op ends the run, and err + // stays nil through normal iteration. Keeping these stores off the + // success path avoids growing every inlined case, which regresses + // tiny, layout-sensitive benchmarks. + if r1 == "nil" { + out.WriteString(indent + "pc++\n") + out.WriteString(indent + "continue mainLoop\n") + } else { + out.WriteString(indent + "res, err = " + r0 + ", " + r1 + "\n") + out.WriteString(indent + "break mainLoop\n") + } + continue + } + out.WriteString(line + "\n") + } + return out.String() +} + +// --------------------------------------------------------------------------- +// Metadata derivation (from the per-fork tables, via vm.GenForks) +// --------------------------------------------------------------------------- + +func (g *generator) deriveMeta(forks []vm.GenFork) { + for code := 0; code < 256; code++ { + for _, fork := range forks { + o := fork.Ops[code] + if !o.Defined { + continue + } + g.meta[code] = opMeta{ + defined: true, + name: o.Name, + introF: fork.RuleField, + constGas: o.ConstantGas, + minStack: o.MinStack, + maxStack: o.MaxStack, + } + break // first fork that defines it wins (its intro fork) + } + } + // Sanity: every inlined opcode must be defined and have fork-stable static + // gas / stack bounds across all forks where it appears (that is what makes + // it safe to bake as a constant). Bail loudly otherwise. + for code, handler := range inlineHandler { + g.checkStable(byte(code), handler, forks) + } + for code := 0x62; code <= 0x7f; code++ { // PUSH3-32 + g.checkStable(byte(code), "makePush", forks) + } + for code := 0x80; code <= 0x8f; code++ { // DUP1-16 + g.checkStable(byte(code), "makeDup", forks) + } +} + +func (g *generator) checkStable(code byte, what string, forks []vm.GenFork) { + m := g.meta[code] + if !m.defined { + fatalf("opcode %#x (%s) selected for inlining but never defined", code, what) + } + for _, fork := range forks { + o := fork.Ops[code] + if !o.Defined { + continue + } + if o.ConstantGas != m.constGas || o.MinStack != m.minStack || o.MaxStack != m.maxStack || o.HasDynamicGas { + fatalf("opcode %#x (%s) is not fork-stable (fork %s): cannot inline", code, what, fork.Name) + } + } +} + +// --------------------------------------------------------------------------- +// Case emission +// --------------------------------------------------------------------------- + +// emitStackChecks emits the underflow/overflow guards for a baked opcode, +// mirroring the legacy loop's order (stack validated before gas). +func (g *generator) emitStackChecks(m opMeta) { + under := m.minStack > 0 + over := m.maxStack < stackLimit + switch { + case under && over: + g.p("if sLen := stack.len(); sLen < %d {\nreturn nil, &ErrStackUnderflow{stackLen: sLen, required: %d}\n} else if sLen > %d {\nreturn nil, &ErrStackOverflow{stackLen: sLen, limit: %d}\n}\n", m.minStack, m.minStack, m.maxStack, m.maxStack) + case under: + g.p("if sLen := stack.len(); sLen < %d {\nreturn nil, &ErrStackUnderflow{stackLen: sLen, required: %d}\n}\n", m.minStack, m.minStack) + case over: + g.p("if sLen := stack.len(); sLen > %d {\nreturn nil, &ErrStackOverflow{stackLen: sLen, limit: %d}\n}\n", m.maxStack, m.maxStack) + } +} + +func (g *generator) emitGasCheck(m opMeta) { + if m.constGas == 0 { + return + } + g.p("if contract.Gas.RegularGas < %d {\nreturn nil, ErrOutOfGas\n}\ncontract.Gas.RegularGas -= %d\n", m.constGas, m.constGas) +} + +// emitWork emits the stack/gas guards and the opcode body (the portion that runs +// when the opcode is active for the current fork). +func (g *generator) emitWork(code byte) { + m := g.meta[code] + g.emitStackChecks(m) + g.emitGasCheck(m) + + // PUSH1-PUSH32 swap their execute function under EIP-4762 (verkle) to charge + // code-chunk gas on the immediate bytes. Defer to the table handler there. + // The baked static gas and stack guard above already match. + if code >= 0x60 && code <= 0x7f { + g.p("if isEIP4762 {\nres, err = table[op].execute(&pc, evm, scope)\nif err != nil {\nbreak mainLoop\n}\npc++\ncontinue mainLoop\n}\n") + } + + switch { + case code >= 0x62 && code <= 0x7f: // PUSH3-PUSH32: inline makePush(n,n) + g.emitPushFixed(int(code) - 0x5f) + case code >= 0x80 && code <= 0x8f: // DUP1-DUP16: inline makeDup(n) + g.p("scope.Stack.dup(%d)\npc++\ncontinue mainLoop\n", int(code)-0x7f) + default: + g.p("%s", g.inlineBody(inlineHandler[code])) + } +} + +// emitPushFixed inlines makePush(n, n) for PUSH (n = 3..32). +func (g *generator) emitPushFixed(n int) { + g.p("codeLen := len(scope.Contract.Code)\n") + g.p("start := min(codeLen, int(pc+1))\n") + g.p("end := min(codeLen, start+%d)\n", n) + g.p("a := scope.Stack.get()\n") + g.p("a.SetBytes(scope.Contract.Code[start:end])\n") + g.p("if missing := %d - (end - start); missing > 0 {\na.Lsh(a, uint(8*missing))\n}\n", n) + g.p("pc += %d\npc++\ncontinue mainLoop\n", n) +} + +func (g *generator) emitInlineCase(code byte) { + m := g.meta[code] + g.p("case %s:\n", m.name) + if m.introF == "" { + g.emitWork(code) + return + } + // Fork-gated: run the inlined body only when the opcode is active for the + // current fork. Otherwise mirror the legacy loop's undefined-opcode handling. + g.p("if rules.%s {\n", m.introF) + g.emitWork(code) + g.p("}\n") + g.p("res, err = opUndefined(&pc, evm, scope)\nbreak mainLoop\n") +} + +// emitDirectCold emits a cold opcode case identical to the default case, except +// the handler, dynamic-gas, and memory-size functions are called by name +// rather than through the indirect operation.* table pointers. Valid only for +// fork-invariant ops (see directCold). +func (g *generator) emitDirectCold(code byte) { + m := g.meta[code] + fns := directCold[code] + g.p("case %s:\n", m.name) + g.emitStackChecks(m) + g.emitGasCheck(m) + g.p("var memorySize uint64\n{\n") + g.p("memSize, overflow := %s(stack)\n", fns[2]) + g.p("if overflow {\nreturn nil, ErrGasUintOverflow\n}\n") + g.p("if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {\nreturn nil, ErrGasUintOverflow\n}\n}\n") + g.p("var dynamicCost GasCosts\n") + g.p("dynamicCost, err = %s(evm, contract, stack, mem, memorySize)\n", fns[1]) + // WriteString: keep %w/%v literal (not generator format verbs). + g.buf.WriteString("if err != nil {\nreturn nil, fmt.Errorf(\"%w: %v\", ErrOutOfGas, err)\n}\n") + g.p("if contract.Gas.RegularGas < dynamicCost.RegularGas {\nreturn nil, ErrOutOfGas\n}\n") + g.p("contract.Gas.RegularGas -= dynamicCost.RegularGas\n") + g.p("if memorySize > 0 {\nmem.Resize(memorySize)\n}\n") + g.p("res, err = %s(&pc, evm, scope)\n", fns[0]) + g.p("if err != nil {\nbreak mainLoop\n}\npc++\ncontinue mainLoop\n") +} + +func (g *generator) emitDefault() { + // WriteString, not p(): this template contains %w/%v that must reach the + // output verbatim (they are not generator format verbs). + g.buf.WriteString(`default: +operation := table[op] +if sLen := stack.len(); sLen < operation.minStack { +return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} +} else if sLen > operation.maxStack { +return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} +} +cost := operation.constantGas +if contract.Gas.RegularGas < cost { +return nil, ErrOutOfGas +} +contract.Gas.RegularGas -= cost +var memorySize uint64 +if operation.dynamicGas != nil { +if operation.memorySize != nil { +memSize, overflow := operation.memorySize(stack) +if overflow { +return nil, ErrGasUintOverflow +} +if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { +return nil, ErrGasUintOverflow +} +} +var dynamicCost GasCosts +dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize) +if err != nil { +return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) +} +if contract.Gas.RegularGas < dynamicCost.RegularGas { +return nil, ErrOutOfGas +} +contract.Gas.RegularGas -= dynamicCost.RegularGas +} +if memorySize > 0 { +mem.Resize(memorySize) +} +res, err = operation.execute(&pc, evm, scope) +if err != nil { +break mainLoop +} +pc++ +continue mainLoop +`) +} + +// --------------------------------------------------------------------------- +// File emission +// --------------------------------------------------------------------------- + +func (g *generator) emitFile() { + g.p("// Code generated by core/vm/gen; DO NOT EDIT.\n\n") + g.p("package vm\n\n") + g.p("import (\n") + g.p("\t\"fmt\"\n\n") + g.p("\t\"github.com/ethereum/go-ethereum/common/math\"\n") + g.p("\t\"github.com/ethereum/go-ethereum/core/tracing\"\n") + g.p(")\n\n") + + g.buf.WriteString(`// execUntraced is the generated, tracing-free interpreter fast path. Hot, +// fork-stable opcodes are inlined with their static gas and stack bounds baked +// in. Fork-invariant cold ops (KECCAK256/MLOAD/MSTORE/MSTORE8) call their +// handler and gas functions directly by name. Everything fork-varying is +// dispatched through the active per-fork table in the default case. EVM.Run +// selects this path when no tracer is configured. +func (evm *EVM) execUntraced(scope *ScopeContext) (ret []byte, err error) { +var ( +contract = scope.Contract +mem = scope.Memory +stack = scope.Stack +table = evm.table +rules = evm.chainRules +isEIP4762 = rules.IsEIP4762 +pc = uint64(0) +res []byte +) +_ = mem +_ = rules +_ = isEIP4762 +_ = table +mainLoop: +for { +if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall { +contractAddr := contract.Address() +consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas) +contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) +if consumed < wanted { +return nil, ErrOutOfGas +} +} +op := contract.GetOp(pc) +switch op { +`) + // Inlined hot cases, in opcode order for readability. + for code := 0; code < 256; code++ { + b := byte(code) + _, named := inlineHandler[b] + isPushFixed := code >= 0x62 && code <= 0x7f + isDup := code >= 0x80 && code <= 0x8f + if named || isPushFixed || isDup { + g.emitInlineCase(b) + } else if _, dc := directCold[b]; dc { + g.emitDirectCold(b) + } + } + g.emitDefault() + g.p("}\n") // switch + g.p("}\n") // for + g.p("if err == errStopToken {\nerr = nil\n}\n") + g.p("return res, err\n") + g.p("}\n") // func +} + +// --------------------------------------------------------------------------- + +func fatalf(format string, args ...any) { + fmt.Fprintf(os.Stderr, "gen: "+format+"\n", args...) + os.Exit(1) +} + +func main() { + _, self, _, ok := runtime.Caller(0) + if !ok { + fatalf("cannot resolve generator source path") + } + vmDir := filepath.Dir(filepath.Dir(self)) // .../core/vm/gen -> .../core/vm + + fset, handlers := parseHandlers(vmDir) + g := &generator{fset: fset, handlers: handlers, buf: new(bytes.Buffer)} + g.deriveMeta(vm.GenForks()) + g.emitFile() + + formatted, err := format.Source(g.buf.Bytes()) + if err != nil { + dbg := filepath.Join(vmDir, "interp_gen.go.broken") + os.WriteFile(dbg, g.buf.Bytes(), 0644) + fatalf("gofmt failed (%v); wrote unformatted output to %s", err, dbg) + } + // INTERP_GEN_OUT lets the CI-match test (interp_gen_test.go) regenerate to a + // temporary file and diff it against the committed one, without clobbering it. + out := filepath.Join(vmDir, "interp_gen.go") + if env := os.Getenv("INTERP_GEN_OUT"); env != "" { + out = env + } + if err := os.WriteFile(out, formatted, 0644); err != nil { + fatalf("write %s: %v", out, err) + } + fmt.Fprintf(os.Stderr, "gen: wrote %s (%d bytes)\n", out, len(formatted)) +} diff --git a/core/vm/genspec.go b/core/vm/genspec.go new file mode 100644 index 0000000000..c1e64cdc2a --- /dev/null +++ b/core/vm/genspec.go @@ -0,0 +1,106 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +//go:generate go run ./gen + +// This file exposes the interpreter's opcode metadata to the code generator in +// core/vm/gen. It is not used at runtime. It exists so the generator can derive +// the per-opcode spec (static gas, stack bounds, the fork an opcode first +// appears in, and whether it carries dynamic gas or memory sizing) from the +// existing per-fork instruction sets, rather than restating that metadata. +// +// The fork-varying dynamic-gas / memory-size / execute *functions* are not +// surfaced here: several are closures (gasCall, the memoryCopierGas family, +// makeGasLog) that cannot be recovered by name. The generated switch instead +// reaches those volatile opcodes through the active per-fork JumpTable at +// runtime (see interp_gen.go), so they need no generator-side restatement. + +// GenOp is the generator-facing scalar metadata for one opcode slot in one fork. +type GenOp struct { + Name string // opcode mnemonic, e.g. "ADD" (valid only if Defined) + Defined bool // false if the slot is undefined/invalid in this fork + ConstantGas uint64 + MinStack int + MaxStack int + HasDynamicGas bool + HasMemorySize bool +} + +// GenFork bundles a fork's name, the params.Rules bool field that activates it +// (empty for Frontier, which is always active), and its per-opcode metadata. +type GenFork struct { + Name string + RuleField string + Ops [256]GenOp +} + +// genForkOrder is the canonical fork progression for code generation, oldest to +// newest, each paired with the params.Rules field that activates it. +// +// Petersburg is omitted: it shares Constantinople's opcode set and only changes +// SSTORE dynamic gas, which flows through the shared gas function. Verkle/UBT is +// omitted: over its Shanghai base it adds no new opcodes (it only swaps gas and +// execute on existing opcodes), which the generated switch picks up from the +// active table at runtime. +var genForkOrder = []struct { + name string + rule string + set *JumpTable +}{ + {"Frontier", "", &frontierInstructionSet}, + {"Homestead", "IsHomestead", &homesteadInstructionSet}, + {"TangerineWhistle", "IsEIP150", &tangerineWhistleInstructionSet}, + {"SpuriousDragon", "IsEIP158", &spuriousDragonInstructionSet}, + {"Byzantium", "IsByzantium", &byzantiumInstructionSet}, + {"Constantinople", "IsConstantinople", &constantinopleInstructionSet}, + {"Istanbul", "IsIstanbul", &istanbulInstructionSet}, + {"Berlin", "IsBerlin", &berlinInstructionSet}, + {"London", "IsLondon", &londonInstructionSet}, + {"Merge", "IsMerge", &mergeInstructionSet}, + {"Shanghai", "IsShanghai", &shanghaiInstructionSet}, + {"Cancun", "IsCancun", &cancunInstructionSet}, + {"Prague", "IsPrague", &pragueInstructionSet}, + {"Osaka", "IsOsaka", &osakaInstructionSet}, + {"Amsterdam", "IsAmsterdam", &amsterdamInstructionSet}, +} + +// GenForks returns per-fork opcode metadata for the interpreter code generator +// (core/vm/gen). It is exported solely for that purpose. +func GenForks() []GenFork { + out := make([]GenFork, len(genForkOrder)) + for i, f := range genForkOrder { + gf := GenFork{Name: f.name, RuleField: f.rule} + for code := 0; code < 256; code++ { + op := f.set[code] + if op == nil || op.undefined { + continue + } + gf.Ops[code] = GenOp{ + Name: OpCode(code).String(), + Defined: true, + ConstantGas: op.constantGas, + MinStack: op.minStack, + MaxStack: op.maxStack, + HasDynamicGas: op.dynamicGas != nil, + HasMemorySize: op.memorySize != nil, + } + } + out[i] = gf + } + return out +} diff --git a/core/vm/interp_diff_test.go b/core/vm/interp_diff_test.go new file mode 100644 index 0000000000..0b32959d86 --- /dev/null +++ b/core/vm/interp_diff_test.go @@ -0,0 +1,365 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +// Differential test comparing the table loop against the generated dispatch. +// +// This file proves that the generated dispatch (execUntraced) is bit-identical +// to the table-walking loop (execTraced, run here without a tracer via +// EVM.forceTableLoop) for the observable surface of an EVM execution: return +// data, gas left, error/halt, refund counter, emitted logs, and the resulting +// state root. It runs the same program through both interpreters over freshly +// built, identical state across several forks, plus a fuzz target over +// arbitrary bytecode. +// +// execTraced is also the production tracing path, and +// if it drifted from the generated dispatch then traced re-execution would +// disagree with what consensus executed. Hook emission itself is covered by +// the tracer test suites instead. + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +// diffForks is the set of fork configurations the diff test runs every program +// under. Spanning forks exercises the baked runtime fork gates (e.g. SHL from +// Constantinople, PUSH0 from Shanghai, CLZ from Osaka) in both the active and +// the not-yet-activated states. +var diffForks = func() []struct { + name string + cfg *params.ChainConfig + merged bool +} { + // preConstantinople: Byzantium active, Constantinople and later not. + preCon := *params.TestChainConfig + preCon.ConstantinopleBlock = nil + preCon.PetersburgBlock = nil + preCon.IstanbulBlock = nil + preCon.MuirGlacierBlock = nil + preCon.BerlinBlock = nil + preCon.LondonBlock = nil + preCon.ArrowGlacierBlock = nil + preCon.GrayGlacierBlock = nil + + return []struct { + name string + cfg *params.ChainConfig + merged bool + }{ + {"Frontier", params.NonActivatedConfig, false}, + {"Byzantium", &preCon, false}, + {"London", params.TestChainConfig, false}, + {"Merged", params.MergedTestChainConfig, true}, + } +}() + +var ( + diffContractAddr = common.HexToAddress("0x000000000000000000000000000000000000c0de") + diffCalleeAddr = common.HexToAddress("0x000000000000000000000000000000000000ca11") + diffCaller = common.HexToAddress("0x000000000000000000000000000000000000face") +) + +// diffCalleeCode is deployed at diffCalleeAddr as a CALL/CREATE target: it +// writes a storage slot, logs, and returns 32 bytes of memory. +// +// PUSH1 0x2a PUSH1 0x07 SSTORE // sstore(7, 42) +// PUSH1 0xbb PUSH1 0x00 MSTORE // mem[0..32] = 0xbb +// PUSH1 0x20 PUSH1 0x00 LOG0 // log0(mem[0:32]) +// PUSH1 0x20 PUSH1 0x00 RETURN // return mem[0:32] +var diffCalleeCode = []byte{ + byte(PUSH1), 0x2a, byte(PUSH1), 0x07, byte(SSTORE), + byte(PUSH1), 0xbb, byte(PUSH1), 0x00, byte(MSTORE), + byte(PUSH1), 0x20, byte(PUSH1), 0x00, byte(LOG0), + byte(PUSH1), 0x20, byte(PUSH1), 0x00, byte(RETURN), +} + +// asm is a tiny helper to build bytecode from opcodes/immediates. +func asm(parts ...any) []byte { + var b []byte + for _, p := range parts { + switch v := p.(type) { + case OpCode: + b = append(b, byte(v)) + case byte: + b = append(b, v) + case int: + b = append(b, byte(v)) + case []byte: + b = append(b, v...) + default: + panic("asm: bad part") + } + } + return b +} + +// diffPrograms is a curated set of bytecode snippets covering the inlined hot +// opcodes, the volatile call-through opcodes, fork-gated opcodes, control flow, +// and the principal error paths. +var diffPrograms = []struct { + name string + code []byte + gas uint64 +}{ + {"arith", asm(PUSH1, 0x07, PUSH1, 0x03, ADD, PUSH1, 0x02, MUL, PUSH1, 0x04, SUB, PUSH1, 0x03, DIV, PUSH1, 0x05, MOD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"signed-arith", asm(PUSH1, 0x07, PUSH1, 0xfd, SDIV, PUSH1, 0x03, SMOD, PUSH1, 0x02, SIGNEXTEND, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"addmod-mulmod-exp", asm(PUSH1, 0x07, PUSH1, 0x05, PUSH1, 0x03, ADDMOD, PUSH1, 0x09, PUSH1, 0x04, PUSH1, 0x02, MULMOD, PUSH1, 0x03, PUSH1, 0x02, EXP, ADD, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"cmp", asm(PUSH1, 0x07, PUSH1, 0x03, LT, PUSH1, 0x01, GT, PUSH1, 0x01, SLT, PUSH1, 0x01, SGT, PUSH1, 0x01, EQ, ISZERO, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"bitwise", asm(PUSH1, 0xf0, PUSH1, 0x0f, AND, PUSH1, 0xaa, OR, PUSH1, 0x55, XOR, NOT, PUSH1, 0x01, BYTE, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"shifts-clz", asm(PUSH1, 0xff, PUSH1, 0x04, SHL, PUSH1, 0x02, SHR, PUSH1, 0x01, SAR, CLZ, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"dup-swap", asm(PUSH1, 0x01, PUSH1, 0x02, PUSH1, 0x03, DUP3, SWAP2, DUP1, SWAP1, POP, ADD, ADD, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"push0-push32", asm(PUSH0, PUSH3, 0x01, 0x02, 0x03, ADD, PUSH5, 0x01, 0x02, 0x03, 0x04, 0x05, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"keccak", asm(PUSH1, 0x20, PUSH1, 0x00, KECCAK256, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"memory", asm(PUSH1, 0xab, PUSH1, 0x00, MSTORE8, PUSH1, 0xcd, PUSH2, 0x00, 0x40, MSTORE, MSIZE, PUSH1, 0x60, MSTORE, PUSH1, 0x80, PUSH1, 0x00, RETURN), 100000}, + {"mcopy", asm(PUSH1, 0xff, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, PUSH1, 0x20, MCOPY, PUSH1, 0x40, PUSH1, 0x00, RETURN), 100000}, + {"storage", asm(PUSH1, 0x63, PUSH1, 0x07, SSTORE, PUSH1, 0x07, SLOAD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"transient", asm(PUSH1, 0x63, PUSH1, 0x07, TSTORE, PUSH1, 0x07, TLOAD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"loop", asm(PUSH1, 0x00, JUMPDEST, PUSH1, 0x01, ADD, DUP1, PUSH1, 0x05, LT, PUSH1, 0x02, JUMPI, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"jump", asm(PUSH1, 0x06, JUMP, INVALID, INVALID, JUMPDEST, PUSH1, 0x2a, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"env", asm(ADDRESS, CALLER, CALLVALUE, ORIGIN, GASPRICE, CODESIZE, GAS, PC, ADD, ADD, ADD, ADD, ADD, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"block", asm(NUMBER, TIMESTAMP, COINBASE, GASLIMIT, CHAINID, SELFBALANCE, BASEFEE, DIFFICULTY, ADD, ADD, ADD, ADD, ADD, ADD, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"calldata", asm(PUSH1, 0x00, CALLDATALOAD, CALLDATASIZE, PUSH1, 0x00, PUSH1, 0x00, CALLDATACOPY, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"codecopy", asm(PUSH1, 0x10, PUSH1, 0x00, PUSH1, 0x00, CODECOPY, PUSH1, 0x10, PUSH1, 0x00, RETURN), 100000}, + {"log", asm(PUSH1, 0x11, PUSH1, 0x00, MSTORE, PUSH1, 0x22, PUSH1, 0x33, PUSH1, 0x20, PUSH1, 0x00, LOG2, STOP), 100000}, + // Fuzz-found regression (the stale-res bug): a res-setting DELEGATECALL + // followed by a halting inlined op (JUMPI to an invalid destination). The + // buggy build returned the DELEGATECALL output instead of nil. + {"delegatecall-then-invalid-jumpi", asm( + PUSH1, 0x30, PUSH1, 0x30, PUSH1, 0x30, PUSH1, 0x30, + PUSH20, diffCalleeAddr.Bytes(), + PUSH2, 0x30, 0x30, DELEGATECALL, + PC, PC, JUMPI), 100000}, + {"extaccess", asm(PUSH20, diffCalleeAddr.Bytes(), EXTCODESIZE, PUSH20, diffCalleeAddr.Bytes(), BALANCE, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 100000}, + {"call", asm( + PUSH1, 0x20, PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, + PUSH20, diffCalleeAddr.Bytes(), PUSH2, 0xff, 0xff, CALL, + PUSH1, 0x20, PUSH1, 0x00, RETURN), 200000}, + {"staticcall", asm( + PUSH1, 0x20, PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, + PUSH20, diffCalleeAddr.Bytes(), PUSH2, 0xff, 0xff, STATICCALL, + PUSH1, 0x20, PUSH1, 0x00, RETURN), 200000}, + {"delegatecall", asm( + PUSH1, 0x20, PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, + PUSH20, diffCalleeAddr.Bytes(), PUSH2, 0xff, 0xff, DELEGATECALL, + PUSH1, 0x20, PUSH1, 0x00, RETURN), 200000}, + {"create", asm( + // store init code that returns empty, then CREATE + PUSH1, 0x00, PUSH1, 0x00, MSTORE, + PUSH1, 0x00, PUSH1, 0x00, PUSH1, 0x00, CREATE, + PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 200000}, + {"revert", asm(PUSH1, 0xaa, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, REVERT), 100000}, + {"selfdestruct", asm(PUSH20, diffCaller.Bytes(), SELFDESTRUCT), 100000}, + {"stop", asm(PUSH1, 0x01, STOP), 100000}, + {"invalid-opcode", asm(PUSH1, 0x01, INVALID), 100000}, + {"undefined-opcode", asm(PUSH1, 0x01, 0x0c), 100000}, + {"stack-underflow", asm(ADD), 100000}, + {"oog", asm(PUSH1, 0x07, PUSH1, 0x03, ADD, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN), 7}, + {"invalid-jump", asm(PUSH1, 0x03, JUMP, STOP), 100000}, +} + +// diffResult captures the observable outcome of running a program. +type diffResult struct { + ret []byte + gasLeft uint64 + errStr string // "" if no error + refund uint64 + root common.Hash + logs []*types.Log +} + +func (r diffResult) equal(o diffResult) (string, bool) { + if !bytes.Equal(r.ret, o.ret) { + return "return data", false + } + if r.gasLeft != o.gasLeft { + return "gas left", false + } + if r.errStr != o.errStr { + return "error", false + } + if r.refund != o.refund { + return "refund", false + } + if r.root != o.root { + return "state root", false + } + if len(r.logs) != len(o.logs) { + return "log count", false + } + for i := range r.logs { + a, b := r.logs[i], o.logs[i] + if a.Address != b.Address || !bytes.Equal(a.Data, b.Data) || len(a.Topics) != len(b.Topics) { + return "log content", false + } + for j := range a.Topics { + if a.Topics[j] != b.Topics[j] { + return "log topic", false + } + } + } + return "", true +} + +func newDiffState(t testing.TB) *state.StateDB { + t.Helper() + statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + if err != nil { + t.Fatalf("state.New: %v", err) + } + // Main contract: balance + a pre-set storage slot. + statedb.CreateAccount(diffContractAddr) + statedb.SetBalance(diffContractAddr, uint256.NewInt(1000), tracing.BalanceChangeUnspecified) + statedb.SetState(diffContractAddr, common.Hash{31: 0x07}, common.Hash{31: 0x07}) + // Callee target for CALL/STATICCALL/DELEGATECALL. + statedb.CreateAccount(diffCalleeAddr) + statedb.SetBalance(diffCalleeAddr, uint256.NewInt(500), tracing.BalanceChangeUnspecified) + statedb.SetCode(diffCalleeAddr, diffCalleeCode, tracing.CodeChangeUnspecified) + // Caller EOA with a balance. + statedb.CreateAccount(diffCaller) + statedb.SetBalance(diffCaller, uint256.NewInt(1<<62), tracing.BalanceChangeUnspecified) + statedb.Finalise(true) + return statedb +} + +func diffBlockCtx(merged bool) BlockContext { + ctx := BlockContext{ + CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *params.Rules) {}, + GetHash: func(uint64) common.Hash { return common.Hash{0xde, 0xad} }, + Coinbase: common.HexToAddress("0xc01ba5e"), + BlockNumber: big.NewInt(8), + Time: 1234, + Difficulty: big.NewInt(0x20000), + GasLimit: 30_000_000, + BaseFee: big.NewInt(7), + BlobBaseFee: big.NewInt(3), + } + if merged { + h := common.HexToHash("0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20") + ctx.Random = &h + } + return ctx +} + +// TestExtraEIPs checks that EIPs enabled via Config.ExtraEips take effect even +// when they touch opcodes the generated dispatch inlines. PUSH0 (EIP-3855) on +// a pre-Shanghai config is the canary: the runtime table has it enabled but +// the baked fork gate does not, so execution must route through the table loop. +func TestExtraEIPs(t *testing.T) { + code := asm(PUSH0, STOP) + statedb := newDiffState(t) + statedb.SetCode(diffContractAddr, code, tracing.CodeChangeUnspecified) + statedb.Finalise(true) + + evm := NewEVM(diffBlockCtx(false), statedb, params.TestChainConfig, Config{ExtraEips: []int{3855}}) + evm.SetTxContext(TxContext{ + Origin: diffCaller, + GasPrice: uint256.NewInt(1), + }) + _, _, err := evm.Call(diffCaller, diffContractAddr, nil, NewGasBudget(100000), new(uint256.Int)) + if err != nil { + t.Fatalf("PUSH0 enabled via ExtraEips failed: %v", err) + } +} + +// runOne executes code at diffContractAddr with the given interpreter selection +// and returns the observable result. +func runOne(t testing.TB, cfg *params.ChainConfig, merged, useTableLoop bool, code, input []byte, gas uint64) diffResult { + t.Helper() + statedb := newDiffState(t) + statedb.SetCode(diffContractAddr, code, tracing.CodeChangeUnspecified) + statedb.Finalise(true) + + evm := NewEVM(diffBlockCtx(merged), statedb, cfg, Config{}) + evm.SetTxContext(TxContext{ + Origin: diffCaller, + GasPrice: uint256.NewInt(1), + BlobHashes: []common.Hash{{0xb1, 0x0b}}, + }) + evm.forceTableLoop = useTableLoop + + ret, leftOver, err := evm.Call(diffCaller, diffContractAddr, input, NewGasBudget(gas), new(uint256.Int)) + errStr := "" + if err != nil { + errStr = err.Error() + } + return diffResult{ + ret: ret, + gasLeft: leftOver.RegularGas, + errStr: errStr, + refund: statedb.GetRefund(), + root: statedb.IntermediateRoot(true), + logs: statedb.Logs(), + } +} + +func TestInterpreterDiff(t *testing.T) { + for _, fk := range diffForks { + for _, prog := range diffPrograms { + t.Run(fk.name+"/"+prog.name, func(t *testing.T) { + input := common.FromHex("0xdeadbeef00000000000000000000000000000000000000000000000000000042") + table := runOne(t, fk.cfg, fk.merged, true, prog.code, input, prog.gas) + gen := runOne(t, fk.cfg, fk.merged, false, prog.code, input, prog.gas) + if where, ok := gen.equal(table); !ok { + t.Fatalf("divergence in %s:\n table: ret=%x gas=%d err=%q refund=%d root=%x logs=%d\n gen: ret=%x gas=%d err=%q refund=%d root=%x logs=%d", + where, + table.ret, table.gasLeft, table.errStr, table.refund, table.root, len(table.logs), + gen.ret, gen.gasLeft, gen.errStr, gen.refund, gen.root, len(gen.logs)) + } + }) + } + } +} + +// FuzzInterpreterDiff fuzzes arbitrary bytecode + calldata + gas and asserts the +// generated dispatch matches the table-walking loop on every observable axis. +func FuzzInterpreterDiff(f *testing.F) { + for _, prog := range diffPrograms { + f.Add(prog.code, []byte{0x01, 0x02, 0x03, 0x04}, uint64(100000)) + } + // A couple of structurally-interesting seeds. + f.Add(asm(PUSH1, 0x00, JUMPDEST, PUSH1, 0x01, ADD, DUP1, PUSH1, 0xff, GT, PUSH1, 0x02, JUMPI, STOP), []byte{}, uint64(50000)) + f.Add(bytes.Repeat([]byte{byte(PUSH1), 0x01}, 64), []byte{}, uint64(100000)) + + f.Fuzz(func(t *testing.T, code, input []byte, gas uint64) { + if len(code) > 24576 { // max contract code size, keep cases realistic + return + } + if gas > 5_000_000 { + gas = 5_000_000 // bound execution time + } + for _, fk := range diffForks { + table := runOne(t, fk.cfg, fk.merged, true, code, input, gas) + gen := runOne(t, fk.cfg, fk.merged, false, code, input, gas) + if where, ok := gen.equal(table); !ok { + t.Fatalf("divergence in %s (fork %s): code=%x input=%x gas=%d\n table: ret=%x gas=%d err=%q refund=%d root=%x logs=%d\n gen: ret=%x gas=%d err=%q refund=%d root=%x logs=%d", + where, fk.name, code, input, gas, + table.ret, table.gasLeft, table.errStr, table.refund, table.root, len(table.logs), + gen.ret, gen.gasLeft, gen.errStr, gen.refund, gen.root, len(gen.logs)) + } + } + }) +} diff --git a/core/vm/interp_gen.go b/core/vm/interp_gen.go new file mode 100644 index 0000000000..a2b3c6ed71 --- /dev/null +++ b/core/vm/interp_gen.go @@ -0,0 +1,1995 @@ +// Code generated by core/vm/gen; DO NOT EDIT. + +package vm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/core/tracing" +) + +// execUntraced is the generated, tracing-free interpreter fast path. Hot, +// fork-stable opcodes are inlined with their static gas and stack bounds baked +// in. Fork-invariant cold ops (KECCAK256/MLOAD/MSTORE/MSTORE8) call their +// handler and gas functions directly by name. Everything fork-varying is +// dispatched through the active per-fork table in the default case. EVM.Run +// selects this path when no tracer is configured. +func (evm *EVM) execUntraced(scope *ScopeContext) (ret []byte, err error) { + var ( + contract = scope.Contract + mem = scope.Memory + stack = scope.Stack + table = evm.table + rules = evm.chainRules + isEIP4762 = rules.IsEIP4762 + pc = uint64(0) + res []byte + ) + _ = mem + _ = rules + _ = isEIP4762 + _ = table +mainLoop: + for { + if isEIP4762 && !contract.IsDeployment && !contract.IsSystemCall { + contractAddr := contract.Address() + consumed, wanted := evm.TxContext.AccessEvents.CodeChunksRangeGas(contractAddr, pc, 1, uint64(len(contract.Code)), false, contract.Gas.RegularGas) + contract.UseGas(GasCosts{RegularGas: consumed}, evm.Config.Tracer, tracing.GasChangeWitnessCodeChunk) + if consumed < wanted { + return nil, ErrOutOfGas + } + } + op := contract.GetOp(pc) + switch op { + case ADD: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Add(&x, y) + pc++ + continue mainLoop + + case MUL: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Mul(&x, y) + pc++ + continue mainLoop + + case SUB: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Sub(&x, y) + pc++ + continue mainLoop + + case DIV: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Div(&x, y) + pc++ + continue mainLoop + + case SDIV: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.SDiv(&x, y) + pc++ + continue mainLoop + + case MOD: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Mod(&x, y) + pc++ + continue mainLoop + + case SMOD: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.SMod(&x, y) + pc++ + continue mainLoop + + case ADDMOD: + if sLen := stack.len(); sLen < 3 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 3} + } + if contract.Gas.RegularGas < 8 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 8 + x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() + z.AddMod(&x, &y, z) + pc++ + continue mainLoop + + case MULMOD: + if sLen := stack.len(); sLen < 3 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 3} + } + if contract.Gas.RegularGas < 8 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 8 + x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek() + z.MulMod(&x, &y, z) + pc++ + continue mainLoop + + case SIGNEXTEND: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + back, num := scope.Stack.pop(), scope.Stack.peek() + num.ExtendSign(num, &back) + pc++ + continue mainLoop + + case LT: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + if x.Lt(y) { + y.SetOne() + } else { + y.Clear() + } + pc++ + continue mainLoop + + case GT: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + if x.Gt(y) { + y.SetOne() + } else { + y.Clear() + } + pc++ + continue mainLoop + + case SLT: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + if x.Slt(y) { + y.SetOne() + } else { + y.Clear() + } + pc++ + continue mainLoop + + case SGT: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + if x.Sgt(y) { + y.SetOne() + } else { + y.Clear() + } + pc++ + continue mainLoop + + case EQ: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + if x.Eq(y) { + y.SetOne() + } else { + y.Clear() + } + pc++ + continue mainLoop + + case ISZERO: + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x := scope.Stack.peek() + if x.IsZero() { + x.SetOne() + } else { + x.Clear() + } + pc++ + continue mainLoop + + case AND: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.And(&x, y) + pc++ + continue mainLoop + + case OR: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Or(&x, y) + pc++ + continue mainLoop + + case XOR: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x, y := scope.Stack.pop(), scope.Stack.peek() + y.Xor(&x, y) + pc++ + continue mainLoop + + case NOT: + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + x := scope.Stack.peek() + x.Not(x) + pc++ + continue mainLoop + + case BYTE: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + th, val := scope.Stack.pop(), scope.Stack.peek() + val.Byte(&th) + pc++ + continue mainLoop + + case SHL: + if rules.IsConstantinople { + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + shift, value := scope.Stack.pop(), scope.Stack.peek() + if shift.LtUint64(256) { + value.Lsh(value, uint(shift.Uint64())) + } else { + value.Clear() + } + pc++ + continue mainLoop + + } + res, err = opUndefined(&pc, evm, scope) + break mainLoop + case SHR: + if rules.IsConstantinople { + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + shift, value := scope.Stack.pop(), scope.Stack.peek() + if shift.LtUint64(256) { + value.Rsh(value, uint(shift.Uint64())) + } else { + value.Clear() + } + pc++ + continue mainLoop + + } + res, err = opUndefined(&pc, evm, scope) + break mainLoop + case SAR: + if rules.IsConstantinople { + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + shift, value := scope.Stack.pop(), scope.Stack.peek() + if shift.GtUint64(256) { + if value.Sign() >= 0 { + value.Clear() + } else { + + value.SetAllOne() + } + pc++ + continue mainLoop + } + n := uint(shift.Uint64()) + value.SRsh(value, n) + pc++ + continue mainLoop + + } + res, err = opUndefined(&pc, evm, scope) + break mainLoop + case CLZ: + if rules.IsOsaka { + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } + if contract.Gas.RegularGas < 5 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 5 + x := scope.Stack.peek() + x.SetUint64(256 - uint64(x.BitLen())) + pc++ + continue mainLoop + + } + res, err = opUndefined(&pc, evm, scope) + break mainLoop + case KECCAK256: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 30 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 30 + var memorySize uint64 + { + memSize, overflow := memoryKeccak256(stack) + if overflow { + return nil, ErrGasUintOverflow + } + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, ErrGasUintOverflow + } + } + var dynamicCost GasCosts + dynamicCost, err = gasKeccak256(evm, contract, stack, mem, memorySize) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) + } + if contract.Gas.RegularGas < dynamicCost.RegularGas { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= dynamicCost.RegularGas + if memorySize > 0 { + mem.Resize(memorySize) + } + res, err = opKeccak256(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + case POP: + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } + if contract.Gas.RegularGas < 2 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 2 + scope.Stack.pop() + pc++ + continue mainLoop + + case MLOAD: + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + var memorySize uint64 + { + memSize, overflow := memoryMLoad(stack) + if overflow { + return nil, ErrGasUintOverflow + } + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, ErrGasUintOverflow + } + } + var dynamicCost GasCosts + dynamicCost, err = gasMLoad(evm, contract, stack, mem, memorySize) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) + } + if contract.Gas.RegularGas < dynamicCost.RegularGas { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= dynamicCost.RegularGas + if memorySize > 0 { + mem.Resize(memorySize) + } + res, err = opMload(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + case MSTORE: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + var memorySize uint64 + { + memSize, overflow := memoryMStore(stack) + if overflow { + return nil, ErrGasUintOverflow + } + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, ErrGasUintOverflow + } + } + var dynamicCost GasCosts + dynamicCost, err = gasMStore(evm, contract, stack, mem, memorySize) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) + } + if contract.Gas.RegularGas < dynamicCost.RegularGas { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= dynamicCost.RegularGas + if memorySize > 0 { + mem.Resize(memorySize) + } + res, err = opMstore(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + case MSTORE8: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + var memorySize uint64 + { + memSize, overflow := memoryMStore8(stack) + if overflow { + return nil, ErrGasUintOverflow + } + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, ErrGasUintOverflow + } + } + var dynamicCost GasCosts + dynamicCost, err = gasMStore8(evm, contract, stack, mem, memorySize) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) + } + if contract.Gas.RegularGas < dynamicCost.RegularGas { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= dynamicCost.RegularGas + if memorySize > 0 { + mem.Resize(memorySize) + } + res, err = opMstore8(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + case JUMP: + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } + if contract.Gas.RegularGas < 8 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 8 + if evm.abort.Load() { + res, err = nil, errStopToken + break mainLoop + } + pos := scope.Stack.pop() + if !scope.Contract.validJumpdest(&pos) { + res, err = nil, ErrInvalidJump + break mainLoop + } + pc = pos.Uint64() - 1 + pc++ + continue mainLoop + + case JUMPI: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 10 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 10 + if evm.abort.Load() { + res, err = nil, errStopToken + break mainLoop + } + pos, cond := scope.Stack.pop(), scope.Stack.pop() + if !cond.IsZero() { + if !scope.Contract.validJumpdest(&pos) { + res, err = nil, ErrInvalidJump + break mainLoop + } + pc = pos.Uint64() - 1 + } + pc++ + continue mainLoop + + case PC: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 2 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 2 + scope.Stack.get().SetUint64(pc) + pc++ + continue mainLoop + + case MSIZE: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 2 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 2 + scope.Stack.get().SetUint64(uint64(scope.Memory.Len())) + pc++ + continue mainLoop + + case JUMPDEST: + if contract.Gas.RegularGas < 1 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 1 + pc++ + continue mainLoop + + case PUSH0: + if rules.IsShanghai { + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 2 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 2 + scope.Stack.get().Clear() + pc++ + continue mainLoop + + } + res, err = opUndefined(&pc, evm, scope) + break mainLoop + case PUSH1: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + var ( + codeLen = uint64(len(scope.Contract.Code)) + elem = scope.Stack.get() + ) + pc += 1 + if pc < codeLen { + elem.SetUint64(uint64(scope.Contract.Code[pc])) + } else { + elem.Clear() + } + pc++ + continue mainLoop + + case PUSH2: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + var ( + codeLen = uint64(len(scope.Contract.Code)) + elem = scope.Stack.get() + ) + if pc+2 < codeLen { + elem.SetBytes2(scope.Contract.Code[pc+1 : pc+3]) + } else if pc+1 < codeLen { + elem.SetUint64(uint64(scope.Contract.Code[pc+1]) << 8) + } else { + elem.Clear() + } + pc += 2 + pc++ + continue mainLoop + + case PUSH3: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+3) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 3 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 3 + pc++ + continue mainLoop + case PUSH4: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+4) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 4 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 4 + pc++ + continue mainLoop + case PUSH5: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+5) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 5 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 5 + pc++ + continue mainLoop + case PUSH6: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+6) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 6 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 6 + pc++ + continue mainLoop + case PUSH7: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+7) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 7 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 7 + pc++ + continue mainLoop + case PUSH8: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+8) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 8 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 8 + pc++ + continue mainLoop + case PUSH9: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+9) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 9 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 9 + pc++ + continue mainLoop + case PUSH10: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+10) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 10 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 10 + pc++ + continue mainLoop + case PUSH11: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+11) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 11 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 11 + pc++ + continue mainLoop + case PUSH12: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+12) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 12 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 12 + pc++ + continue mainLoop + case PUSH13: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+13) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 13 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 13 + pc++ + continue mainLoop + case PUSH14: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+14) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 14 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 14 + pc++ + continue mainLoop + case PUSH15: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+15) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 15 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 15 + pc++ + continue mainLoop + case PUSH16: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+16) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 16 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 16 + pc++ + continue mainLoop + case PUSH17: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+17) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 17 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 17 + pc++ + continue mainLoop + case PUSH18: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+18) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 18 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 18 + pc++ + continue mainLoop + case PUSH19: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+19) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 19 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 19 + pc++ + continue mainLoop + case PUSH20: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+20) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 20 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 20 + pc++ + continue mainLoop + case PUSH21: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+21) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 21 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 21 + pc++ + continue mainLoop + case PUSH22: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+22) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 22 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 22 + pc++ + continue mainLoop + case PUSH23: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+23) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 23 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 23 + pc++ + continue mainLoop + case PUSH24: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+24) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 24 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 24 + pc++ + continue mainLoop + case PUSH25: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+25) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 25 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 25 + pc++ + continue mainLoop + case PUSH26: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+26) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 26 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 26 + pc++ + continue mainLoop + case PUSH27: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+27) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 27 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 27 + pc++ + continue mainLoop + case PUSH28: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+28) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 28 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 28 + pc++ + continue mainLoop + case PUSH29: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+29) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 29 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 29 + pc++ + continue mainLoop + case PUSH30: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+30) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 30 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 30 + pc++ + continue mainLoop + case PUSH31: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+31) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 31 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 31 + pc++ + continue mainLoop + case PUSH32: + if sLen := stack.len(); sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + if isEIP4762 { + res, err = table[op].execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + codeLen := len(scope.Contract.Code) + start := min(codeLen, int(pc+1)) + end := min(codeLen, start+32) + a := scope.Stack.get() + a.SetBytes(scope.Contract.Code[start:end]) + if missing := 32 - (end - start); missing > 0 { + a.Lsh(a, uint(8*missing)) + } + pc += 32 + pc++ + continue mainLoop + case DUP1: + if sLen := stack.len(); sLen < 1 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 1} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(1) + pc++ + continue mainLoop + case DUP2: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(2) + pc++ + continue mainLoop + case DUP3: + if sLen := stack.len(); sLen < 3 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 3} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(3) + pc++ + continue mainLoop + case DUP4: + if sLen := stack.len(); sLen < 4 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 4} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(4) + pc++ + continue mainLoop + case DUP5: + if sLen := stack.len(); sLen < 5 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 5} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(5) + pc++ + continue mainLoop + case DUP6: + if sLen := stack.len(); sLen < 6 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 6} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(6) + pc++ + continue mainLoop + case DUP7: + if sLen := stack.len(); sLen < 7 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 7} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(7) + pc++ + continue mainLoop + case DUP8: + if sLen := stack.len(); sLen < 8 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 8} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(8) + pc++ + continue mainLoop + case DUP9: + if sLen := stack.len(); sLen < 9 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 9} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(9) + pc++ + continue mainLoop + case DUP10: + if sLen := stack.len(); sLen < 10 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 10} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(10) + pc++ + continue mainLoop + case DUP11: + if sLen := stack.len(); sLen < 11 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 11} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(11) + pc++ + continue mainLoop + case DUP12: + if sLen := stack.len(); sLen < 12 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 12} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(12) + pc++ + continue mainLoop + case DUP13: + if sLen := stack.len(); sLen < 13 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 13} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(13) + pc++ + continue mainLoop + case DUP14: + if sLen := stack.len(); sLen < 14 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 14} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(14) + pc++ + continue mainLoop + case DUP15: + if sLen := stack.len(); sLen < 15 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 15} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(15) + pc++ + continue mainLoop + case DUP16: + if sLen := stack.len(); sLen < 16 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 16} + } else if sLen > 1023 { + return nil, &ErrStackOverflow{stackLen: sLen, limit: 1023} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.dup(16) + pc++ + continue mainLoop + case SWAP1: + if sLen := stack.len(); sLen < 2 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 2} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap1() + pc++ + continue mainLoop + + case SWAP2: + if sLen := stack.len(); sLen < 3 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 3} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap2() + pc++ + continue mainLoop + + case SWAP3: + if sLen := stack.len(); sLen < 4 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 4} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap3() + pc++ + continue mainLoop + + case SWAP4: + if sLen := stack.len(); sLen < 5 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 5} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap4() + pc++ + continue mainLoop + + case SWAP5: + if sLen := stack.len(); sLen < 6 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 6} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap5() + pc++ + continue mainLoop + + case SWAP6: + if sLen := stack.len(); sLen < 7 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 7} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap6() + pc++ + continue mainLoop + + case SWAP7: + if sLen := stack.len(); sLen < 8 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 8} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap7() + pc++ + continue mainLoop + + case SWAP8: + if sLen := stack.len(); sLen < 9 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 9} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap8() + pc++ + continue mainLoop + + case SWAP9: + if sLen := stack.len(); sLen < 10 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 10} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap9() + pc++ + continue mainLoop + + case SWAP10: + if sLen := stack.len(); sLen < 11 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 11} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap10() + pc++ + continue mainLoop + + case SWAP11: + if sLen := stack.len(); sLen < 12 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 12} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap11() + pc++ + continue mainLoop + + case SWAP12: + if sLen := stack.len(); sLen < 13 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 13} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap12() + pc++ + continue mainLoop + + case SWAP13: + if sLen := stack.len(); sLen < 14 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 14} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap13() + pc++ + continue mainLoop + + case SWAP14: + if sLen := stack.len(); sLen < 15 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 15} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap14() + pc++ + continue mainLoop + + case SWAP15: + if sLen := stack.len(); sLen < 16 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 16} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap15() + pc++ + continue mainLoop + + case SWAP16: + if sLen := stack.len(); sLen < 17 { + return nil, &ErrStackUnderflow{stackLen: sLen, required: 17} + } + if contract.Gas.RegularGas < 3 { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= 3 + scope.Stack.swap16() + pc++ + continue mainLoop + + default: + operation := table[op] + if sLen := stack.len(); sLen < operation.minStack { + return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack} + } else if sLen > operation.maxStack { + return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack} + } + cost := operation.constantGas + if contract.Gas.RegularGas < cost { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= cost + var memorySize uint64 + if operation.dynamicGas != nil { + if operation.memorySize != nil { + memSize, overflow := operation.memorySize(stack) + if overflow { + return nil, ErrGasUintOverflow + } + if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow { + return nil, ErrGasUintOverflow + } + } + var dynamicCost GasCosts + dynamicCost, err = operation.dynamicGas(evm, contract, stack, mem, memorySize) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrOutOfGas, err) + } + if contract.Gas.RegularGas < dynamicCost.RegularGas { + return nil, ErrOutOfGas + } + contract.Gas.RegularGas -= dynamicCost.RegularGas + } + if memorySize > 0 { + mem.Resize(memorySize) + } + res, err = operation.execute(&pc, evm, scope) + if err != nil { + break mainLoop + } + pc++ + continue mainLoop + } + } + if err == errStopToken { + err = nil + } + return res, err +} diff --git a/core/vm/interp_gen_test.go b/core/vm/interp_gen_test.go new file mode 100644 index 0000000000..83b4bb1ed9 --- /dev/null +++ b/core/vm/interp_gen_test.go @@ -0,0 +1,51 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package vm + +import ( + "os" + "os/exec" + "path/filepath" + "testing" +) + +// TestGeneratedDispatchUpToDate asserts that the committed interp_gen.go matches +// what `go generate` (core/vm/gen) produces from the current opcode/gas/fork +// definitions. It is the CI guard against hand-edits to the generated file and +// against the generator drifting from the committed output. +func TestGeneratedDispatchUpToDate(t *testing.T) { + if testing.Short() { + t.Skip("skipping generator round-trip in -short mode") + } + tmp := filepath.Join(t.TempDir(), "interp_gen.go") + cmd := exec.Command("go", "run", "./gen") + cmd.Env = append(os.Environ(), "INTERP_GEN_OUT="+tmp) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("running generator: %v\n%s", err, out) + } + got, err := os.ReadFile(tmp) + if err != nil { + t.Fatalf("reading regenerated output: %v", err) + } + want, err := os.ReadFile("interp_gen.go") + if err != nil { + t.Fatalf("reading committed interp_gen.go: %v", err) + } + if string(got) != string(want) { + t.Fatalf("interp_gen.go is out of date; run `go generate ./core/vm/...` and commit the result") + } +} diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index c2dfe3769c..4c0dd799dc 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -92,6 +92,9 @@ func (ctx *ScopeContext) ContractCode() []byte { // It's important to note that any errors returned by the interpreter should be // considered a revert-and-consume-all-gas operation except for // ErrExecutionReverted which means revert-and-keep-gas-left. +// +// Run sets up the per-call scope and picks the interpreter loop. Without a +// tracer it runs the generated execUntraced, otherwise execTraced. func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) { // Increment the call depth which is restricted to 1024 evm.depth++ @@ -112,17 +115,48 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte if len(contract.Code) == 0 { return nil, nil } + contract.Input = input + mem := NewMemory() // bound memory + stack := evm.arena.stack() // local stack + scope := &ScopeContext{ + Memory: mem, + Stack: stack, + Contract: contract, + } + + // The exit hooks in execTraced still need the stack and memory, so this + // teardown must run after them. Defers in execTraced fire before this one, + // so the ordering holds. + defer func() { + stack.release() + mem.Free() + }() + + // The generated fast path bakes its fork gates at generate time, so it + // cannot see table changes made by ExtraEips. Those configs run the table + // loop, as do tracing and the differential test via forceTableLoop. + if evm.forceTableLoop || len(evm.Config.ExtraEips) > 0 || evm.Config.Tracer != nil { + return evm.execTraced(scope) + } + return evm.execUntraced(scope) +} + +// execTraced is the old table-walking interpreter loop. It dispatches every +// opcode through the per-fork operation table and emits the tracing hooks +// exactly as before. It stays hand-written so trace output does not change. +// +// The differential test also runs this loop without a tracer and compares it +// against the generated execUntraced, which keeps the two from drifting +// apart. +func (evm *EVM) execTraced(scope *ScopeContext) (ret []byte, err error) { var ( op OpCode // current opcode jumpTable *JumpTable = evm.table - mem = NewMemory() // bound memory - stack = evm.arena.stack() // local stack - callContext = &ScopeContext{ - Memory: mem, - Stack: stack, - Contract: contract, - } + mem = scope.Memory + stack = scope.Stack + contract = scope.Contract + callContext = scope // For optimisation reason we're using uint64 as the program counter. // It's theoretically possible to go above 2^64. The YP defines the PC // to be uint256. Practically much less so feasible. @@ -136,15 +170,6 @@ func (evm *EVM) Run(contract *Contract, input []byte, readOnly bool) (ret []byte debug = evm.Config.Tracer != nil isEIP4762 = evm.chainRules.IsEIP4762 ) - // Don't move this deferred function, it's placed before the OnOpcode-deferred method, - // so that it gets executed _after_: the OnOpcode needs the stacks before - // they are returned to the pools - defer func() { - stack.release() - mem.Free() - }() - contract.Input = input - if debug { defer func() { // this deferred method handles exit-with-error if err == nil { diff --git a/core/vm/runtime/evm_bench_test.go b/core/vm/runtime/evm_bench_test.go new file mode 100644 index 0000000000..31a265c1f6 --- /dev/null +++ b/core/vm/runtime/evm_bench_test.go @@ -0,0 +1,106 @@ +// Copyright 2026 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// Whole-contract interpreter benchmarks from the evm-bench suite +// (github.com/ziyadedher/evm-bench). Each contract exposes a Benchmark() +// entrypoint (selector 0x30627b7c) that performs the whole workload internally +// (e.g. 5000 mints), so the benchmark is a single Call per iteration. These +// complement the opcode/loop micro-benchmarks in runtime_test.go with realistic +// workloads (a ray tracer, ERC-20 transfer/mint/approval, a hashing loop) and +// use the same names as evm-bench / gevm for cross-comparison. +// +// Run: go test ./core/vm/runtime/ -run '^$' -bench 'Benchmark(Snailtracer|TenThousandHashes|ERC20)' -benchmem -count=10 +// Compare A/B: ./core/vm/runtime/testdata/evm-bench/compare.sh +// +// The committed runtime bytecode in testdata/*.hex is regenerated by +// testdata/evm-bench/gen.sh (solc via docker). See that script for provenance +// and exact compiler versions. + +package runtime + +import ( + _ "embed" + "encoding/hex" + "strings" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/holiman/uint256" +) + +//go:embed testdata/snailtracer.hex +var benchSnailtracerHex string + +//go:embed testdata/tenthousandhashes.hex +var benchTenThousandHashesHex string + +//go:embed testdata/erc20transfer.hex +var benchERC20TransferHex string + +//go:embed testdata/erc20mint.hex +var benchERC20MintHex string + +//go:embed testdata/erc20approval.hex +var benchERC20ApprovalHex string + +// benchmarkSelector is keccak256("Benchmark()")[:4], the evm-bench entrypoint. +var benchmarkSelector = []byte{0x30, 0x62, 0x7b, 0x7c} + +func benchHexToBytes(s string) []byte { + b, err := hex.DecodeString(strings.TrimSpace(s)) + if err != nil { + panic(err) + } + return b +} + +// runEVMBench deploys the contract's runtime bytecode and repeatedly invokes its +// Benchmark() entrypoint. The contract's constructor is not run, so storage +// starts empty. The evm-bench contracts set up whatever state they need inside +// Benchmark(), which matches the work the evm-bench harness measures. +func runEVMBench(b *testing.B, codeHex string) { + code := benchHexToBytes(codeHex) + statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + if err != nil { + b.Fatal(err) + } + contract := common.BytesToAddress([]byte{0x10}) + caller := common.BytesToAddress([]byte{0x01}) + statedb.CreateAccount(caller) + statedb.AddBalance(caller, uint256.NewInt(1_000_000_000), tracing.BalanceChangeUnspecified) + statedb.SetCode(contract, code, tracing.CodeChangeUnspecified) + + cfg := &Config{Origin: caller, State: statedb, GasLimit: 10_000_000_000} + + // Sanity: the workload must run to completion, not revert/out-of-gas. + if _, _, err := Call(contract, benchmarkSelector, cfg); err != nil { + b.Fatalf("Benchmark() did not complete: %v", err) + } + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + Call(contract, benchmarkSelector, cfg) + } +} + +func BenchmarkSnailtracer(b *testing.B) { runEVMBench(b, benchSnailtracerHex) } +func BenchmarkTenThousandHashes(b *testing.B) { runEVMBench(b, benchTenThousandHashesHex) } +func BenchmarkERC20Transfer(b *testing.B) { runEVMBench(b, benchERC20TransferHex) } +func BenchmarkERC20Mint(b *testing.B) { runEVMBench(b, benchERC20MintHex) } +func BenchmarkERC20ApprovalTransfer(b *testing.B) { runEVMBench(b, benchERC20ApprovalHex) } diff --git a/core/vm/runtime/testdata/erc20approval.hex b/core/vm/runtime/testdata/erc20approval.hex new file mode 100644 index 0000000000..0700413573 --- /dev/null +++ b/core/vm/runtime/testdata/erc20approval.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461013857806370a082311461014b57806395d89b4114610174578063a457c2d71461017c578063a9059cbb1461018f578063dd62ed3e146101a257600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461010c57806330627b7c1461011f578063313ce56714610129575b600080fd5b6100c16101b5565b6040516100ce91906108ca565b60405180910390f35b6100ea6100e5366004610934565b610247565b60405190151581526020016100ce565b6002545b6040519081526020016100ce565b6100ea61011a36600461095e565b610261565b610127610285565b005b604051601281526020016100ce565b6100ea610146366004610934565b6103e4565b6100fe61015936600461099a565b6001600160a01b031660009081526020819052604090205490565b6100c1610406565b6100ea61018a366004610934565b610415565b6100ea61019d366004610934565b610490565b6100fe6101b03660046109bc565b61049e565b6060600380546101c4906109ef565b80601f01602080910402602001604051908101604052809291908181526020018280546101f0906109ef565b801561023d5780601f106102125761010080835404028352916020019161023d565b820191906000526020600020905b81548152906001019060200180831161022057829003601f168201915b5050505050905090565b6000336102558185856104c9565b60019150505b92915050565b60003361026f8582856105ed565b61027a858585610667565b506001949350505050565b6102a8336102956012600a610b23565b6102a390633b9aca00610b32565b61080b565b60015b6103e88110156103e1576102bf333361049e565b156103115760405162461bcd60e51b815260206004820152601b60248201527f6e6f6e2d7a65726f20616c6c6f77616e636520746f207374617274000000000060448201526064015b60405180910390fd5b61031b3382610247565b5080610327333361049e565b1461036c5760405162461bcd60e51b81526020600482015260156024820152746469646e2774206769766520616c6c6f77616e636560581b6044820152606401610308565b610377333383610261565b50610382333361049e565b156103cf5760405162461bcd60e51b815260206004820152601960248201527f6e6f6e2d7a65726f20616c6c6f77616e636520746f20656e64000000000000006044820152606401610308565b806103d981610b49565b9150506102ab565b50565b6000336102558185856103f7838361049e565b6104019190610b62565b6104c9565b6060600480546101c4906109ef565b60003381610423828661049e565b9050838110156104835760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610308565b61027a82868684036104c9565b600033610255818585610667565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b03831661052b5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610308565b6001600160a01b03821661058c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610308565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006105f9848461049e565b9050600019811461066157818110156106545760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610308565b61066184848484036104c9565b50505050565b6001600160a01b0383166106cb5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610308565b6001600160a01b03821661072d5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610308565b6001600160a01b038316600090815260208190526040902054818110156107a55760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610308565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610661565b6001600160a01b0382166108615760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610308565b80600260008282546108739190610b62565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b818110156108f7578581018301518582016040015282016108db565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461092f57600080fd5b919050565b6000806040838503121561094757600080fd5b61095083610918565b946020939093013593505050565b60008060006060848603121561097357600080fd5b61097c84610918565b925061098a60208501610918565b9150604084013590509250925092565b6000602082840312156109ac57600080fd5b6109b582610918565b9392505050565b600080604083850312156109cf57600080fd5b6109d883610918565b91506109e660208401610918565b90509250929050565b600181811c90821680610a0357607f821691505b602082108103610a2357634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115610a7a578160001904821115610a6057610a60610a29565b80851615610a6d57918102915b93841c9390800290610a44565b509250929050565b600082610a915750600161025b565b81610a9e5750600061025b565b8160018114610ab45760028114610abe57610ada565b600191505061025b565b60ff841115610acf57610acf610a29565b50506001821b61025b565b5060208310610133831016604e8410600b8410161715610afd575081810a61025b565b610b078383610a3f565b8060001904821115610b1b57610b1b610a29565b029392505050565b60006109b560ff841683610a82565b808202811582820484141761025b5761025b610a29565b600060018201610b5b57610b5b610a29565b5060010190565b8082018082111561025b5761025b610a2956fea2646970667358221220c33117c9d08b3ca92e5865f957cca670ed19c2990ba76736dd40b693ae62098064736f6c63430008110033 \ No newline at end of file diff --git a/core/vm/runtime/testdata/erc20mint.hex b/core/vm/runtime/testdata/erc20mint.hex new file mode 100644 index 0000000000..ed06ae6b14 --- /dev/null +++ b/core/vm/runtime/testdata/erc20mint.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461013857806370a082311461014b57806395d89b4114610174578063a457c2d71461017c578063a9059cbb1461018f578063dd62ed3e146101a257600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461010c57806330627b7c1461011f578063313ce56714610129575b600080fd5b6100c16101b5565b6040516100ce919061079c565b60405180910390f35b6100ea6100e5366004610806565b610247565b60405190151581526020016100ce565b6002545b6040519081526020016100ce565b6100ea61011a366004610830565b610261565b610127610285565b005b604051601281526020016100ce565b6100ea610146366004610806565b6102b1565b6100fe61015936600461086c565b6001600160a01b031660009081526020819052604090205490565b6100c16102d3565b6100ea61018a366004610806565b6102e2565b6100ea61019d366004610806565b610362565b6100fe6101b036600461088e565b610370565b6060600380546101c4906108c1565b80601f01602080910402602001604051908101604052809291908181526020018280546101f0906108c1565b801561023d5780601f106102125761010080835404028352916020019161023d565b820191906000526020600020905b81548152906001019060200180831161022057829003601f168201915b5050505050905090565b60003361025581858561039b565b60019150505b92915050565b60003361026f8582856104bf565b61027a858585610539565b506001949350505050565b60005b6113888110156102ae5761029c33826106dd565b806102a681610911565b915050610288565b50565b6000336102558185856102c48383610370565b6102ce919061092a565b61039b565b6060600480546101c4906108c1565b600033816102f08286610370565b9050838110156103555760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b61027a828686840361039b565b600033610255818585610539565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103fd5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161034c565b6001600160a01b03821661045e5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161034c565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006104cb8484610370565b9050600019811461053357818110156105265760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161034c565b610533848484840361039b565b50505050565b6001600160a01b03831661059d5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161034c565b6001600160a01b0382166105ff5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161034c565b6001600160a01b038316600090815260208190526040902054818110156106775760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161034c565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610533565b6001600160a01b0382166107335760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161034c565b8060026000828254610745919061092a565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b818110156107c9578581018301518582016040015282016107ad565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461080157600080fd5b919050565b6000806040838503121561081957600080fd5b610822836107ea565b946020939093013593505050565b60008060006060848603121561084557600080fd5b61084e846107ea565b925061085c602085016107ea565b9150604084013590509250925092565b60006020828403121561087e57600080fd5b610887826107ea565b9392505050565b600080604083850312156108a157600080fd5b6108aa836107ea565b91506108b8602084016107ea565b90509250929050565b600181811c908216806108d557607f821691505b6020821081036108f557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600060018201610923576109236108fb565b5060010190565b8082018082111561025b5761025b6108fb56fea2646970667358221220fa5dc8b94ca8266a9f37b573a3ac8993380ce8da74caa7732abd6e7ed31cd37e64736f6c63430008110033 \ No newline at end of file diff --git a/core/vm/runtime/testdata/erc20transfer.hex b/core/vm/runtime/testdata/erc20transfer.hex new file mode 100644 index 0000000000..cc01470901 --- /dev/null +++ b/core/vm/runtime/testdata/erc20transfer.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50600436106100b45760003560e01c80633950935111610071578063395093511461013857806370a082311461014b57806395d89b4114610174578063a457c2d71461017c578063a9059cbb1461018f578063dd62ed3e146101a257600080fd5b806306fdde03146100b9578063095ea7b3146100d757806318160ddd146100fa57806323b872dd1461010c57806330627b7c1461011f578063313ce56714610129575b600080fd5b6100c16101b5565b6040516100ce91906107bf565b60405180910390f35b6100ea6100e5366004610829565b610247565b60405190151581526020016100ce565b6002545b6040519081526020016100ce565b6100ea61011a366004610853565b610261565b610127610285565b005b604051601281526020016100ce565b6100ea610146366004610829565b6102d4565b6100fe61015936600461088f565b6001600160a01b031660009081526020819052604090205490565b6100c16102f6565b6100ea61018a366004610829565b610305565b6100ea61019d366004610829565b610385565b6100fe6101b03660046108b1565b610393565b6060600380546101c4906108e4565b80601f01602080910402602001604051908101604052809291908181526020018280546101f0906108e4565b801561023d5780601f106102125761010080835404028352916020019161023d565b820191906000526020600020905b81548152906001019060200180831161022057829003601f168201915b5050505050905090565b6000336102558185856103be565b60019150505b92915050565b60003361026f8582856104e2565b61027a85858561055c565b506001949350505050565b6102a6336102956012600a610a18565b6102a190612710610a27565b610700565b60005b6113888110156102d1576102be600182610385565b50806102c981610a3e565b9150506102a9565b50565b6000336102558185856102e78383610393565b6102f19190610a57565b6103be565b6060600480546101c4906108e4565b600033816103138286610393565b9050838110156103785760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b61027a82868684036103be565b60003361025581858561055c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166104205760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161036f565b6001600160a01b0382166104815760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161036f565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006104ee8484610393565b9050600019811461055657818110156105495760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161036f565b61055684848484036103be565b50505050565b6001600160a01b0383166105c05760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161036f565b6001600160a01b0382166106225760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161036f565b6001600160a01b0383166000908152602081905260409020548181101561069a5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161036f565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610556565b6001600160a01b0382166107565760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161036f565b80600260008282546107689190610a57565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b600060208083528351808285015260005b818110156107ec578581018301518582016040015282016107d0565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461082457600080fd5b919050565b6000806040838503121561083c57600080fd5b6108458361080d565b946020939093013593505050565b60008060006060848603121561086857600080fd5b6108718461080d565b925061087f6020850161080d565b9150604084013590509250925092565b6000602082840312156108a157600080fd5b6108aa8261080d565b9392505050565b600080604083850312156108c457600080fd5b6108cd8361080d565b91506108db6020840161080d565b90509250929050565b600181811c908216806108f857607f821691505b60208210810361091857634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111561096f5781600019048211156109555761095561091e565b8085161561096257918102915b93841c9390800290610939565b509250929050565b6000826109865750600161025b565b816109935750600061025b565b81600181146109a957600281146109b3576109cf565b600191505061025b565b60ff8411156109c4576109c461091e565b50506001821b61025b565b5060208310610133831016604e8410600b84101617156109f2575081810a61025b565b6109fc8383610934565b8060001904821115610a1057610a1061091e565b029392505050565b60006108aa60ff841683610977565b808202811582820484141761025b5761025b61091e565b600060018201610a5057610a5061091e565b5060010190565b8082018082111561025b5761025b61091e56fea26469706673582212200111a8455f1c6aba553f7e7a1fe413d6a44eececa7e001df4c190aef76f39b5864736f6c63430008110033 \ No newline at end of file diff --git a/core/vm/runtime/testdata/evm-bench/README.md b/core/vm/runtime/testdata/evm-bench/README.md new file mode 100644 index 0000000000..cc5ec51620 --- /dev/null +++ b/core/vm/runtime/testdata/evm-bench/README.md @@ -0,0 +1,56 @@ +# evm-bench whole-contract interpreter benchmarks + +Realistic-workload benchmarks for the `core/vm` interpreter, driven by contracts +from the [evm-bench](https://github.com/ziyadedher/evm-bench) suite. They +complement the opcode/loop micro-benchmarks in `runtime_test.go`. + +- Benchmark code: [`core/vm/runtime/evm_bench_test.go`](../../evm_bench_test.go) +- Runtime bytecode: `core/vm/runtime/testdata/*.hex` (committed, `//go:embed`-ed) + +Each contract exposes a `Benchmark()` entrypoint (selector `0x30627b7c`) that +performs the entire workload internally, so one `runtime.Call` = one full +workload: + +| benchmark | workload | +|---|---| +| `Snailtracer` | a ray tracer, compute and memory heavy | +| `ERC20Transfer` | ERC-20 transfers in a loop | +| `ERC20Mint` | 5000 `_mint`s (SSTORE/keccak/LOG-heavy) | +| `ERC20ApprovalTransfer` | 1000 approve+transferFrom cycles | +| `TenThousandHashes` | a keccak loop | + +## Running + +```sh +# all of them, current build +go test ./core/vm/runtime/ -run '^$' -bench 'Benchmark(Snailtracer|TenThousandHashes|ERC20)' -benchmem -count=10 + +# A/B vs a baseline (default master), with benchstat +core/vm/runtime/testdata/evm-bench/compare.sh # vs master +core/vm/runtime/testdata/evm-bench/compare.sh HEAD~1 8 # vs another ref, count=8 +``` + +`compare.sh` benches the current working tree and a baseline ref (checked out in +a throwaway worktree with this suite copied in, so it works whether or not the +interpreter change is committed) and runs `benchstat`. + +## Regenerating the bytecode + +[`gen.sh`](gen.sh) regenerates the ERC-20 and ten-thousand-hashes `.hex` from +the evm-bench Solidity sources (solc 0.8.17 via docker). These contracts set up +their state inside `Benchmark()`, so the runtime bytecode is callable directly. + +`snailtracer.hex` is **vendored, not regenerated by gen.sh**: evm-bench's +`SnailTracer` initializes its scene in the constructor, so its runtime bytecode +(no constructor) renders an empty scene. The committed file is a runtime-callable +build (scene encoded in code) vendored from +[Giulio2002/gevm](https://github.com/Giulio2002/gevm)'s gethbench testdata. + +The `.hex` files are committed, so running the benchmarks needs no toolchain. + +## Notes + +- The contract constructor is not executed, so storage starts empty. The + evm-bench contracts set up whatever state they need inside `Benchmark()`, + matching the work the evm-bench harness measures. +- Names match evm-bench / gevm for cross-comparison. diff --git a/core/vm/runtime/testdata/evm-bench/compare.sh b/core/vm/runtime/testdata/evm-bench/compare.sh new file mode 100755 index 0000000000..7ae00603a1 --- /dev/null +++ b/core/vm/runtime/testdata/evm-bench/compare.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# A/B benchmark of the codegen interpreter vs a baseline: runs the evm-bench +# contract workloads (core/vm/runtime/evm_bench_test.go) plus the synthetic +# dispatch loops (BenchmarkSimpleLoop/loop*) on the current working tree and on +# a baseline ref, then benchstats them. +# +# The baseline is checked out in a throwaway git worktree and this suite is +# copied into it, so the comparison works whether or not the interpreter changes +# are committed yet. Requires: go, git, and benchstat +# (go install golang.org/x/perf/cmd/benchstat@latest). +# +# Usage: core/vm/runtime/testdata/evm-bench/compare.sh [baseref] [count] +# baseref defaults to "master", count to 10. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../../../../.." && pwd)" # repo root +cd "$ROOT" +BASEREF="${1:-master}" +COUNT="${2:-10}" +# Each benchmark runs a FIXED iteration count instead of the default 1s of +# benchtime. With time-based benchtime the faster side runs more iterations, +# so one-time costs (map growth, pool warmup) amortize over a different N and +# B/op picks up phantom deltas, and GC pacing can do the same to sec/op. Fixed +# N makes both sides do identical work. The counts target roughly one second +# per count on a fast box. Each entry is pattern:iterations, and SimpleLoop +# needs the /^loop element to select only its loop variants (go test splits +# -bench on / and benchmarks without sub-benchmarks cannot match a two-element +# pattern, so the loops run as their own invocation anyway). +BENCHES=( + '^BenchmarkSnailtracer$:20x' + '^BenchmarkTenThousandHashes$:200x' + '^BenchmarkERC20Transfer$:100x' + '^BenchmarkERC20Mint$:150x' + '^BenchmarkERC20ApprovalTransfer$:120x' + '^BenchmarkSimpleLoop$/^loop:7x' +) +NEW="$(mktemp)"; OLD="$(mktemp)" + +run_suite() { # run_suite + local dir="$1" out="$2" entry pat n + for entry in "${BENCHES[@]}"; do + pat="${entry%:*}" + n="${entry##*:}" + ( cd "$dir" && go test ./core/vm/runtime/ -run '^$' -bench "$pat" -benchmem -benchtime "$n" -count="$COUNT" ) | tee -a "$out" + done +} + +echo "==> current working tree" +run_suite "$ROOT" "$NEW" + +echo "==> baseline: $BASEREF (throwaway worktree, suite copied in)" +WT="$(mktemp -d)" +git worktree add --quiet --detach "$WT" "$BASEREF" +cp core/vm/runtime/evm_bench_test.go "$WT/core/vm/runtime/" +mkdir -p "$WT/core/vm/runtime/testdata" +cp core/vm/runtime/testdata/*.hex "$WT/core/vm/runtime/testdata/" +run_suite "$WT" "$OLD" +git worktree remove --force "$WT" + +echo "==> benchstat: $BASEREF (left) vs working tree (right)" +benchstat "$OLD" "$NEW" diff --git a/core/vm/runtime/testdata/evm-bench/gen.sh b/core/vm/runtime/testdata/evm-bench/gen.sh new file mode 100755 index 0000000000..6b69134823 --- /dev/null +++ b/core/vm/runtime/testdata/evm-bench/gen.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Regenerates core/vm/runtime/testdata/*.hex, the runtime bytecode for the +# whole-contract interpreter benchmarks (see core/vm/runtime/evm_bench_test.go). +# +# Source: the evm-bench suite (github.com/ziyadedher/evm-bench). Each contract +# exposes Benchmark() (selector 0x30627b7c) which performs the whole workload. +# Compiled with solc via docker (no local solc needed). solc versions match each +# contract's pragma: 0.8.17 for the ERC-20/hashing contracts, 0.4.26 for the +# legacy SnailTracer. +# +# Usage: core/vm/runtime/testdata/evm-bench/gen.sh +set -euo pipefail + +TD="$(cd "$(dirname "$0")/.." && pwd)" # .../core/vm/runtime/testdata +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +git clone --depth 1 https://github.com/ziyadedher/evm-bench "$WORK/evm-bench" >/dev/null 2>&1 +B="$WORK/evm-bench/benchmarks" + +# ERC-20 (transfer/mint/approval) + ten-thousand-hashes: pragma ^0.8.17. These +# set up all their state inside Benchmark(), so the runtime bytecode is callable +# directly with no constructor, which is how the benchmark drives them. +docker run --rm -v "$B":/src -w /src ethereum/solc:0.8.17 \ + --optimize --bin-runtime --overwrite -o /src/out \ + erc20/transfer/ERC20Transfer.sol \ + erc20/mint/ERC20Mint.sol \ + erc20/approval-transfer/ERC20ApprovalTransfer.sol \ + ten-thousand-hashes/TenThousandHashes.sol + +cp "$B/out/TenThousandHashes.bin-runtime" "$TD/tenthousandhashes.hex" +cp "$B/out/ERC20Transfer.bin-runtime" "$TD/erc20transfer.hex" +cp "$B/out/ERC20Mint.bin-runtime" "$TD/erc20mint.hex" +cp "$B/out/ERC20ApprovalTransfer.bin-runtime" "$TD/erc20approval.hex" + +# NOTE: snailtracer.hex is not regenerated here. evm-bench's SnailTracer +# initializes its scene in the constructor, so its --bin-runtime (no constructor) +# renders an empty scene. The committed snailtracer.hex is a runtime-callable +# build (scene encoded in code, Benchmark() self-contained) vendored from +# Giulio2002/gevm's gethbench testdata. Leave it as-is. + +echo "regenerated (snailtracer.hex left vendored):" +ls -l "$TD"/*.hex diff --git a/core/vm/runtime/testdata/snailtracer.hex b/core/vm/runtime/testdata/snailtracer.hex new file mode 100644 index 0000000000..9546187294 --- /dev/null +++ b/core/vm/runtime/testdata/snailtracer.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033 \ No newline at end of file diff --git a/core/vm/runtime/testdata/tenthousandhashes.hex b/core/vm/runtime/testdata/tenthousandhashes.hex new file mode 100644 index 0000000000..2892c1d984 --- /dev/null +++ b/core/vm/runtime/testdata/tenthousandhashes.hex @@ -0,0 +1 @@ +6080604052348015600f57600080fd5b506004361060285760003560e01c806330627b7c14602d575b600080fd5b60336035565b005b60005b614e20811015606a5760408051602081018390520160408051601f198184030190525280606381606d565b9150506038565b50565b600060018201608c57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220e499ea6d1ccd5fce28e8cd2507f6fef33f0d005543e04580f290bbd01df0655464736f6c63430008110033 \ No newline at end of file