diff --git a/cannon/cmd/load_elf.go b/cannon/cmd/load_elf.go index d28455479c425..a5ee43d13986e 100644 --- a/cannon/cmd/load_elf.go +++ b/cannon/cmd/load_elf.go @@ -20,7 +20,7 @@ import ( var ( LoadELFVMTypeFlag = &cli.StringFlag{ Name: "type", - Usage: "VM type to create state for. Valid options: " + openum.EnumString(stateVersions()), + Usage: "VM type to create state for. Valid options: " + openum.EnumString(versions.GetStateVersionStrings()), Required: true, } LoadELFPathFlag = &cli.PathFlag{ @@ -43,14 +43,6 @@ var ( } ) -func stateVersions() []string { - vers := make([]string, len(versions.StateVersionTypes)) - for i, v := range versions.StateVersionTypes { - vers[i] = v.String() - } - return vers -} - func LoadELF(ctx *cli.Context) error { elfPath := ctx.Path(LoadELFPathFlag.Name) elfProgram, err := elf.Open(elfPath) @@ -69,7 +61,7 @@ func LoadELF(ctx *cli.Context) error { return err } switch ver { - case versions.VersionSingleThreaded2: + case versions.GetCurrentSingleThreaded(): createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { return program.LoadELF(f, singlethreaded.CreateInitialState) } @@ -80,7 +72,7 @@ func LoadELF(ctx *cli.Context) error { } return program.PatchStack(state) } - case versions.VersionMultiThreaded_v2, versions.VersionMultiThreaded64_v3: + case versions.GetCurrentMultiThreaded(), versions.GetCurrentMultiThreaded64(): createInitialState = func(f *elf.File) (mipsevm.FPVMState, error) { return program.LoadELF(f, multithreaded.CreateInitialState) } diff --git a/cannon/mipsevm/versions/detect.go b/cannon/mipsevm/versions/detect.go index c667ade282461..3e8d4e317df18 100644 --- a/cannon/mipsevm/versions/detect.go +++ b/cannon/mipsevm/versions/detect.go @@ -26,10 +26,8 @@ func DetectVersion(path string) (StateVersion, error) { return 0, err } - switch ver { - case VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2, VersionMultiThreaded64, VersionMultiThreaded64_v2, VersionMultiThreaded_v2, VersionMultiThreaded64_v3: - return ver, nil - default: + if !IsValidStateVersion(ver) { return 0, fmt.Errorf("%w: %d", ErrUnknownVersion, ver) } + return ver, nil } diff --git a/cannon/mipsevm/versions/detect_test.go b/cannon/mipsevm/versions/detect_test.go index f0d9e19e97a6c..efdc650e9f02b 100644 --- a/cannon/mipsevm/versions/detect_test.go +++ b/cannon/mipsevm/versions/detect_test.go @@ -50,7 +50,7 @@ func TestDetectVersion_fromFile(t *testing.T) { // Check that the latest supported versions write new states in a way that is detected correctly func TestDetectVersion_singleThreadedBinary(t *testing.T) { - targetVersion := VersionSingleThreaded2 + targetVersion := GetCurrentSingleThreaded() if !arch.IsMips32 { t.Skip("Single-threaded states are not supported for 64-bit VMs") } @@ -64,9 +64,9 @@ func TestDetectVersion_singleThreadedBinary(t *testing.T) { } func TestDetectVersion_multiThreadedBinary(t *testing.T) { - targetVersion := VersionMultiThreaded_v2 + targetVersion := GetCurrentMultiThreaded() if !arch.IsMips32 { - targetVersion = VersionMultiThreaded64_v3 + targetVersion = GetCurrentMultiThreaded64() } state, err := NewFromState(multithreaded.CreateEmptyState()) diff --git a/cannon/mipsevm/versions/state.go b/cannon/mipsevm/versions/state.go index 1e6c17ed14237..bba831bfc3595 100644 --- a/cannon/mipsevm/versions/state.go +++ b/cannon/mipsevm/versions/state.go @@ -14,34 +14,12 @@ import ( "github.com/ethereum-optimism/optimism/op-service/serialize" ) -type StateVersion uint8 - -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 - // VersionMultiThreaded is the original implementation of 32-bit multithreaded cannon, tagged at cannon/v1.3.0 - VersionMultiThreaded - // VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall - // This is the latest 32-bit single-threaded vm - VersionSingleThreaded2 - // VersionMultiThreaded64 is the original 64-bit MTCannon implementation (pre-audit), tagged at cannon/v1.2.0 - VersionMultiThreaded64 - // VersionMultiThreaded64_v2 includes an audit fix to ensure futex values are always 32-bit, tagged at cannon/v1.3.0 - VersionMultiThreaded64_v2 - // VersionMultiThreaded_v2 is the latest 32-bit multithreaded vm - VersionMultiThreaded_v2 - // VersionMultiThreaded64_v3 is the latest 64-bit multithreaded vm - VersionMultiThreaded64_v3 -) - var ( ErrUnknownVersion = errors.New("unknown version") ErrJsonNotSupported = errors.New("json not supported") ErrUnsupportedMipsArch = errors.New("mips architecture is not supported") ) -var StateVersionTypes = []StateVersion{VersionSingleThreaded, VersionMultiThreaded, VersionSingleThreaded2, VersionMultiThreaded64, VersionMultiThreaded64_v2, VersionMultiThreaded_v2, VersionMultiThreaded64_v3} - func LoadStateFromFile(path string) (*VersionedState, error) { if !serialize.IsBinaryFile(path) { // Always use singlethreaded for JSON states @@ -61,18 +39,18 @@ func NewFromState(state mipsevm.FPVMState) (*VersionedState, error) { return nil, ErrUnsupportedMipsArch } return &VersionedState{ - Version: VersionSingleThreaded2, + Version: GetCurrentSingleThreaded(), FPVMState: state, }, nil case *multithreaded.State: if arch.IsMips32 { return &VersionedState{ - Version: VersionMultiThreaded_v2, + Version: GetCurrentMultiThreaded(), FPVMState: state, }, nil } else { return &VersionedState{ - Version: VersionMultiThreaded64_v3, + Version: GetCurrentMultiThreaded64(), FPVMState: state, }, nil } @@ -103,7 +81,7 @@ func (s *VersionedState) Deserialize(in io.Reader) error { } switch s.Version { - case VersionSingleThreaded2: + case GetCurrentSingleThreaded(): if !arch.IsMips32 { return ErrUnsupportedMipsArch } @@ -113,7 +91,7 @@ func (s *VersionedState) Deserialize(in io.Reader) error { } s.FPVMState = state return nil - case VersionMultiThreaded_v2: + case GetCurrentMultiThreaded(): if !arch.IsMips32 { return ErrUnsupportedMipsArch } @@ -123,7 +101,7 @@ func (s *VersionedState) Deserialize(in io.Reader) error { } s.FPVMState = state return nil - case VersionMultiThreaded64_v3: + case GetCurrentMultiThreaded64(): if arch.IsMips32 { return ErrUnsupportedMipsArch } @@ -149,45 +127,3 @@ func (s *VersionedState) MarshalJSON() ([]byte, error) { } return json.Marshal(s.FPVMState) } - -func (s StateVersion) String() string { - switch s { - case VersionSingleThreaded: - return "singlethreaded" - case VersionMultiThreaded: - return "multithreaded" - case VersionSingleThreaded2: - return "singlethreaded-2" - case VersionMultiThreaded64: - return "multithreaded64" - case VersionMultiThreaded64_v2: - return "multithreaded64-2" - case VersionMultiThreaded_v2: - return "multithreaded-2" - case VersionMultiThreaded64_v3: - return "multithreaded64-3" - default: - return "unknown" - } -} - -func ParseStateVersion(ver string) (StateVersion, error) { - switch ver { - case "singlethreaded": - return VersionSingleThreaded, nil - case "multithreaded": - return VersionMultiThreaded, nil - case "singlethreaded-2": - return VersionSingleThreaded2, nil - case "multithreaded64": - return VersionMultiThreaded64, nil - case "multithreaded64-2": - return VersionMultiThreaded64_v2, nil - case "multithreaded-2": - return VersionMultiThreaded_v2, nil - case "multithreaded64-3": - return VersionMultiThreaded64_v3, nil - default: - return StateVersion(0), errors.New("unknown state version") - } -} diff --git a/cannon/mipsevm/versions/state64_test.go b/cannon/mipsevm/versions/state64_test.go index d86bee3e81d34..b8f74d1db0385 100644 --- a/cannon/mipsevm/versions/state64_test.go +++ b/cannon/mipsevm/versions/state64_test.go @@ -19,7 +19,7 @@ func TestNewFromState(t *testing.T) { actual, err := NewFromState(multithreaded.CreateEmptyState()) require.NoError(t, err) require.IsType(t, &multithreaded.State{}, actual.FPVMState) - require.Equal(t, VersionMultiThreaded64_v3, actual.Version) + require.Equal(t, GetCurrentMultiThreaded64(), actual.Version) }) } @@ -40,7 +40,7 @@ func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { version StateVersion createState func() mipsevm.FPVMState }{ - {VersionMultiThreaded64_v3, func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }}, + {GetCurrentMultiThreaded64(), func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }}, } for _, test := range tests { test := test diff --git a/cannon/mipsevm/versions/state_test.go b/cannon/mipsevm/versions/state_test.go index 1768ecaea0309..930906b6fbb01 100644 --- a/cannon/mipsevm/versions/state_test.go +++ b/cannon/mipsevm/versions/state_test.go @@ -20,14 +20,14 @@ func TestNewFromState(t *testing.T) { actual, err := NewFromState(singlethreaded.CreateEmptyState()) require.NoError(t, err) require.IsType(t, &singlethreaded.State{}, actual.FPVMState) - require.Equal(t, VersionSingleThreaded2, actual.Version) + require.Equal(t, GetCurrentSingleThreaded(), actual.Version) }) t.Run("multithreaded-latestVersion", func(t *testing.T) { actual, err := NewFromState(multithreaded.CreateEmptyState()) require.NoError(t, err) require.IsType(t, &multithreaded.State{}, actual.FPVMState) - require.Equal(t, VersionMultiThreaded_v2, actual.Version) + require.Equal(t, GetCurrentMultiThreaded(), actual.Version) }) } @@ -58,8 +58,8 @@ func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { version StateVersion createState func() mipsevm.FPVMState }{ - {VersionSingleThreaded2, func() mipsevm.FPVMState { return singlethreaded.CreateEmptyState() }}, - {VersionMultiThreaded_v2, func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }}, + {GetCurrentSingleThreaded(), func() mipsevm.FPVMState { return singlethreaded.CreateEmptyState() }}, + {GetCurrentMultiThreaded(), func() mipsevm.FPVMState { return multithreaded.CreateEmptyState() }}, } for _, test := range tests { test := test @@ -75,16 +75,6 @@ func TestVersionsOtherThanZeroDoNotSupportJSON(t *testing.T) { } } -func TestParseStateVersion(t *testing.T) { - for _, version := range StateVersionTypes { - t.Run(version.String(), func(t *testing.T) { - result, err := ParseStateVersion(version.String()) - require.NoError(t, err) - require.Equal(t, version, result) - }) - } -} - func writeToFile(t *testing.T, filename string, data serialize.Serializable) string { dir := t.TempDir() path := filepath.Join(dir, filename) diff --git a/cannon/mipsevm/versions/version.go b/cannon/mipsevm/versions/version.go new file mode 100644 index 0000000000000..0c33144e130a9 --- /dev/null +++ b/cannon/mipsevm/versions/version.go @@ -0,0 +1,105 @@ +package versions + +import ( + "errors" + "slices" +) + +type StateVersion uint8 + +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 + // VersionMultiThreaded is the original implementation of 32-bit multithreaded cannon, tagged at cannon/v1.3.0 + VersionMultiThreaded + // VersionSingleThreaded2 is based on VersionSingleThreaded with the addition of support for fcntl(F_GETFD) syscall + // This is the latest 32-bit single-threaded vm + VersionSingleThreaded2 + // VersionMultiThreaded64 is the original 64-bit MTCannon implementation (pre-audit), tagged at cannon/v1.2.0 + VersionMultiThreaded64 + // VersionMultiThreaded64_v2 includes an audit fix to ensure futex values are always 32-bit, tagged at cannon/v1.3.0 + VersionMultiThreaded64_v2 + // VersionMultiThreaded_v2 is the latest 32-bit multithreaded vm + VersionMultiThreaded_v2 + // VersionMultiThreaded64_v3 is the latest 64-bit multithreaded vm + VersionMultiThreaded64_v3 +) + +var StateVersionTypes = []StateVersion{ + VersionSingleThreaded, + VersionMultiThreaded, + VersionSingleThreaded2, + VersionMultiThreaded64, + VersionMultiThreaded64_v2, + VersionMultiThreaded_v2, + VersionMultiThreaded64_v3, +} + +func (s StateVersion) String() string { + switch s { + case VersionSingleThreaded: + return "singlethreaded" + case VersionMultiThreaded: + return "multithreaded" + case VersionSingleThreaded2: + return "singlethreaded-2" + case VersionMultiThreaded64: + return "multithreaded64" + case VersionMultiThreaded64_v2: + return "multithreaded64-2" + case VersionMultiThreaded_v2: + return "multithreaded-2" + case VersionMultiThreaded64_v3: + return "multithreaded64-3" + default: + return "unknown" + } +} + +func ParseStateVersion(ver string) (StateVersion, error) { + switch ver { + case "singlethreaded": + return VersionSingleThreaded, nil + case "multithreaded": + return VersionMultiThreaded, nil + case "singlethreaded-2": + return VersionSingleThreaded2, nil + case "multithreaded64": + return VersionMultiThreaded64, nil + case "multithreaded64-2": + return VersionMultiThreaded64_v2, nil + case "multithreaded-2": + return VersionMultiThreaded_v2, nil + case "multithreaded64-3": + return VersionMultiThreaded64_v3, nil + default: + return StateVersion(0), errors.New("unknown state version") + } +} + +func IsValidStateVersion(ver StateVersion) bool { + return slices.Contains(StateVersionTypes, ver) +} + +func GetStateVersionStrings() []string { + vers := make([]string, len(StateVersionTypes)) + for i, v := range StateVersionTypes { + vers[i] = v.String() + } + return vers +} + +// GetCurrentMultiThreaded64 returns the 64-bit multithreaded VM version that is currently supported +func GetCurrentMultiThreaded64() StateVersion { + return VersionMultiThreaded64_v3 +} + +// GetCurrentMultiThreaded returns the 32-bit multithreaded VM version that is currently supported +func GetCurrentMultiThreaded() StateVersion { + return VersionMultiThreaded_v2 +} + +// GetCurrentSingleThreaded returns the 32-bit single-threaded VM version that is currently supported +func GetCurrentSingleThreaded() StateVersion { + return VersionSingleThreaded2 +} diff --git a/cannon/mipsevm/versions/version_test.go b/cannon/mipsevm/versions/version_test.go new file mode 100644 index 0000000000000..2bfcf335edd30 --- /dev/null +++ b/cannon/mipsevm/versions/version_test.go @@ -0,0 +1,17 @@ +package versions + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseStateVersion(t *testing.T) { + for _, version := range StateVersionTypes { + t.Run(version.String(), func(t *testing.T) { + result, err := ParseStateVersion(version.String()) + require.NoError(t, err) + require.Equal(t, version, result) + }) + } +} diff --git a/cannon/multicannon/exec.go b/cannon/multicannon/exec.go index 982b83c556925..f8709a2fe1f63 100644 --- a/cannon/multicannon/exec.go +++ b/cannon/multicannon/exec.go @@ -8,7 +8,6 @@ import ( "os" "os/exec" "path/filepath" - "slices" "github.com/ethereum-optimism/optimism/cannon/mipsevm/versions" ) @@ -21,7 +20,7 @@ var vmFS embed.FS const baseDir = "embeds" func ExecuteCannon(ctx context.Context, args []string, ver versions.StateVersion) error { - if !slices.Contains(versions.StateVersionTypes, ver) { + if !versions.IsValidStateVersion(ver) { return errors.New("unsupported version") }