From b6a63e58a3188474d1c2c74f2ff24c1ba6da2e37 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Wed, 31 Jan 2024 18:57:12 +0900 Subject: [PATCH 1/3] Export json methods --- cannon/cmd/json.go | 4 ++-- cannon/cmd/json_test.go | 8 ++++---- cannon/cmd/load_elf.go | 4 ++-- cannon/cmd/run.go | 10 +++++----- cannon/cmd/witness.go | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cannon/cmd/json.go b/cannon/cmd/json.go index 615bacedc86..6f1dee1cca9 100644 --- a/cannon/cmd/json.go +++ b/cannon/cmd/json.go @@ -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,7 +27,7 @@ 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) error { if outputPath == "" { return nil } diff --git a/cannon/cmd/json_test.go b/cannon/cmd/json_test.go index 63d4636b7d9..ebb126195f7 100644 --- a/cannon/cmd/json_test.go +++ b/cannon/cmd/json_test.go @@ -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) 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) 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) } diff --git a/cannon/cmd/load_elf.go b/cannon/cmd/load_elf.go index 5cfc3078e90..e8da4e6ab0b 100644 --- a/cannon/cmd/load_elf.go +++ b/cannon/cmd/load_elf.go @@ -66,10 +66,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 := WriteJSON[*mipsevm.Metadata](ctx.Path(LoadELFMetaFlag.Name), meta); err != nil { return fmt.Errorf("failed to output metadata: %w", err) } - return writeJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state) + return WriteJSON[*mipsevm.State](ctx.Path(LoadELFOutFlag.Name), state) } var LoadELFCommand = &cli.Command{ diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 5b821cf0695..a3462b34a24 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -234,7 +234,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 := LoadJSON[mipsevm.State](ctx.Path(RunInputFlag.Name)) if err != nil { return err } @@ -284,7 +284,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 := LoadJSON[mipsevm.Metadata](metaPath); err != nil { return fmt.Errorf("failed to load metadata: %w", err) } else { meta = m @@ -337,7 +337,7 @@ func Run(ctx *cli.Context) error { } if snapshotAt(state) { - if err := writeJSON(fmt.Sprintf(snapshotFmt, step), state); err != nil { + if err := WriteJSON(fmt.Sprintf(snapshotFmt, step), state); err != nil { return fmt.Errorf("failed to write state snapshot: %w", err) } } @@ -369,7 +369,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 := WriteJSON(fmt.Sprintf(proofFmt, step), proof); err != nil { return fmt.Errorf("failed to write proof data: %w", err) } } else { @@ -398,7 +398,7 @@ func Run(ctx *cli.Context) error { } } - if err := writeJSON(ctx.Path(RunOutputFlag.Name), state); err != nil { + if err := WriteJSON(ctx.Path(RunOutputFlag.Name), state); err != nil { return fmt.Errorf("failed to write state output: %w", err) } return nil diff --git a/cannon/cmd/witness.go b/cannon/cmd/witness.go index d92a2777918..c951433ec7b 100644 --- a/cannon/cmd/witness.go +++ b/cannon/cmd/witness.go @@ -25,7 +25,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 := LoadJSON[mipsevm.State](input) if err != nil { return fmt.Errorf("invalid input state (%v): %w", input, err) } From 1d45b4991e445301567adb7b1addb3653f7a42c4 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Wed, 31 Jan 2024 18:57:54 +0900 Subject: [PATCH 2/3] Use VMState interface to share Matcher with Asterisc --- cannon/cmd/matcher.go | 22 ++++++++++++---------- cannon/mipsevm/state.go | 2 ++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/cannon/cmd/matcher.go b/cannon/cmd/matcher.go index 2f0267c143b..b569ef6df6b 100644 --- a/cannon/cmd/matcher.go +++ b/cannon/cmd/matcher.go @@ -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 } } diff --git a/cannon/mipsevm/state.go b/cannon/mipsevm/state.go index ba95f1938df..fea495218a5 100644 --- a/cannon/mipsevm/state.go +++ b/cannon/mipsevm/state.go @@ -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) } From 961dbee6d0b20890fbcb556f2ad143125b3555a3 Mon Sep 17 00:00:00 2001 From: Tei Im Date: Wed, 7 Feb 2024 14:01:19 +0900 Subject: [PATCH 3/3] Move json methods to op-service --- cannon/cmd/load_elf.go | 5 +++-- cannon/cmd/run.go | 13 ++++++++----- cannon/cmd/witness.go | 3 ++- op-chain-ops/cmd/op-upgrade/main.go | 2 +- op-chain-ops/cmd/op-version-check/main.go | 2 +- op-node/cmd/genesis/cmd.go | 6 +++--- {cannon/cmd => op-service/jsonutil}/json.go | 7 ++++--- .../cmd => op-service/jsonutil}/json_test.go | 6 +++--- op-service/jsonutil/write.go | 18 ------------------ 9 files changed, 25 insertions(+), 37 deletions(-) rename {cannon/cmd => op-service/jsonutil}/json.go (88%) rename {cannon/cmd => op-service/jsonutil}/json_test.go (92%) delete mode 100644 op-service/jsonutil/write.go diff --git a/cannon/cmd/load_elf.go b/cannon/cmd/load_elf.go index e8da4e6ab0b..7c41fc3c0ef 100644 --- a/cannon/cmd/load_elf.go +++ b/cannon/cmd/load_elf.go @@ -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{ diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index a3462b34a24..4f15ad95f18 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -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 } @@ -284,7 +287,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 @@ -337,7 +340,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) } } @@ -369,7 +372,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 { @@ -398,7 +401,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 diff --git a/cannon/cmd/witness.go b/cannon/cmd/witness.go index c951433ec7b..c2f38daa362 100644 --- a/cannon/cmd/witness.go +++ b/cannon/cmd/witness.go @@ -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) } diff --git a/op-chain-ops/cmd/op-upgrade/main.go b/op-chain-ops/cmd/op-upgrade/main.go index 87fcb9f8f54..7c5cf87f261 100644 --- a/op-chain-ops/cmd/op-upgrade/main.go +++ b/op-chain-ops/cmd/op-upgrade/main.go @@ -208,7 +208,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 { diff --git a/op-chain-ops/cmd/op-version-check/main.go b/op-chain-ops/cmd/op-version-check/main.go index 202084df3da..36144f9cacf 100644 --- a/op-chain-ops/cmd/op-version-check/main.go +++ b/op-chain-ops/cmd/op-version-check/main.go @@ -152,7 +152,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 { diff --git a/op-node/cmd/genesis/cmd.go b/op-node/cmd/genesis/cmd.go index 67ea3e078f6..d94c7556f18 100644 --- a/op-node/cmd/genesis/cmd.go +++ b/op-node/cmd/genesis/cmd.go @@ -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) }, }, } diff --git a/cannon/cmd/json.go b/op-service/jsonutil/json.go similarity index 88% rename from cannon/cmd/json.go rename to op-service/jsonutil/json.go index 6f1dee1cca9..a7918d6d4a6 100644 --- a/cannon/cmd/json.go +++ b/op-service/jsonutil/json.go @@ -1,4 +1,4 @@ -package cmd +package jsonutil import ( "encoding/json" @@ -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) } diff --git a/cannon/cmd/json_test.go b/op-service/jsonutil/json_test.go similarity index 92% rename from cannon/cmd/json_test.go rename to op-service/jsonutil/json_test.go index ebb126195f7..16eb91992f8 100644 --- a/cannon/cmd/json_test.go +++ b/op-service/jsonutil/json_test.go @@ -1,4 +1,4 @@ -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 @@ -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 diff --git a/op-service/jsonutil/write.go b/op-service/jsonutil/write.go deleted file mode 100644 index 8b0a295f971..00000000000 --- a/op-service/jsonutil/write.go +++ /dev/null @@ -1,18 +0,0 @@ -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) -}