mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 19:00:46 +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
|
||||
}
|
||||
|
||||
// chargeRegularOnly deducts a regular-only cost.
|
||||
func (g *GasBudget) chargeRegularOnly(r uint64) error {
|
||||
// ChargeRegularOnly deducts a regular-only cost.
|
||||
func (g *GasBudget) ChargeRegularOnly(r uint64) error {
|
||||
if g.RegularGas < r {
|
||||
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
|
||||
// interpreter.go. It returns the top-level opXxx handlers by name, the
|
||||
// //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).
|
||||
func parseHandlers(vmDir string) (fset *token.FileSet, opcodeHandlers, stackHelpers, gasHelpers map[string]*ast.FuncDecl) {
|
||||
fset = token.NewFileSet()
|
||||
|
|
@ -132,7 +132,7 @@ func parseHandlers(vmDir string) (fset *token.FileSet, opcodeHandlers, stackHelp
|
|||
continue
|
||||
}
|
||||
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
|
||||
case fn.Recv == nil: // top-level opXxx handler
|
||||
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
|
||||
// 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
|
||||
// to contract.Gas and the method's single uint64 parameter to amount, substituted
|
||||
// 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
|
||||
// rewriteGasReturns).
|
||||
func (g *generator) generateStaticGas(amount any) string {
|
||||
fn := g.gasHelpers["chargeRegularOnly"]
|
||||
fn := g.gasHelpers["ChargeRegularOnly"]
|
||||
if fn == nil {
|
||||
fatalf("no chargeRegularOnly gas helper to inline")
|
||||
fatalf("no ChargeRegularOnly gas helper to inline")
|
||||
}
|
||||
names := paramNames(fn)
|
||||
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 = regexp.MustCompile(`\b`+recvName(fn)+`\b`).ReplaceAllString(src, "contract.Gas")
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import (
|
|||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// This file exposes the interpreter's opcode metadata to the code generator in
|
||||
|
|
@ -56,35 +58,13 @@ type GenFork struct {
|
|||
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},
|
||||
}
|
||||
// codegenSkippedForks are forks in geth's fork schedule that the generator does
|
||||
// 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),
|
||||
// which the generated switch picks up from the active table at runtime. Emitting
|
||||
// a lane for it would trip the generator's fork-stability check (an inlined op's
|
||||
// execute function is not allowed to vary by fork).
|
||||
var codegenSkippedForks = map[string]bool{"IsUBT": true}
|
||||
|
||||
// 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
|
||||
|
|
@ -108,28 +88,75 @@ func genFnName(fn any) string {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
out := make([]GenFork, len(genForkOrder))
|
||||
for i, f := range genForkOrder {
|
||||
gf := GenFork{Name: f.name, RuleField: f.rule}
|
||||
for code := range 256 {
|
||||
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,
|
||||
ExecuteFn: genFnName(op.execute),
|
||||
DynamicGasFn: genFnName(op.dynamicGas),
|
||||
MemorySizeFn: genFnName(op.memorySize),
|
||||
}
|
||||
// Frontier is always active and carries no rule gate.
|
||||
frontier, _ := LookupInstructionSet(params.Rules{})
|
||||
out := []GenFork{genFork("Frontier", "", &frontier)}
|
||||
|
||||
rt := reflect.TypeOf(params.Rules{})
|
||||
for i := range rt.NumField() {
|
||||
field := rt.Field(i)
|
||||
if field.Type.Kind() != reflect.Bool || codegenSkippedForks[field.Name] {
|
||||
continue
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
// multidimensional charge through the reservoir.
|
||||
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
|
||||
}
|
||||
} else if !contract.Gas.charge(dynamicCost) {
|
||||
|
|
|
|||
|
|
@ -601,7 +601,7 @@ mainLoop:
|
|||
break mainLoop
|
||||
}
|
||||
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
|
||||
break mainLoop
|
||||
}
|
||||
|
|
@ -664,7 +664,7 @@ mainLoop:
|
|||
break mainLoop
|
||||
}
|
||||
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
|
||||
break mainLoop
|
||||
}
|
||||
|
|
@ -712,7 +712,7 @@ mainLoop:
|
|||
break mainLoop
|
||||
}
|
||||
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
|
||||
break mainLoop
|
||||
}
|
||||
|
|
@ -760,7 +760,7 @@ mainLoop:
|
|||
break mainLoop
|
||||
}
|
||||
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
|
||||
break mainLoop
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue