diff --git a/cannon/cmd/load_elf.go b/cannon/cmd/load_elf.go index 5cfc3078e90..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/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/cmd/run.go b/cannon/cmd/run.go index 5b821cf0695..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 d92a2777918..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/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) } 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 85% rename from cannon/cmd/json.go rename to op-service/jsonutil/json.go index 615bacedc86..a7918d6d4a6 100644 --- a/cannon/cmd/json.go +++ b/op-service/jsonutil/json.go @@ -1,4 +1,4 @@ -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) } diff --git a/cannon/cmd/json_test.go b/op-service/jsonutil/json_test.go similarity index 86% rename from cannon/cmd/json_test.go rename to op-service/jsonutil/json_test.go index 63d4636b7d9..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 @@ -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) } 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) -}