mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 03:10:48 +00:00
core/vm: fixes due to rebase and removed hardcoded fork schedule in genspec
This commit is contained in:
parent
efb58fc526
commit
008e27ae8a
5 changed files with 90 additions and 63 deletions
|
|
@ -84,8 +84,8 @@ func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
|
||||||
return prior, ok
|
return prior, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// chargeRegularOnly deducts a regular-only cost.
|
// ChargeRegularOnly deducts a regular-only cost.
|
||||||
func (g *GasBudget) chargeRegularOnly(r uint64) error {
|
func (g *GasBudget) ChargeRegularOnly(r uint64) error {
|
||||||
if g.RegularGas < r {
|
if g.RegularGas < r {
|
||||||
return ErrOutOfGas
|
return ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func (g *generator) p(format string, args ...any) {
|
||||||
// parseHandlers parses instructions.go, eips.go, stack.go, gascosts.go and
|
// parseHandlers parses instructions.go, eips.go, stack.go, gascosts.go and
|
||||||
// interpreter.go. It returns the top-level opXxx handlers by name, the
|
// interpreter.go. It returns the top-level opXxx handlers by name, the
|
||||||
// //gen:inline *Stack helper methods, and the gas/memory helper functions
|
// //gen:inline *Stack helper methods, and the gas/memory helper functions
|
||||||
// (chargeRegularOnly, computeMemorySize, chargeDynamicGas) whose bodies are
|
// (ChargeRegularOnly, computeMemorySize, chargeDynamicGas) whose bodies are
|
||||||
// spliced into the generated dispatch (all by name).
|
// spliced into the generated dispatch (all by name).
|
||||||
func parseHandlers(vmDir string) (fset *token.FileSet, opcodeHandlers, stackHelpers, gasHelpers map[string]*ast.FuncDecl) {
|
func parseHandlers(vmDir string) (fset *token.FileSet, opcodeHandlers, stackHelpers, gasHelpers map[string]*ast.FuncDecl) {
|
||||||
fset = token.NewFileSet()
|
fset = token.NewFileSet()
|
||||||
|
|
@ -132,7 +132,7 @@ func parseHandlers(vmDir string) (fset *token.FileSet, opcodeHandlers, stackHelp
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case fn.Name.Name == "chargeRegularOnly" || fn.Name.Name == "computeMemorySize" || fn.Name.Name == "chargeDynamicGas" || fn.Name.Name == "chargeVerkleCodeChunkGas": // spliced gas/memory helpers
|
case fn.Name.Name == "ChargeRegularOnly" || fn.Name.Name == "computeMemorySize" || fn.Name.Name == "chargeDynamicGas" || fn.Name.Name == "chargeVerkleCodeChunkGas": // spliced gas/memory helpers
|
||||||
gasHelpers[fn.Name.Name] = fn
|
gasHelpers[fn.Name.Name] = fn
|
||||||
case fn.Recv == nil: // top-level opXxx handler
|
case fn.Recv == nil: // top-level opXxx handler
|
||||||
opcodeHandlers[fn.Name.Name] = fn
|
opcodeHandlers[fn.Name.Name] = fn
|
||||||
|
|
@ -627,7 +627,7 @@ func (g *generator) generateStackChecks(minExpr, maxExpr any, under, over bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateStaticGas returns the static-gas charge, spliced call-free from the
|
// generateStaticGas returns the static-gas charge, spliced call-free from the
|
||||||
// chargeRegularOnly body for amount: a constant on the inlined and
|
// ChargeRegularOnly body for amount: a constant on the inlined and
|
||||||
// direct-call paths, operation.constantGas in the table path. The receiver maps
|
// direct-call paths, operation.constantGas in the table path. The receiver maps
|
||||||
// to contract.Gas and the method's single uint64 parameter to amount, substituted
|
// to contract.Gas and the method's single uint64 parameter to amount, substituted
|
||||||
// textually on word boundaries (which cannot touch fields like RegularGas). Its
|
// textually on word boundaries (which cannot touch fields like RegularGas). Its
|
||||||
|
|
@ -635,13 +635,13 @@ func (g *generator) generateStackChecks(minExpr, maxExpr any, under, over bool)
|
||||||
// is dropped so the opcode falls through to its remaining steps (see
|
// is dropped so the opcode falls through to its remaining steps (see
|
||||||
// rewriteGasReturns).
|
// rewriteGasReturns).
|
||||||
func (g *generator) generateStaticGas(amount any) string {
|
func (g *generator) generateStaticGas(amount any) string {
|
||||||
fn := g.gasHelpers["chargeRegularOnly"]
|
fn := g.gasHelpers["ChargeRegularOnly"]
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
fatalf("no chargeRegularOnly gas helper to inline")
|
fatalf("no ChargeRegularOnly gas helper to inline")
|
||||||
}
|
}
|
||||||
names := paramNames(fn)
|
names := paramNames(fn)
|
||||||
if len(names) != 1 {
|
if len(names) != 1 {
|
||||||
fatalf("chargeRegularOnly takes %d params, want 1", len(names))
|
fatalf("ChargeRegularOnly takes %d params, want 1", len(names))
|
||||||
}
|
}
|
||||||
src := g.renderAst(fn.Body.List)
|
src := g.renderAst(fn.Body.List)
|
||||||
src = regexp.MustCompile(`\b`+recvName(fn)+`\b`).ReplaceAllString(src, "contract.Gas")
|
src = regexp.MustCompile(`\b`+recvName(fn)+`\b`).ReplaceAllString(src, "contract.Gas")
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file exposes the interpreter's opcode metadata to the code generator in
|
// This file exposes the interpreter's opcode metadata to the code generator in
|
||||||
|
|
@ -56,35 +58,13 @@ type GenFork struct {
|
||||||
Ops [256]GenOp
|
Ops [256]GenOp
|
||||||
}
|
}
|
||||||
|
|
||||||
// genForkOrder is the canonical fork progression for code generation, oldest to
|
// codegenSkippedForks are forks in geth's fork schedule that the generator does
|
||||||
// newest, each paired with the params.Rules field that activates it.
|
// not give a lane. Verkle/UBT is the only one: over its Shanghai base it adds no
|
||||||
//
|
// opcodes, it only swaps gas and execute functions on existing ones (enable4762),
|
||||||
// Petersburg is omitted: it shares Constantinople's opcode set and only changes
|
// which the generated switch picks up from the active table at runtime. Emitting
|
||||||
// SSTORE dynamic gas, which flows through the shared gas function. Verkle/UBT is
|
// a lane for it would trip the generator's fork-stability check (an inlined op's
|
||||||
// omitted: over its Shanghai base it adds no new opcodes (it only swaps gas and
|
// execute function is not allowed to vary by fork).
|
||||||
// execute on existing opcodes), which the generated switch picks up from the
|
var codegenSkippedForks = map[string]bool{"IsUBT": true}
|
||||||
// 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},
|
|
||||||
}
|
|
||||||
|
|
||||||
// genFnName returns the FuncForPC name of a jump-table function value with the
|
// genFnName returns the FuncForPC name of a jump-table function value with the
|
||||||
// package path stripped (e.g. "gasKeccak256"), or "" if nil. An aliased var
|
// package path stripped (e.g. "gasKeccak256"), or "" if nil. An aliased var
|
||||||
|
|
@ -108,28 +88,75 @@ func genFnName(fn any) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenForks returns per-fork opcode metadata for the interpreter code generator
|
// GenForks returns per-fork opcode metadata for the interpreter code generator
|
||||||
// (core/vm/gen). It is exported solely for that purpose.
|
// (core/vm/gen), one entry per fork that changes the opcode table, oldest to
|
||||||
|
// newest. It derives the progression from params.Rules and LookupInstructionSet
|
||||||
|
// (in params.Rules declaration order, which is chronological) so new forks are
|
||||||
|
// picked up without restating a list here.
|
||||||
func GenForks() []GenFork {
|
func GenForks() []GenFork {
|
||||||
out := make([]GenFork, len(genForkOrder))
|
// Frontier is always active and carries no rule gate.
|
||||||
for i, f := range genForkOrder {
|
frontier, _ := LookupInstructionSet(params.Rules{})
|
||||||
gf := GenFork{Name: f.name, RuleField: f.rule}
|
out := []GenFork{genFork("Frontier", "", &frontier)}
|
||||||
for code := range 256 {
|
|
||||||
op := f.set[code]
|
rt := reflect.TypeOf(params.Rules{})
|
||||||
if op == nil || op.undefined {
|
for i := range rt.NumField() {
|
||||||
continue
|
field := rt.Field(i)
|
||||||
}
|
if field.Type.Kind() != reflect.Bool || codegenSkippedForks[field.Name] {
|
||||||
gf.Ops[code] = GenOp{
|
continue
|
||||||
Name: OpCode(code).String(),
|
|
||||||
Defined: true,
|
|
||||||
ConstantGas: op.constantGas,
|
|
||||||
MinStack: op.minStack,
|
|
||||||
MaxStack: op.maxStack,
|
|
||||||
ExecuteFn: genFnName(op.execute),
|
|
||||||
DynamicGasFn: genFnName(op.dynamicGas),
|
|
||||||
MemorySizeFn: genFnName(op.memorySize),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out[i] = gf
|
// Activate only this field so the fork resolves to the table it gates.
|
||||||
|
var rules params.Rules
|
||||||
|
reflect.ValueOf(&rules).Elem().Field(i).SetBool(true)
|
||||||
|
set, _ := LookupInstructionSet(rules)
|
||||||
|
if sameOps(&set, &frontier) {
|
||||||
|
continue // this rule does not change the opcode table
|
||||||
|
}
|
||||||
|
out = append(out, genFork(strings.TrimPrefix(field.Name, "Is"), field.Name, &set))
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// genFork extracts the generator-facing per-opcode metadata from one fork's
|
||||||
|
// instruction set.
|
||||||
|
func genFork(name, rule string, set *JumpTable) GenFork {
|
||||||
|
gf := GenFork{Name: name, RuleField: rule}
|
||||||
|
for code := range 256 {
|
||||||
|
op := 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,
|
||||||
|
ExecuteFn: genFnName(op.execute),
|
||||||
|
DynamicGasFn: genFnName(op.dynamicGas),
|
||||||
|
MemorySizeFn: genFnName(op.memorySize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gf
|
||||||
|
}
|
||||||
|
|
||||||
|
// sameOps reports whether two instruction sets carry identical per-opcode static
|
||||||
|
// metadata: which slots are defined, and their static gas and stack bounds. It
|
||||||
|
// is used to tell whether a fork actually changes the opcode table. Handler,
|
||||||
|
// dynamic-gas and memory-size functions are ignored (they cannot be compared for
|
||||||
|
// equality and are reached through the table at runtime).
|
||||||
|
func sameOps(a, b *JumpTable) bool {
|
||||||
|
for code := range 256 {
|
||||||
|
oa, ob := a[code], b[code]
|
||||||
|
undefA := oa == nil || oa.undefined
|
||||||
|
undefB := ob == nil || ob.undefined
|
||||||
|
if undefA != undefB {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if undefA {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oa.constantGas != ob.constantGas || oa.minStack != ob.minStack || oa.maxStack != ob.maxStack {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ func (evm *EVM) execTraced(scope *ScopeContext) (ret []byte, err error) {
|
||||||
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
|
||||||
}
|
}
|
||||||
// for tracing: this gas consumption event is emitted below in the debug section.
|
// for tracing: this gas consumption event is emitted below in the debug section.
|
||||||
if err := contract.Gas.chargeRegularOnly(cost); err != nil {
|
if err := contract.Gas.ChargeRegularOnly(cost); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,7 +287,7 @@ func (contract *Contract) chargeDynamicGas(operation *operation, evm *EVM, stack
|
||||||
// A regular-only deduction when there is no state gas, otherwise the full
|
// A regular-only deduction when there is no state gas, otherwise the full
|
||||||
// multidimensional charge through the reservoir.
|
// multidimensional charge through the reservoir.
|
||||||
if dynamicCost.StateGas == 0 {
|
if dynamicCost.StateGas == 0 {
|
||||||
if cerr := contract.Gas.chargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
if cerr := contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
||||||
return dynamicCost, cerr
|
return dynamicCost, cerr
|
||||||
}
|
}
|
||||||
} else if !contract.Gas.charge(dynamicCost) {
|
} else if !contract.Gas.charge(dynamicCost) {
|
||||||
|
|
|
||||||
|
|
@ -601,7 +601,7 @@ mainLoop:
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
if dynamicCost.StateGas == 0 {
|
if dynamicCost.StateGas == 0 {
|
||||||
if cerr := contract.Gas.chargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
if cerr := contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
||||||
res, err = nil, cerr
|
res, err = nil, cerr
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
|
|
@ -664,7 +664,7 @@ mainLoop:
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
if dynamicCost.StateGas == 0 {
|
if dynamicCost.StateGas == 0 {
|
||||||
if cerr := contract.Gas.chargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
if cerr := contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
||||||
res, err = nil, cerr
|
res, err = nil, cerr
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
|
|
@ -712,7 +712,7 @@ mainLoop:
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
if dynamicCost.StateGas == 0 {
|
if dynamicCost.StateGas == 0 {
|
||||||
if cerr := contract.Gas.chargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
if cerr := contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
||||||
res, err = nil, cerr
|
res, err = nil, cerr
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
|
|
@ -760,7 +760,7 @@ mainLoop:
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
if dynamicCost.StateGas == 0 {
|
if dynamicCost.StateGas == 0 {
|
||||||
if cerr := contract.Gas.chargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
if cerr := contract.Gas.ChargeRegularOnly(dynamicCost.RegularGas); cerr != nil {
|
||||||
res, err = nil, cerr
|
res, err = nil, cerr
|
||||||
break mainLoop
|
break mainLoop
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue