Skip to content

Commit

Permalink
WrapperCodeGenerator: add script for generating wrappers
Browse files Browse the repository at this point in the history
- ExportInstructionSet: export instruction set based on input version to json
- ImportInstructionSet: import instruction set from json
- OpCodeWrapperGenerator: generate wrappers for an instruction set
  • Loading branch information
Francesco4203 committed Jul 8, 2024
1 parent 5d400f1 commit ec0f76f
Showing 1 changed file with 219 additions and 0 deletions.
219 changes: 219 additions & 0 deletions WrapperCodeGenerator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package main

import (
"encoding/json"
"os"
"text/template"

"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)

type InstructionSetJSON struct {
Version string `json:"version"`
InstructionSet []vm.OperationAttribute `json:"instruction_set"`
}
func ExportInstructionSet(version string) {
// Get the instruction set
var rules params.Rules
switch {
case version == "Homestead":
rules = params.Rules{
IsHomestead: true,
}
case version == "TangerineWhistle":
rules = params.Rules{
IsEIP150: true,
}
case version == "SpuriousDragon":
rules = params.Rules{
IsEIP158: true,
}
case version == "Byzantium":
rules = params.Rules{
IsByzantium: true,
}
case version == "Constantinople":
rules = params.Rules{
IsConstantinople: true,
}
case version == "Istanbul":
rules = params.Rules{
IsIstanbul: true,
}
case version == "Berlin":
rules = params.Rules{
IsBerlin: true,
}
case version == "London":
rules = params.Rules{
IsLondon: true,
}
default:
// frontier by default
rules = params.Rules{

}
}
instructionSet := vm.ExportInstructionSet(rules)

// Export the instruction set to json
file, err := os.Create("instruction_set.json")
if err != nil {
panic(err)
}
defer file.Close()

encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(
InstructionSetJSON{
Version: version,
InstructionSet: instructionSet,
},
)
if err != nil {
panic(err)
}
}

func ImportInstructionSet() InstructionSetJSON {
// Import the instruction set from json
file, err := os.Open("instruction_set.json")
if err != nil {
panic(err)
}
defer file.Close()

decoder := json.NewDecoder(file)
var instructionSetJSON InstructionSetJSON
err = decoder.Decode(&instructionSetJSON)
if err != nil {
panic(err)
}
return instructionSetJSON
}

func OpCodeWrapperGenerator() {
// Get instruction set
instructionSetJSON := ImportInstructionSet()

// Define the template
tmpl := template.Must(template.New("opcode").Parse(`
package vm
import "github.com/ethereum/go-ethereum/common/math"
{{$parent := .}}
{{range .InstructionSet}}
func {{.Name}}Wrapper{{$parent.Version}}(operation *operation, pc *uint64, in *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
{{- if .Supported}}
{{- if or .MemorySize .DynamicCost}}
mem := callContext.Memory
{{- end}}
stack := callContext.Stack
contract := callContext.Contract
var err error
var res []byte
in.evm.Context.Counter++
if sLen := stack.len(); sLen < {{.MinStack}} {
return nil, &ErrStackUnderflow{stackLen: sLen, required: {{.MinStack}}}
} else if sLen > {{.MaxStack}} {
return nil, &ErrStackOverflow{stackLen: sLen, limit: {{.MaxStack}}}
}
{{- if or .Writes (eq .Name "opCall")}}
if in.readOnly && in.evm.chainRules.IsByzantium {
{{- if .Writes}}
return nil, ErrWriteProtection
{{- else if (eq .Name "opCall")}}
if stack.Back(2).Sign() != 0 {
return nil, ErrWriteProtection
}
{{- end}}
}
{{- end}}
{{- if not .DynamicCost}}
if !contract.UseGas({{.ConstantGas}}) {
return nil, ErrOutOfGas
}
{{- end}}
{{- if or .MemorySize .DynamicCost}}
var memorySize uint64
{{- end}}
{{- if .MemorySize}}
memSize, overflow := operation.memorySize(stack)
if overflow {
return nil, ErrGasUintOverflow
}
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
return nil, ErrGasUintOverflow
}
{{- end}}
{{- if .DynamicCost}}
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
if err != nil || !contract.UseGas({{.ConstantGas}} + dynamicCost) {
return nil, ErrOutOfGas
}
{{- end}}
{{- if or .MemorySize .DynamicCost}}
if memorySize > 0 {
mem.Resize(memorySize)
}
{{- end}}
res, err = operation.execute(pc, in, callContext)
{{- if .Returns}}
in.returnData = res
{{- end}}
if err != nil {
return nil, err
}
{{- if .Reverts}}
return res, ErrExecutionReverted
{{- end}}
{{- if and (not .Jumps) (not .Reverts)}}
*pc++
{{- end}}
{{- if not .Reverts}}
return res, nil
{{- end}}
{{- else}}
return nil, nil
{{- end}}
}
{{end}}
var stringToWrapper{{$parent.Version}} = map[string]executionWrapperFunc{
{{- range .InstructionSet}}
"{{.Name}}": {{.Name}}Wrapper{{$parent.Version}},{{end}}
}
`))

// Create the source file
file, err := os.Create("instruction_wrapper_" + instructionSetJSON.Version + ".go")
if err != nil {
panic(err)
}
defer file.Close()

// Execute the template and write to the source file
err = tmpl.Execute(file, instructionSetJSON)
if err != nil {
panic(err)
}
}

func main() {
if len(os.Args) > 1 {
version := os.Args[1]
ExportInstructionSet(version)
OpCodeWrapperGenerator()
} else {
// export all by default
versions := []string{"Homestead", "TangerineWhistle", "SpuriousDragon", "Byzantium", "Constantinople", "Istanbul", "Berlin", "London", "Frontier"}
for _, version := range versions {
ExportInstructionSet(version)
OpCodeWrapperGenerator()
}
}
}

0 comments on commit ec0f76f

Please sign in to comment.