Commit dfc7eda4 authored by Tei Im's avatar Tei Im Committed by GitHub

cannon: Export methods for Asterisc (#9350)

* Export json methods

* Use VMState interface to share Matcher with Asterisc

* Move json methods to op-service
parent 5be36b5a
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
) )
var ( var (
...@@ -66,10 +67,10 @@ func LoadELF(ctx *cli.Context) error { ...@@ -66,10 +67,10 @@ func LoadELF(ctx *cli.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to compute program metadata: %w", err) return fmt.Errorf("failed to compute program metadata: %w", err)
} }
if err := writeJSON[*mipsevm.Metadata](ctx.Path(LoadELFMetaFlag.Name), meta); err != nil { if err := jsonutil.WriteJSON[*mipsevm.Metadata](ctx.Path(LoadELFMetaFlag.Name), meta, OutFilePerm); err != nil {
return fmt.Errorf("failed to output metadata: %w", err) return fmt.Errorf("failed to output metadata: %w", err)
} }
return writeJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state) return jsonutil.WriteJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state, OutFilePerm)
} }
var LoadELFCommand = &cli.Command{ var LoadELFCommand = &cli.Command{
......
...@@ -4,11 +4,13 @@ import ( ...@@ -4,11 +4,13 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
) )
type StepMatcher func(st *mipsevm.State) bool type VMState interface {
GetStep() uint64
}
type StepMatcher func(st VMState) bool
type StepMatcherFlag struct { type StepMatcherFlag struct {
repr string repr string
...@@ -26,11 +28,11 @@ func MustStepMatcherFlag(pattern string) *StepMatcherFlag { ...@@ -26,11 +28,11 @@ func MustStepMatcherFlag(pattern string) *StepMatcherFlag {
func (m *StepMatcherFlag) Set(value string) error { func (m *StepMatcherFlag) Set(value string) error {
m.repr = value m.repr = value
if value == "" || value == "never" { if value == "" || value == "never" {
m.matcher = func(st *mipsevm.State) bool { m.matcher = func(st VMState) bool {
return false return false
} }
} else if value == "always" { } else if value == "always" {
m.matcher = func(st *mipsevm.State) bool { m.matcher = func(st VMState) bool {
return true return true
} }
} else if strings.HasPrefix(value, "=") { } else if strings.HasPrefix(value, "=") {
...@@ -38,16 +40,16 @@ func (m *StepMatcherFlag) Set(value string) error { ...@@ -38,16 +40,16 @@ func (m *StepMatcherFlag) Set(value string) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to parse step number: %w", err) return fmt.Errorf("failed to parse step number: %w", err)
} }
m.matcher = func(st *mipsevm.State) bool { m.matcher = func(st VMState) bool {
return st.Step == when return st.GetStep() == when
} }
} else if strings.HasPrefix(value, "%") { } else if strings.HasPrefix(value, "%") {
when, err := strconv.ParseUint(value[1:], 0, 64) when, err := strconv.ParseUint(value[1:], 0, 64)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse step interval number: %w", err) return fmt.Errorf("failed to parse step interval number: %w", err)
} }
m.matcher = func(st *mipsevm.State) bool { m.matcher = func(st VMState) bool {
return st.Step%when == 0 return st.GetStep()%when == 0
} }
} else { } else {
return fmt.Errorf("unrecognized step matcher: %q", value) return fmt.Errorf("unrecognized step matcher: %q", value)
...@@ -61,7 +63,7 @@ func (m *StepMatcherFlag) String() string { ...@@ -61,7 +63,7 @@ func (m *StepMatcherFlag) String() string {
func (m *StepMatcherFlag) Matcher() StepMatcher { func (m *StepMatcherFlag) Matcher() StepMatcher {
if m.matcher == nil { // Set(value) is not called for omitted inputs, default to never matching. if m.matcher == nil { // Set(value) is not called for omitted inputs, default to never matching.
return func(st *mipsevm.State) bool { return func(st VMState) bool {
return false return false
} }
} }
......
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
preimage "github.com/ethereum-optimism/optimism/op-preimage" preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
) )
var ( var (
...@@ -90,6 +91,8 @@ var ( ...@@ -90,6 +91,8 @@ var (
Name: "pprof.cpu", Name: "pprof.cpu",
Usage: "enable pprof cpu profiling", Usage: "enable pprof cpu profiling",
} }
OutFilePerm = os.FileMode(0o755)
) )
type Proof struct { type Proof struct {
...@@ -234,7 +237,7 @@ func Run(ctx *cli.Context) error { ...@@ -234,7 +237,7 @@ func Run(ctx *cli.Context) error {
defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop() defer profile.Start(profile.NoShutdownHook, profile.ProfilePath("."), profile.CPUProfile).Stop()
} }
state, err := loadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name)) state, err := jsonutil.LoadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name))
if err != nil { if err != nil {
return err return err
} }
...@@ -298,7 +301,7 @@ func Run(ctx *cli.Context) error { ...@@ -298,7 +301,7 @@ func Run(ctx *cli.Context) error {
l.Info("no metadata file specified, defaulting to empty metadata") l.Info("no metadata file specified, defaulting to empty metadata")
meta = &mipsevm.Metadata{Symbols: nil} // provide empty metadata by default meta = &mipsevm.Metadata{Symbols: nil} // provide empty metadata by default
} else { } else {
if m, err := loadJSON[mipsevm.Metadata](metaPath); err != nil { if m, err := jsonutil.LoadJSON[mipsevm.Metadata](metaPath); err != nil {
return fmt.Errorf("failed to load metadata: %w", err) return fmt.Errorf("failed to load metadata: %w", err)
} else { } else {
meta = m meta = m
...@@ -351,7 +354,7 @@ func Run(ctx *cli.Context) error { ...@@ -351,7 +354,7 @@ func Run(ctx *cli.Context) error {
} }
if snapshotAt(state) { if snapshotAt(state) {
if err := writeJSON(fmt.Sprintf(snapshotFmt, step), state); err != nil { if err := jsonutil.WriteJSON(fmt.Sprintf(snapshotFmt, step), state, OutFilePerm); err != nil {
return fmt.Errorf("failed to write state snapshot: %w", err) return fmt.Errorf("failed to write state snapshot: %w", err)
} }
} }
...@@ -383,7 +386,7 @@ func Run(ctx *cli.Context) error { ...@@ -383,7 +386,7 @@ func Run(ctx *cli.Context) error {
proof.OracleValue = witness.PreimageValue proof.OracleValue = witness.PreimageValue
proof.OracleOffset = witness.PreimageOffset proof.OracleOffset = witness.PreimageOffset
} }
if err := writeJSON(fmt.Sprintf(proofFmt, step), proof); err != nil { if err := jsonutil.WriteJSON(fmt.Sprintf(proofFmt, step), proof, OutFilePerm); err != nil {
return fmt.Errorf("failed to write proof data: %w", err) return fmt.Errorf("failed to write proof data: %w", err)
} }
} else { } else {
...@@ -406,7 +409,7 @@ func Run(ctx *cli.Context) error { ...@@ -406,7 +409,7 @@ func Run(ctx *cli.Context) error {
} }
} }
if err := writeJSON(ctx.Path(RunOutputFlag.Name), state); err != nil { if err := jsonutil.WriteJSON(ctx.Path(RunOutputFlag.Name), state, OutFilePerm); err != nil {
return fmt.Errorf("failed to write state output: %w", err) return fmt.Errorf("failed to write state output: %w", err)
} }
return nil return nil
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/ethereum-optimism/optimism/cannon/mipsevm" "github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
...@@ -25,7 +26,7 @@ var ( ...@@ -25,7 +26,7 @@ var (
func Witness(ctx *cli.Context) error { func Witness(ctx *cli.Context) error {
input := ctx.Path(WitnessInputFlag.Name) input := ctx.Path(WitnessInputFlag.Name)
output := ctx.Path(WitnessOutputFlag.Name) output := ctx.Path(WitnessOutputFlag.Name)
state, err := loadJSON[mipsevm.State](input) state, err := jsonutil.LoadJSON[mipsevm.State](input)
if err != nil { if err != nil {
return fmt.Errorf("invalid input state (%v): %w", input, err) return fmt.Errorf("invalid input state (%v): %w", input, err)
} }
......
...@@ -42,6 +42,8 @@ type State struct { ...@@ -42,6 +42,8 @@ type State struct {
LastHint hexutil.Bytes `json:"lastHint,omitempty"` LastHint hexutil.Bytes `json:"lastHint,omitempty"`
} }
func (s *State) GetStep() uint64 { return s.Step }
func (s *State) VMStatus() uint8 { func (s *State) VMStatus() uint8 {
return vmStatus(s.Exited, s.ExitCode) return vmStatus(s.Exited, s.ExitCode)
} }
......
...@@ -210,7 +210,7 @@ func entrypoint(ctx *cli.Context) error { ...@@ -210,7 +210,7 @@ func entrypoint(ctx *cli.Context) error {
// Write the batch to disk or stdout // Write the batch to disk or stdout
if outfile := ctx.Path("outfile"); outfile != "" { if outfile := ctx.Path("outfile"); outfile != "" {
if err := jsonutil.WriteJSON(outfile, batch); err != nil { if err := jsonutil.WriteJSON(outfile, batch, 0o666); err != nil {
return err return err
} }
} else { } else {
......
...@@ -155,7 +155,7 @@ func entrypoint(ctx *cli.Context) error { ...@@ -155,7 +155,7 @@ func entrypoint(ctx *cli.Context) error {
} }
// Write contract versions to disk or stdout // Write contract versions to disk or stdout
if outfile := ctx.Path("outfile"); outfile != "" { if outfile := ctx.Path("outfile"); outfile != "" {
if err := jsonutil.WriteJSON(outfile, output); err != nil { if err := jsonutil.WriteJSON(outfile, output, 0o666); err != nil {
return err return err
} }
} else { } else {
......
...@@ -127,7 +127,7 @@ var Subcommands = cli.Commands{ ...@@ -127,7 +127,7 @@ var Subcommands = cli.Commands{
return err return err
} }
return jsonutil.WriteJSON(ctx.String("outfile.l1"), l1Genesis) return jsonutil.WriteJSON(ctx.String("outfile.l1"), l1Genesis, 0o666)
}, },
}, },
{ {
...@@ -250,10 +250,10 @@ var Subcommands = cli.Commands{ ...@@ -250,10 +250,10 @@ var Subcommands = cli.Commands{
return fmt.Errorf("generated rollup config does not pass validation: %w", err) return fmt.Errorf("generated rollup config does not pass validation: %w", err)
} }
if err := jsonutil.WriteJSON(ctx.String("outfile.l2"), l2Genesis); err != nil { if err := jsonutil.WriteJSON(ctx.String("outfile.l2"), l2Genesis, 0o666); err != nil {
return err return err
} }
return jsonutil.WriteJSON(ctx.String("outfile.rollup"), rollupConfig) return jsonutil.WriteJSON(ctx.String("outfile.rollup"), rollupConfig, 0o666)
}, },
}, },
} }
......
package cmd package jsonutil
import ( import (
"encoding/json" "encoding/json"
...@@ -10,7 +10,7 @@ import ( ...@@ -10,7 +10,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
) )
func loadJSON[X any](inputPath string) (*X, error) { func LoadJSON[X any](inputPath string) (*X, error) {
if inputPath == "" { if inputPath == "" {
return nil, errors.New("no path specified") return nil, errors.New("no path specified")
} }
...@@ -27,14 +27,14 @@ func loadJSON[X any](inputPath string) (*X, error) { ...@@ -27,14 +27,14 @@ func loadJSON[X any](inputPath string) (*X, error) {
return &state, nil return &state, nil
} }
func writeJSON[X any](outputPath string, value X) error { func WriteJSON[X any](outputPath string, value X, perm os.FileMode) error {
if outputPath == "" { if outputPath == "" {
return nil return nil
} }
var out io.Writer var out io.Writer
finish := func() error { return nil } finish := func() error { return nil }
if outputPath != "-" { if outputPath != "-" {
f, err := ioutil.NewAtomicWriterCompressed(outputPath, 0755) f, err := ioutil.NewAtomicWriterCompressed(outputPath, perm)
if err != nil { if err != nil {
return fmt.Errorf("failed to open output file: %w", err) return fmt.Errorf("failed to open output file: %w", err)
} }
...@@ -48,6 +48,7 @@ func writeJSON[X any](outputPath string, value X) error { ...@@ -48,6 +48,7 @@ func writeJSON[X any](outputPath string, value X) error {
out = os.Stdout out = os.Stdout
} }
enc := json.NewEncoder(out) enc := json.NewEncoder(out)
enc.SetIndent("", " ")
if err := enc.Encode(value); err != nil { if err := enc.Encode(value); err != nil {
return fmt.Errorf("failed to encode to JSON: %w", err) return fmt.Errorf("failed to encode to JSON: %w", err)
} }
......
package cmd package jsonutil
import ( import (
"encoding/json" "encoding/json"
...@@ -13,7 +13,7 @@ func TestRoundTripJSON(t *testing.T) { ...@@ -13,7 +13,7 @@ func TestRoundTripJSON(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
file := filepath.Join(dir, "test.json") file := filepath.Join(dir, "test.json")
data := &jsonTestData{A: "yay", B: 3} data := &jsonTestData{A: "yay", B: 3}
err := writeJSON(file, data) err := WriteJSON(file, data, 0o755)
require.NoError(t, err) require.NoError(t, err)
// Confirm the file is uncompressed // Confirm the file is uncompressed
...@@ -23,7 +23,7 @@ func TestRoundTripJSON(t *testing.T) { ...@@ -23,7 +23,7 @@ func TestRoundTripJSON(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
var result *jsonTestData var result *jsonTestData
result, err = loadJSON[jsonTestData](file) result, err = LoadJSON[jsonTestData](file)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, data, result) require.EqualValues(t, data, result)
} }
...@@ -32,7 +32,7 @@ func TestRoundTripJSONWithGzip(t *testing.T) { ...@@ -32,7 +32,7 @@ func TestRoundTripJSONWithGzip(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
file := filepath.Join(dir, "test.json.gz") file := filepath.Join(dir, "test.json.gz")
data := &jsonTestData{A: "yay", B: 3} data := &jsonTestData{A: "yay", B: 3}
err := writeJSON(file, data) err := WriteJSON(file, data, 0o755)
require.NoError(t, err) require.NoError(t, err)
// Confirm the file isn't raw JSON // Confirm the file isn't raw JSON
...@@ -42,7 +42,7 @@ func TestRoundTripJSONWithGzip(t *testing.T) { ...@@ -42,7 +42,7 @@ func TestRoundTripJSONWithGzip(t *testing.T) {
require.Error(t, err, "should not be able to decode without decompressing") require.Error(t, err, "should not be able to decode without decompressing")
var result *jsonTestData var result *jsonTestData
result, err = loadJSON[jsonTestData](file) result, err = LoadJSON[jsonTestData](file)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, data, result) require.EqualValues(t, data, result)
} }
......
package jsonutil
import (
"encoding/json"
"os"
)
func WriteJSON(outfile string, input interface{}) error {
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666)
if err != nil {
return err
}
defer f.Close()
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
return enc.Encode(input)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment