diff --git a/Makefile b/Makefile index 51d0d1397b339..ebbf574e893c9 100644 --- a/Makefile +++ b/Makefile @@ -143,6 +143,12 @@ cannon-prestate: op-program cannon ## Generates prestate using cannon and op-pro mv op-program/bin/0.json op-program/bin/prestate-proof.json .PHONY: cannon-prestate +cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the multithreaded cannon format + ./cannon/bin/cannon load-elf --type mt --path op-program/bin/op-program-client.elf --out op-program/bin/prestate-mt.json --meta op-program/bin/meta-mt.json + ./cannon/bin/cannon run --type mt --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate-mt.json --meta op-program/bin/meta-mt.json --proof-fmt 'op-program/bin/%d-mt.json' --output "" + mv op-program/bin/0-mt.json op-program/bin/prestate-proof-mt.json +.PHONY: cannon-prestate + mod-tidy: ## Cleans up unused dependencies in Go modules # Below GOPRIVATE line allows mod-tidy to be run immediately after # releasing new versions. This bypasses the Go modules proxy, which diff --git a/cannon/cmd/load_elf.go b/cannon/cmd/load_elf.go index f111d1a8831ff..a6d14b5cb9742 100644 --- a/cannon/cmd/load_elf.go +++ b/cannon/cmd/load_elf.go @@ -4,6 +4,8 @@ import ( "debug/elf" "fmt" + "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/urfave/cli/v2" "github.com/ethereum-optimism/optimism/cannon/mipsevm/program" @@ -12,6 +14,12 @@ import ( ) var ( + LoadELFType = &cli.StringFlag{ + Name: "type", + Usage: "VM type to create state for. Options are 'cannon' (default), 'mt'", + Value: "cannon", + Required: false, + } LoadELFPathFlag = &cli.PathFlag{ Name: "path", Usage: "Path to 32-bit big-endian MIPS ELF file", @@ -39,6 +47,26 @@ var ( ) func LoadELF(ctx *cli.Context) error { + var createInitialState func(f *elf.File) (mipsevm.FPVMState, error) + var writeState func(path string, state mipsevm.FPVMState) error + + if ctx.String("type") == "cannon" { + createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { + return program.LoadELF(f, singlethreaded.CreateInitialState) + } + writeState = func(path string, state mipsevm.FPVMState) error { + return jsonutil.WriteJSON[*singlethreaded.State](path, state.(*singlethreaded.State), OutFilePerm) + } + } else if ctx.String("type") == "mt" { + createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { + return program.LoadELF(f, multithreaded.CreateInitialState) + } + writeState = func(path string, state mipsevm.FPVMState) error { + return jsonutil.WriteJSON[*multithreaded.State](path, state.(*multithreaded.State), OutFilePerm) + } + } else { + return fmt.Errorf("invalid VM type: %s", ctx.String("type")) + } elfPath := ctx.Path(LoadELFPathFlag.Name) elfProgram, err := elf.Open(elfPath) if err != nil { @@ -47,7 +75,7 @@ func LoadELF(ctx *cli.Context) error { if elfProgram.Machine != elf.EM_MIPS { return fmt.Errorf("ELF is not big-endian MIPS R3000, but got %q", elfProgram.Machine.String()) } - state, err := program.LoadELF(elfProgram, singlethreaded.CreateInitialState) + state, err := createInitialState(elfProgram) if err != nil { return fmt.Errorf("failed to load ELF data into VM state: %w", err) } @@ -71,7 +99,7 @@ func LoadELF(ctx *cli.Context) error { if err := jsonutil.WriteJSON[*program.Metadata](ctx.Path(LoadELFMetaFlag.Name), meta, OutFilePerm); err != nil { return fmt.Errorf("failed to output metadata: %w", err) } - return jsonutil.WriteJSON[*singlethreaded.State](ctx.Path(LoadELFOutFlag.Name), state, OutFilePerm) + return writeState(ctx.Path(LoadELFOutFlag.Name), state) } var LoadELFCommand = &cli.Command{ @@ -80,6 +108,7 @@ var LoadELFCommand = &cli.Command{ Description: "Load ELF file into Cannon JSON state, optionally patch out functions", Action: LoadELF, Flags: []cli.Flag{ + LoadELFType, LoadELFPathFlag, LoadELFPatchFlag, LoadELFOutFlag, diff --git a/cannon/cmd/run.go b/cannon/cmd/run.go index 22addbf3eb1cd..3c7099c87ff4f 100644 --- a/cannon/cmd/run.go +++ b/cannon/cmd/run.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" @@ -25,10 +26,9 @@ import ( var ( RunType = &cli.StringFlag{ - Name: "type", - Usage: "VM type to run. Options are 'cannon' (default)", - Value: "cannon", - // TODO(client-pod#903): This should be required once we have additional vm types + Name: "type", + Usage: "VM type to run. Options are 'cannon' (default), 'mt'", + Value: "cannon", Required: false, } RunInputFlag = &cli.PathFlag{ @@ -261,6 +261,7 @@ var _ mipsevm.PreimageOracle = (*ProcessPreimageOracle)(nil) type VMType string var cannonVMType VMType = "cannon" +var mtVMType VMType = "mt" func Run(ctx *cli.Context) error { if ctx.Bool(RunPProfCPU.Name) { @@ -270,6 +271,8 @@ func Run(ctx *cli.Context) error { var vmType VMType if vmTypeStr := ctx.String(RunType.Name); vmTypeStr == string(cannonVMType) { vmType = cannonVMType + } else if vmTypeStr == string(mtVMType) { + vmType = mtVMType } else { return fmt.Errorf("unknown VM type %q", vmType) } @@ -366,6 +369,7 @@ func Run(ctx *cli.Context) error { var vm mipsevm.FPVM var debugProgram bool if vmType == cannonVMType { + l.Info("Using cannon VM") cannon, err := singlethreaded.NewInstrumentedStateFromFile(ctx.Path(RunInputFlag.Name), po, outLog, errLog, meta) if err != nil { return err @@ -380,6 +384,22 @@ func Run(ctx *cli.Context) error { } } vm = cannon + } else if vmType == mtVMType { + l.Info("Using cannon multithreaded VM") + cannon, err := multithreaded.NewInstrumentedStateFromFile(ctx.Path(RunInputFlag.Name), po, outLog, errLog, l) + if err != nil { + return err + } + debugProgram = ctx.Bool(RunDebugFlag.Name) + if debugProgram { + if metaPath := ctx.Path(RunMetaFlag.Name); metaPath == "" { + return fmt.Errorf("cannot enable debug mode without a metadata file") + } + if err := cannon.InitDebug(meta); err != nil { + return fmt.Errorf("failed to initialize debug mode: %w", err) + } + } + vm = cannon } else { return fmt.Errorf("unknown VM type %q", vmType) } diff --git a/cannon/cmd/witness.go b/cannon/cmd/witness.go index 656684b691e96..02aeb14ac09ec 100644 --- a/cannon/cmd/witness.go +++ b/cannon/cmd/witness.go @@ -4,6 +4,8 @@ import ( "fmt" "os" + "github.com/ethereum-optimism/optimism/cannon/mipsevm" + "github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded" "github.com/urfave/cli/v2" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" @@ -11,6 +13,12 @@ import ( ) var ( + WitnessType = &cli.StringFlag{ + Name: "type", + Usage: "VM type to create state for. Options are 'cannon' (default), 'mt'", + Value: "cannon", + Required: false, + } WitnessInputFlag = &cli.PathFlag{ Name: "input", Usage: "path of input JSON state.", @@ -27,10 +35,19 @@ var ( func Witness(ctx *cli.Context) error { input := ctx.Path(WitnessInputFlag.Name) output := ctx.Path(WitnessOutputFlag.Name) - state, err := jsonutil.LoadJSON[singlethreaded.State](input) + var state mipsevm.FPVMState + var err error + if ctx.String("type") == "cannon" { + state, err = jsonutil.LoadJSON[singlethreaded.State](input) + } else if ctx.String("type") == "mt" { + state, err = jsonutil.LoadJSON[multithreaded.State](input) + } else { + return fmt.Errorf("invalid VM type: %s", ctx.String("type")) + } if err != nil { return fmt.Errorf("invalid input state (%v): %w", input, err) } + witness, h := state.EncodeWitness() if output != "" { if err := os.WriteFile(output, witness, 0755); err != nil { @@ -47,6 +64,7 @@ var WitnessCommand = &cli.Command{ Description: "Convert a Cannon JSON state into a binary witness. The hash of the witness is written to stdout", Action: Witness, Flags: []cli.Flag{ + WitnessType, WitnessInputFlag, WitnessOutputFlag, },