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 (
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
)
var (
......@@ -66,10 +67,10 @@ func LoadELF(ctx *cli.Context) error {
if err != nil {
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 writeJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state)
return jsonutil.WriteJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state, OutFilePerm)
}
var LoadELFCommand = &cli.Command{
......
......@@ -4,11 +4,13 @@ import (
"fmt"
"strconv"
"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 {
repr string
......@@ -26,11 +28,11 @@ func MustStepMatcherFlag(pattern string) *StepMatcherFlag {
func (m *StepMatcherFlag) Set(value string) error {
m.repr = value
if value == "" || value == "never" {
m.matcher = func(st *mipsevm.State) bool {
m.matcher = func(st VMState) bool {
return false
}
} else if value == "always" {
m.matcher = func(st *mipsevm.State) bool {
m.matcher = func(st VMState) bool {
return true
}
} else if strings.HasPrefix(value, "=") {
......@@ -38,16 +40,16 @@ func (m *StepMatcherFlag) Set(value string) error {
if err != nil {
return fmt.Errorf("failed to parse step number: %w", err)
}
m.matcher = func(st *mipsevm.State) bool {
return st.Step == when
m.matcher = func(st VMState) bool {
return st.GetStep() == when
}
} else if strings.HasPrefix(value, "%") {
when, err := strconv.ParseUint(value[1:], 0, 64)
if err != nil {
return fmt.Errorf("failed to parse step interval number: %w", err)
}
m.matcher = func(st *mipsevm.State) bool {
return st.Step%when == 0
m.matcher = func(st VMState) bool {
return st.GetStep()%when == 0
}
} else {
return fmt.Errorf("unrecognized step matcher: %q", value)
......@@ -61,7 +63,7 @@ func (m *StepMatcherFlag) String() string {
func (m *StepMatcherFlag) Matcher() StepMatcher {
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
}
}
......
......@@ -16,6 +16,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
preimage "github.com/ethereum-optimism/optimism/op-preimage"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
)
var (
......@@ -90,6 +91,8 @@ var (
Name: "pprof.cpu",
Usage: "enable pprof cpu profiling",
}
OutFilePerm = os.FileMode(0o755)
)
type Proof struct {
......@@ -234,7 +237,7 @@ func Run(ctx *cli.Context) error {
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 {
return err
}
......@@ -298,7 +301,7 @@ func Run(ctx *cli.Context) error {
l.Info("no metadata file specified, defaulting to empty metadata")
meta = &mipsevm.Metadata{Symbols: nil} // provide empty metadata by default
} 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)
} else {
meta = m
......@@ -351,7 +354,7 @@ func Run(ctx *cli.Context) error {
}
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)
}
}
......@@ -383,7 +386,7 @@ func Run(ctx *cli.Context) error {
proof.OracleValue = witness.PreimageValue
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)
}
} else {
......@@ -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 nil
......
......@@ -5,6 +5,7 @@ import (
"os"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"
"github.com/urfave/cli/v2"
)
......@@ -25,7 +26,7 @@ var (
func Witness(ctx *cli.Context) error {
input := ctx.Path(WitnessInputFlag.Name)
output := ctx.Path(WitnessOutputFlag.Name)
state, err := loadJSON[mipsevm.State](input)
state, err := jsonutil.LoadJSON[mipsevm.State](input)
if err != nil {
return fmt.Errorf("invalid input state (%v): %w", input, err)
}
......
......@@ -42,6 +42,8 @@ type State struct {
LastHint hexutil.Bytes `json:"lastHint,omitempty"`
}
func (s *State) GetStep() uint64 { return s.Step }
func (s *State) VMStatus() uint8 {
return vmStatus(s.Exited, s.ExitCode)
}
......
......@@ -210,7 +210,7 @@ func entrypoint(ctx *cli.Context) error {
// Write the batch to disk or stdout
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
}
} else {
......
......@@ -155,7 +155,7 @@ func entrypoint(ctx *cli.Context) error {
}
// Write contract versions to disk or stdout
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
}
} else {
......
......@@ -127,7 +127,7 @@ var Subcommands = cli.Commands{
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{
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 jsonutil.WriteJSON(ctx.String("outfile.rollup"), rollupConfig)
return jsonutil.WriteJSON(ctx.String("outfile.rollup"), rollupConfig, 0o666)
},
},
}
......
package cmd
package jsonutil
import (
"encoding/json"
......@@ -10,7 +10,7 @@ import (
"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 == "" {
return nil, errors.New("no path specified")
}
......@@ -27,14 +27,14 @@ func loadJSON[X any](inputPath string) (*X, error) {
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 == "" {
return nil
}
var out io.Writer
finish := func() error { return nil }
if outputPath != "-" {
f, err := ioutil.NewAtomicWriterCompressed(outputPath, 0755)
f, err := ioutil.NewAtomicWriterCompressed(outputPath, perm)
if err != nil {
return fmt.Errorf("failed to open output file: %w", err)
}
......@@ -48,6 +48,7 @@ func writeJSON[X any](outputPath string, value X) error {
out = os.Stdout
}
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
if err := enc.Encode(value); err != nil {
return fmt.Errorf("failed to encode to JSON: %w", err)
}
......
package cmd
package jsonutil
import (
"encoding/json"
......@@ -13,7 +13,7 @@ func TestRoundTripJSON(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "test.json")
data := &jsonTestData{A: "yay", B: 3}
err := writeJSON(file, data)
err := WriteJSON(file, data, 0o755)
require.NoError(t, err)
// Confirm the file is uncompressed
......@@ -23,7 +23,7 @@ func TestRoundTripJSON(t *testing.T) {
require.NoError(t, err)
var result *jsonTestData
result, err = loadJSON[jsonTestData](file)
result, err = LoadJSON[jsonTestData](file)
require.NoError(t, err)
require.EqualValues(t, data, result)
}
......@@ -32,7 +32,7 @@ func TestRoundTripJSONWithGzip(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "test.json.gz")
data := &jsonTestData{A: "yay", B: 3}
err := writeJSON(file, data)
err := WriteJSON(file, data, 0o755)
require.NoError(t, err)
// Confirm the file isn't raw JSON
......@@ -42,7 +42,7 @@ func TestRoundTripJSONWithGzip(t *testing.T) {
require.Error(t, err, "should not be able to decode without decompressing")
var result *jsonTestData
result, err = loadJSON[jsonTestData](file)
result, err = LoadJSON[jsonTestData](file)
require.NoError(t, err)
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