Commit 56502ddc authored by Inphi's avatar Inphi Committed by GitHub

cannon: Multi VM executor (#12072)

* cannon: Multi VM executor

* fix run subcmd arg fwding

* fix mt prestate

* add list subcmd; multicannon in op-stack-go

* remove cannon-latest

* safer strconv

* lint

* include .gitkeep in embed

* fix .git copy

* add detect.go tests

* add nosemgrep

* review comments

* list filtering

* add note to MIPS.sol in version stf ref

* use fork-exec

* minimal flag parsing

* load old cannon binaries from docker images

* note

* --help flag defaults

* remove redundant copy from cannon-builder-0
parent 30725498
...@@ -142,13 +142,13 @@ $(DEVNET_CANNON_PRESTATE_FILES): ...@@ -142,13 +142,13 @@ $(DEVNET_CANNON_PRESTATE_FILES):
make cannon-prestate-mt make cannon-prestate-mt
cannon-prestate: op-program cannon ## Generates prestate using cannon and op-program cannon-prestate: op-program cannon ## Generates prestate using cannon and op-program
./cannon/bin/cannon load-elf --path op-program/bin/op-program-client.elf --out op-program/bin/prestate.json --meta op-program/bin/meta.json ./cannon/bin/cannon load-elf --type singlethreaded --path op-program/bin/op-program-client.elf --out op-program/bin/prestate.json --meta op-program/bin/meta.json
./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate.json --meta op-program/bin/meta.json --proof-fmt 'op-program/bin/%d.json' --output "" ./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate.json --meta op-program/bin/meta.json --proof-fmt 'op-program/bin/%d.json' --output ""
mv op-program/bin/0.json op-program/bin/prestate-proof.json mv op-program/bin/0.json op-program/bin/prestate-proof.json
.PHONY: cannon-prestate .PHONY: cannon-prestate
cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the multithreaded cannon format cannon-prestate-mt: op-program cannon ## Generates prestate using cannon and op-program in the multithreaded cannon format
./cannon/bin/cannon load-elf --type cannon-mt --path op-program/bin/op-program-client.elf --out op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json ./cannon/bin/cannon load-elf --type multithreaded --path op-program/bin/op-program-client.elf --out op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json
./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate-mt.bin.gz --meta op-program/bin/meta-mt.json --proof-fmt 'op-program/bin/%d-mt.json' --output "" ./cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input op-program/bin/prestate-mt.bin.gz --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 mv op-program/bin/0-mt.json op-program/bin/prestate-proof-mt.json
.PHONY: cannon-prestate-mt .PHONY: cannon-prestate-mt
......
...@@ -13,3 +13,4 @@ state.json ...@@ -13,3 +13,4 @@ state.json
*.pprof *.pprof
*.out *.out
bin bin
multicannon/embeds/cannon*
...@@ -13,8 +13,15 @@ ifeq ($(shell uname),Darwin) ...@@ -13,8 +13,15 @@ ifeq ($(shell uname),Darwin)
FUZZLDFLAGS := -ldflags=-extldflags=-Wl,-ld_classic FUZZLDFLAGS := -ldflags=-extldflags=-Wl,-ld_classic
endif endif
cannon: cannon-impl:
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon . env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon-impl .
cannon-embeds: cannon-impl
@cp bin/cannon-impl ./multicannon/embeds/cannon-0
@cp bin/cannon-impl ./multicannon/embeds/cannon-1
cannon: cannon-embeds
env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) go build -v $(LDFLAGS) -o ./bin/cannon ./multicannon/
clean: clean:
rm -rf bin rm -rf bin
......
...@@ -30,7 +30,7 @@ make cannon ...@@ -30,7 +30,7 @@ make cannon
# Transform MIPS op-program client binary into first VM state. # Transform MIPS op-program client binary into first VM state.
# This outputs state.json (VM state) and meta.json (for debug symbols). # This outputs state.json (VM state) and meta.json (for debug symbols).
./bin/cannon load-elf --path=../op-program/bin/op-program-client.elf ./bin/cannon load-elf --type singlethreaded --path=../op-program/bin/op-program-client.elf
# Run cannon emulator (with example inputs) # Run cannon emulator (with example inputs)
# Note that the server-mode op-program command is passed into cannon (after the --), # Note that the server-mode op-program command is passed into cannon (after the --),
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded" "github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
"github.com/ethereum-optimism/optimism/cannon/serialize" "github.com/ethereum-optimism/optimism/cannon/serialize"
openum "github.com/ethereum-optimism/optimism/op-service/enum"
"github.com/ethereum-optimism/optimism/op-service/ioutil" "github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum-optimism/optimism/op-service/jsonutil" "github.com/ethereum-optimism/optimism/op-service/jsonutil"
) )
...@@ -19,9 +20,8 @@ import ( ...@@ -19,9 +20,8 @@ import (
var ( var (
LoadELFVMTypeFlag = &cli.StringFlag{ LoadELFVMTypeFlag = &cli.StringFlag{
Name: "type", Name: "type",
Usage: "VM type to create state for. Options are 'cannon' (default), 'cannon-mt'", Usage: "VM type to create state for. Valid options: " + openum.EnumString(stateVersions()),
Value: "cannon", Required: true,
Required: false,
} }
LoadELFPathFlag = &cli.PathFlag{ LoadELFPathFlag = &cli.PathFlag{
Name: "path", Name: "path",
...@@ -43,21 +43,12 @@ var ( ...@@ -43,21 +43,12 @@ var (
} }
) )
type VMType string func stateVersions() []string {
vers := make([]string, len(versions.StateVersionTypes))
var ( for i, v := range versions.StateVersionTypes {
cannonVMType VMType = "cannon" vers[i] = v.String()
mtVMType VMType = "cannon-mt"
)
func vmTypeFromString(ctx *cli.Context) (VMType, error) {
if vmTypeStr := ctx.String(LoadELFVMTypeFlag.Name); vmTypeStr == string(cannonVMType) {
return cannonVMType, nil
} else if vmTypeStr == string(mtVMType) {
return mtVMType, nil
} else {
return "", fmt.Errorf("unknown VM type %q", vmTypeStr)
} }
return vers
} }
func LoadELF(ctx *cli.Context) error { func LoadELF(ctx *cli.Context) error {
...@@ -73,9 +64,12 @@ func LoadELF(ctx *cli.Context) error { ...@@ -73,9 +64,12 @@ func LoadELF(ctx *cli.Context) error {
var createInitialState func(f *elf.File) (mipsevm.FPVMState, error) var createInitialState func(f *elf.File) (mipsevm.FPVMState, error)
var patcher = program.PatchStack var patcher = program.PatchStack
if vmType, err := vmTypeFromString(ctx); err != nil { ver, err := versions.ParseStateVersion(ctx.String(LoadELFVMTypeFlag.Name))
if err != nil {
return err return err
} else if vmType == cannonVMType { }
switch ver {
case versions.VersionSingleThreaded:
createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
return program.LoadELF(f, singlethreaded.CreateInitialState) return program.LoadELF(f, singlethreaded.CreateInitialState)
} }
...@@ -86,12 +80,12 @@ func LoadELF(ctx *cli.Context) error { ...@@ -86,12 +80,12 @@ func LoadELF(ctx *cli.Context) error {
} }
return program.PatchStack(state) return program.PatchStack(state)
} }
} else if vmType == mtVMType { case versions.VersionMultiThreaded:
createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) {
return program.LoadELF(f, multithreaded.CreateInitialState) return program.LoadELF(f, multithreaded.CreateInitialState)
} }
} else { default:
return fmt.Errorf("invalid VM type: %q", vmType) return fmt.Errorf("unsupported state version: %d (%s)", ver, ver.String())
} }
state, err := createInitialState(elfProgram) state, err := createInitialState(elfProgram)
...@@ -118,15 +112,19 @@ func LoadELF(ctx *cli.Context) error { ...@@ -118,15 +112,19 @@ func LoadELF(ctx *cli.Context) error {
return serialize.Write(ctx.Path(LoadELFOutFlag.Name), versionedState, OutFilePerm) return serialize.Write(ctx.Path(LoadELFOutFlag.Name), versionedState, OutFilePerm)
} }
var LoadELFCommand = &cli.Command{ func CreateLoadELFCommand(action cli.ActionFunc) *cli.Command {
Name: "load-elf", return &cli.Command{
Usage: "Load ELF file into Cannon state", Name: "load-elf",
Description: "Load ELF file into Cannon state", Usage: "Load ELF file into Cannon state",
Action: LoadELF, Description: "Load ELF file into Cannon state",
Flags: []cli.Flag{ Action: action,
LoadELFVMTypeFlag, Flags: []cli.Flag{
LoadELFPathFlag, LoadELFVMTypeFlag,
LoadELFOutFlag, LoadELFPathFlag,
LoadELFMetaFlag, LoadELFOutFlag,
}, LoadELFMetaFlag,
},
}
} }
var LoadELFCommand = CreateLoadELFCommand(LoadELF)
...@@ -496,26 +496,30 @@ func Run(ctx *cli.Context) error { ...@@ -496,26 +496,30 @@ func Run(ctx *cli.Context) error {
return nil return nil
} }
var RunCommand = &cli.Command{ func CreateRunCommand(action cli.ActionFunc) *cli.Command {
Name: "run", return &cli.Command{
Usage: "Run VM step(s) and generate proof data to replicate onchain.", Name: "run",
Description: "Run VM step(s) and generate proof data to replicate onchain. See flags to match when to output a proof, a snapshot, or to stop early.", Usage: "Run VM step(s) and generate proof data to replicate onchain.",
Action: Run, Description: "Run VM step(s) and generate proof data to replicate onchain. See flags to match when to output a proof, a snapshot, or to stop early.",
Flags: []cli.Flag{ Action: action,
RunInputFlag, Flags: []cli.Flag{
RunOutputFlag, RunInputFlag,
RunProofAtFlag, RunOutputFlag,
RunProofFmtFlag, RunProofAtFlag,
RunSnapshotAtFlag, RunProofFmtFlag,
RunSnapshotFmtFlag, RunSnapshotAtFlag,
RunStopAtFlag, RunSnapshotFmtFlag,
RunStopAtPreimageFlag, RunStopAtFlag,
RunStopAtPreimageTypeFlag, RunStopAtPreimageFlag,
RunStopAtPreimageLargerThanFlag, RunStopAtPreimageTypeFlag,
RunMetaFlag, RunStopAtPreimageLargerThanFlag,
RunInfoAtFlag, RunMetaFlag,
RunPProfCPU, RunInfoAtFlag,
RunDebugFlag, RunPProfCPU,
RunDebugInfoFlag, RunDebugFlag,
}, RunDebugInfoFlag,
},
}
} }
var RunCommand = CreateRunCommand(Run)
...@@ -39,13 +39,17 @@ func Witness(ctx *cli.Context) error { ...@@ -39,13 +39,17 @@ func Witness(ctx *cli.Context) error {
return nil return nil
} }
var WitnessCommand = &cli.Command{ func CreateWitnessCommand(action cli.ActionFunc) *cli.Command {
Name: "witness", return &cli.Command{
Usage: "Convert a Cannon JSON state into a binary witness", Name: "witness",
Description: "Convert a Cannon JSON state into a binary witness. The hash of the witness is written to stdout", Usage: "Convert a Cannon JSON state into a binary witness",
Action: Witness, Description: "Convert a Cannon JSON state into a binary witness. The hash of the witness is written to stdout",
Flags: []cli.Flag{ Action: action,
WitnessInputFlag, Flags: []cli.Flag{
WitnessOutputFlag, WitnessInputFlag,
}, WitnessOutputFlag,
},
}
} }
var WitnessCommand = CreateWitnessCommand(Witness)
package versions
import (
"fmt"
"io"
"github.com/ethereum-optimism/optimism/cannon/serialize"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
)
func DetectVersion(path string) (StateVersion, error) {
if !serialize.IsBinaryFile(path) {
return VersionSingleThreaded, nil
}
var f io.ReadCloser
f, err := ioutil.OpenDecompressed(path)
if err != nil {
return 0, fmt.Errorf("failed to open file %q: %w", path, err)
}
defer f.Close()
var ver StateVersion
bin := serialize.NewBinaryReader(f)
if err := bin.ReadUInt(&ver); err != nil {
return 0, err
}
switch ver {
case VersionSingleThreaded, VersionMultiThreaded:
return ver, nil
default:
return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver)
}
}
package versions
import (
"os"
"path/filepath"
"testing"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/multithreaded"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/singlethreaded"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/stretchr/testify/require"
)
func TestDetectVersion(t *testing.T) {
t.Run("SingleThreadedJSON", func(t *testing.T) {
state, err := NewFromState(singlethreaded.CreateEmptyState())
require.NoError(t, err)
path := writeToFile(t, "state.json", state)
version, err := DetectVersion(path)
require.NoError(t, err)
require.Equal(t, VersionSingleThreaded, version)
})
t.Run("SingleThreadedBinary", func(t *testing.T) {
state, err := NewFromState(singlethreaded.CreateEmptyState())
require.NoError(t, err)
path := writeToFile(t, "state.bin.gz", state)
version, err := DetectVersion(path)
require.NoError(t, err)
require.Equal(t, VersionSingleThreaded, version)
})
t.Run("MultiThreadedBinary", func(t *testing.T) {
state, err := NewFromState(multithreaded.CreateEmptyState())
require.NoError(t, err)
path := writeToFile(t, "state.bin.gz", state)
version, err := DetectVersion(path)
require.NoError(t, err)
require.Equal(t, VersionMultiThreaded, version)
})
}
func TestDetectVersionInvalid(t *testing.T) {
t.Run("bad gzip", func(t *testing.T) {
dir := t.TempDir()
filename := "state.bin.gz"
path := filepath.Join(dir, filename)
require.NoError(t, os.WriteFile(path, []byte("ekans"), 0o644))
_, err := DetectVersion(path)
require.ErrorContains(t, err, "failed to open file")
})
t.Run("unknown version", func(t *testing.T) {
dir := t.TempDir()
filename := "state.bin.gz"
path := filepath.Join(dir, filename)
const badVersion = 0xFF
err := ioutil.WriteCompressedBytes(path, []byte{badVersion}, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
require.NoError(t, err)
_, err = DetectVersion(path)
require.ErrorIs(t, err, ErrUnknownVersion)
})
}
...@@ -16,6 +16,7 @@ import ( ...@@ -16,6 +16,7 @@ import (
type StateVersion uint8 type StateVersion uint8
const ( const (
// VersionSingleThreaded is the version of the Cannon STF found in op-contracts/v1.6.0 - https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.6.0/packages/contracts-bedrock/src/cannon/MIPS.sol
VersionSingleThreaded StateVersion = iota VersionSingleThreaded StateVersion = iota
VersionMultiThreaded VersionMultiThreaded
) )
...@@ -25,6 +26,8 @@ var ( ...@@ -25,6 +26,8 @@ var (
ErrJsonNotSupported = errors.New("json not supported") ErrJsonNotSupported = errors.New("json not supported")
) )
var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded}
func LoadStateFromFile(path string) (*VersionedState, error) { func LoadStateFromFile(path string) (*VersionedState, error) {
if !serialize.IsBinaryFile(path) { if !serialize.IsBinaryFile(path) {
// Always use singlethreaded for JSON states // Always use singlethreaded for JSON states
...@@ -103,3 +106,25 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) { ...@@ -103,3 +106,25 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) {
} }
return json.Marshal(s.FPVMState) return json.Marshal(s.FPVMState)
} }
func (s StateVersion) String() string {
switch s {
case VersionSingleThreaded:
return "singlethreaded"
case VersionMultiThreaded:
return "multithreaded"
default:
return "unknown"
}
}
func ParseStateVersion(ver string) (StateVersion, error) {
switch ver {
case "singlethreaded":
return VersionSingleThreaded, nil
case "multithreaded":
return VersionMultiThreaded, nil
default:
return StateVersion(0), errors.New("unknown state version")
}
}
package main
import (
"context"
"embed"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
)
// use the all directive to ensure the .gitkeep file is retained and avoid compiler errors
//go:embed all:embeds
var vmFS embed.FS
const baseDir = "embeds"
func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error {
switch ver {
case versions.VersionSingleThreaded, versions.VersionMultiThreaded:
default:
return errors.New("unsupported version")
}
cannonProgramName := vmFilename(ver)
cannonProgramBin, err := vmFS.ReadFile(cannonProgramName)
if err != nil {
return err
}
cannonProgramPath, err := extractTempFile(filepath.Base(cannonProgramName), cannonProgramBin)
if err != nil {
fmt.Fprintf(os.Stderr, "Error extracting %s: %v\n", cannonProgramName, err)
os.Exit(1)
}
defer os.Remove(cannonProgramPath)
if err := os.Chmod(cannonProgramPath, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Error setting execute permission for %s: %v\n", cannonProgramName, err)
os.Exit(1)
}
// nosemgrep: go.lang.security.audit.dangerous-exec-command.dangerous-exec-command
cmd := exec.CommandContext(ctx, cannonProgramPath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
return fmt.Errorf("unable to launch cannon-impl program: %w", err)
}
if err := cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
// relay exit code to the parent process
os.Exit(exitErr.ExitCode())
} else {
return fmt.Errorf("failed to wait for cannon-impl program: %w", err)
}
}
return nil
}
func extractTempFile(name string, data []byte) (string, error) {
tempDir := os.TempDir()
tempFile, err := os.CreateTemp(tempDir, name+"-*")
if err != nil {
return "", err
}
defer tempFile.Close()
if _, err := tempFile.Write(data); err != nil {
return "", err
}
return tempFile.Name(), nil
}
func vmFilename(ver versions.StateVersion) string {
return fmt.Sprintf("%s/cannon-%d", baseDir, ver)
}
package main
import (
"fmt"
"math"
"strconv"
"strings"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
)
func List(ctx *cli.Context) error {
return list()
}
func list() error {
fmt.Println("Available cannon versions:")
artifacts, err := getArtifacts()
if err != nil {
return err
}
for _, art := range artifacts {
if art.isValid() {
fmt.Printf("filename: %s\tversion: %s (%d)\n", art.filename, versions.StateVersion(art.ver), art.ver)
} else {
fmt.Printf("filename: %s\tversion: %s\n", art.filename, "unknown")
}
}
return nil
}
func getArtifacts() ([]artifact, error) {
var ret []artifact
entries, err := vmFS.ReadDir(baseDir)
if err != nil {
return nil, err
}
for _, entry := range entries {
filename := entry.Name()
toks := strings.Split(filename, "-")
if len(toks) != 2 {
continue
}
if toks[0] != "cannon" {
continue
}
ver, err := strconv.ParseUint(toks[1], 10, 8)
if err != nil {
ret = append(ret, artifact{filename, math.MaxUint64})
continue
}
ret = append(ret, artifact{filename, ver})
}
return ret, nil
}
type artifact struct {
filename string
ver uint64
}
func (a artifact) isValid() bool {
return a.ver != math.MaxUint64
}
var ListCommand = &cli.Command{
Name: "list",
Usage: "List embedded Cannon VM implementations",
Description: "List embedded Cannon VM implementations",
Action: List,
}
package main
import (
"fmt"
"os"
"github.com/ethereum-optimism/optimism/cannon/cmd"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
"github.com/urfave/cli/v2"
)
func LoadELF(ctx *cli.Context) error {
if len(os.Args) == 2 && os.Args[2] == "--help" {
if err := list(); err != nil {
return err
}
fmt.Println("use `--type <vm type> --help` to get more detailed help")
}
typ, err := parseFlag(os.Args[1:], "--type")
if err != nil {
return err
}
ver, err := versions.ParseStateVersion(typ)
if err != nil {
return err
}
return ExecuteCannon(ctx.Context, os.Args[1:], ver)
}
var LoadELFCommand = cmd.CreateLoadELFCommand(LoadELF)
package main
import (
"context"
"errors"
"fmt"
"os"
"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
"github.com/urfave/cli/v2"
)
func main() {
app := cli.NewApp()
app.Name = "multicannon"
app.Usage = "MIPS Fault Proof tool"
app.Description = "MIPS Fault Proof tool"
app.Commands = []*cli.Command{
LoadELFCommand,
WitnessCommand,
RunCommand,
ListCommand,
}
ctx := ctxinterrupt.WithCancelOnInterrupt(context.Background())
err := app.RunContext(ctx, os.Args)
if err != nil {
if errors.Is(err, ctx.Err()) {
_, _ = fmt.Fprintf(os.Stderr, "command interrupted")
os.Exit(130)
} else {
_, _ = fmt.Fprintf(os.Stderr, "error: %v", err)
os.Exit(1)
}
}
}
package main
import (
"fmt"
"os"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
)
func Run(ctx *cli.Context) error {
fmt.Printf("args %v\n", os.Args[:])
if len(os.Args) == 3 && os.Args[2] == "--help" {
if err := list(); err != nil {
return err
}
fmt.Println("use `--input <valid input file> --help` to get more detailed help")
}
inputPath, err := parsePathFlag(os.Args[1:], "--input")
if err != nil {
return err
}
version, err := versions.DetectVersion(inputPath)
if err != nil {
return err
}
return ExecuteCannon(ctx.Context, os.Args[1:], version)
}
// var RunCommand = cmd.CreateRunCommand(Run)
var RunCommand = &cli.Command{
Name: "run",
Usage: "Run VM step(s) and generate proof data to replicate onchain.",
Description: "Run VM step(s) and generate proof data to replicate onchain. See flags to match when to output a proof, a snapshot, or to stop early.",
Action: Run,
SkipFlagParsing: true,
}
package main
import (
"errors"
"fmt"
"os"
"strings"
)
// parseFlag reads a flag argument. It assumes the flag has an argument
func parseFlag(args []string, flag string) (string, error) {
for i := 0; i < len(args); i++ {
arg := args[i]
if strings.HasPrefix(arg, flag) {
toks := strings.Split(arg, "=")
if len(toks) == 2 {
return toks[1], nil
} else if i+1 == len(args) {
return "", fmt.Errorf("flag needs an argument: %s", flag)
} else {
return args[i+1], nil
}
}
}
return "", fmt.Errorf("missing flag: %s", flag)
}
func parsePathFlag(args []string, flag string) (string, error) {
path, err := parseFlag(args, flag)
if err != nil {
return "", err
}
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
return "", fmt.Errorf("file `%s` does not exist", path)
}
return path, nil
}
package main
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestParseFlag(t *testing.T) {
cases := []struct {
name string
args string
flag string
expect string
expectErr string
}{
{
name: "bar=one",
args: "--foo --bar=one --baz",
flag: "--bar",
expect: "one",
},
{
name: "bar one",
args: "--foo --bar one --baz",
flag: "--bar",
expect: "one",
},
{
name: "bar one first flag",
args: "--bar one --foo two --baz three",
flag: "--bar",
expect: "one",
},
{
name: "bar one last flag",
args: "--foo --baz --bar one",
flag: "--bar",
expect: "one",
},
{
name: "non-existent flag",
args: "--foo one",
flag: "--bar",
expectErr: "missing flag",
},
{
name: "empty args",
args: "",
flag: "--foo",
expectErr: "missing flag",
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
args := strings.Split(tt.args, " ")
result, err := parseFlag(args, tt.flag)
if tt.expectErr != "" {
require.ErrorContains(t, err, tt.expectErr)
} else {
require.NoError(t, err)
require.Equal(t, tt.expect, result)
}
})
}
}
package main
import (
"fmt"
"os"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/cannon/cmd"
"github.com/ethereum-optimism/optimism/cannon/mipsevm/versions"
)
func Witness(ctx *cli.Context) error {
if len(os.Args) == 3 && os.Args[2] == "--help" {
if err := list(); err != nil {
return err
}
fmt.Println("use `--input <valid input file> --help` to get more detailed help")
}
inputPath, err := parsePathFlag(os.Args[1:], "--input")
if err != nil {
return err
}
version, err := versions.DetectVersion(inputPath)
if err != nil {
return err
}
return ExecuteCannon(ctx.Context, os.Args[1:], version)
}
var WitnessCommand = cmd.CreateWitnessCommand(Witness)
...@@ -35,8 +35,8 @@ RUN --mount=type=cache,target=/root/.cache/go-build cd op-program && make op-pro ...@@ -35,8 +35,8 @@ RUN --mount=type=cache,target=/root/.cache/go-build cd op-program && make op-pro
GOOS=linux GOARCH=mips GOMIPS=softfloat GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_PROGRAM_VERSION" GOOS=linux GOARCH=mips GOMIPS=softfloat GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$OP_PROGRAM_VERSION"
# Run the op-program-client.elf binary directly through cannon's load-elf subcommand. # Run the op-program-client.elf binary directly through cannon's load-elf subcommand.
RUN /app/cannon/bin/cannon load-elf --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate.json --meta "" RUN /app/cannon/bin/cannon load-elf --type singlethreaded --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate.json --meta ""
RUN /app/cannon/bin/cannon load-elf --type cannon-mt --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate-mt.bin.gz --meta "" RUN /app/cannon/bin/cannon load-elf --type multithreaded --path /app/op-program/bin/op-program-client.elf --out /app/op-program/bin/prestate-mt.bin.gz --meta ""
# Generate the prestate proof containing the absolute pre-state hash. # Generate the prestate proof containing the absolute pre-state hash.
RUN /app/cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input /app/op-program/bin/prestate.json --meta "" --proof-fmt '/app/op-program/bin/%d.json' --output "" RUN /app/cannon/bin/cannon run --proof-at '=0' --stop-at '=1' --input /app/op-program/bin/prestate.json --meta "" --proof-fmt '/app/op-program/bin/%d.json' --output ""
......
...@@ -46,8 +46,16 @@ ARG TARGETARCH ...@@ -46,8 +46,16 @@ ARG TARGETARCH
# Build the Go services, utilizing caches and share the many common packages. # Build the Go services, utilizing caches and share the many common packages.
# The "id" defaults to the value of "target", the cache will thus be reused during this build. # The "id" defaults to the value of "target", the cache will thus be reused during this build.
# "sharing" defaults to "shared", the cache will thus be available to other concurrent docker builds. # "sharing" defaults to "shared", the cache will thus be available to other concurrent docker builds.
# For now fetch the v1 cannon binary from the op-challenger image
#FROM --platform=$BUILDPLATFORM us-docker.pkg.dev/oplabs-tools-artifacts/images/op-challenger:v1.1.0 AS cannon-builder-0
FROM --platform=$BUILDPLATFORM builder AS cannon-builder FROM --platform=$BUILDPLATFORM builder AS cannon-builder
ARG CANNON_VERSION=v0.0.0 # note: bump this CANNON_VERSION when the VM behavior changes
ARG CANNON_VERSION=v1.0.0
# uncomment these lines once there's a new Cannon version available
#COPY --from=cannon-builder-0 /usr/local/bin/cannon ./cannon/multicannon/embeds/cannon-0
#COPY --from=cannon-builder-0 /usr/local/bin/cannon ./cannon/multicannon/embeds/cannon-1
RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build cd cannon && make cannon \ RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build cd cannon && make cannon \
GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$CANNON_VERSION" GOOS=$TARGETOS GOARCH=$TARGETARCH GITCOMMIT=$GIT_COMMIT GITDATE=$GIT_DATE VERSION="$CANNON_VERSION"
...@@ -158,4 +166,4 @@ CMD ["op-supervisor"] ...@@ -158,4 +166,4 @@ CMD ["op-supervisor"]
FROM --platform=$TARGETPLATFORM $TARGET_BASE_IMAGE AS op-deployer-target FROM --platform=$TARGETPLATFORM $TARGET_BASE_IMAGE AS op-deployer-target
COPY --from=op-deployer-builder /app/op-chain-ops/bin/op-deployer /usr/local/bin/ COPY --from=op-deployer-builder /app/op-chain-ops/bin/op-deployer /usr/local/bin/
CMD ["op-deployer"] CMD ["op-deployer"]
\ No newline at end of file
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