Commit 98038608 authored by Inphi's avatar Inphi Committed by GitHub

op-challenger: Improve GameType typing (#11012)

parent 1440a519
...@@ -4,10 +4,10 @@ import ( ...@@ -4,10 +4,10 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/flags" "github.com/ethereum-optimism/optimism/op-challenger/flags"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/tools" "github.com/ethereum-optimism/optimism/op-challenger/tools"
opservice "github.com/ethereum-optimism/optimism/op-service" opservice "github.com/ethereum-optimism/optimism/op-service"
oplog "github.com/ethereum-optimism/optimism/op-service/log" oplog "github.com/ethereum-optimism/optimism/op-service/log"
...@@ -22,7 +22,7 @@ var ( ...@@ -22,7 +22,7 @@ var (
Name: "trace-type", Name: "trace-type",
Usage: "Trace types to support.", Usage: "Trace types to support.",
EnvVars: opservice.PrefixEnvVar(flags.EnvVarPrefix, "TRACE_TYPE"), EnvVars: opservice.PrefixEnvVar(flags.EnvVarPrefix, "TRACE_TYPE"),
Value: config.TraceTypeCannon.String(), Value: types.TraceTypeCannon.String(),
} }
OutputRootFlag = &cli.StringFlag{ OutputRootFlag = &cli.StringFlag{
Name: "output-root", Name: "output-root",
......
This diff is collapsed.
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum-optimism/optimism/op-service/oppprof" "github.com/ethereum-optimism/optimism/op-service/oppprof"
...@@ -50,45 +51,6 @@ var ( ...@@ -50,45 +51,6 @@ var (
ErrAsteriscNetworkUnknown = errors.New("unknown asterisc network") ErrAsteriscNetworkUnknown = errors.New("unknown asterisc network")
) )
type TraceType string
const (
TraceTypeAlphabet TraceType = "alphabet"
TraceTypeFast TraceType = "fast"
TraceTypeCannon TraceType = "cannon"
TraceTypeAsterisc TraceType = "asterisc"
TraceTypePermissioned TraceType = "permissioned"
)
var TraceTypes = []TraceType{TraceTypeAlphabet, TraceTypeCannon, TraceTypePermissioned, TraceTypeAsterisc, TraceTypeFast}
func (t TraceType) String() string {
return string(t)
}
// Set implements the Set method required by the [cli.Generic] interface.
func (t *TraceType) Set(value string) error {
if !ValidTraceType(TraceType(value)) {
return fmt.Errorf("unknown trace type: %q", value)
}
*t = TraceType(value)
return nil
}
func (t *TraceType) Clone() any {
cpy := *t
return &cpy
}
func ValidTraceType(value TraceType) bool {
for _, t := range TraceTypes {
if t == value {
return true
}
}
return false
}
const ( const (
DefaultPollInterval = time.Second * 12 DefaultPollInterval = time.Second * 12
DefaultCannonSnapshotFreq = uint(1_000_000_000) DefaultCannonSnapshotFreq = uint(1_000_000_000)
...@@ -122,7 +84,7 @@ type Config struct { ...@@ -122,7 +84,7 @@ type Config struct {
SelectiveClaimResolution bool // Whether to only resolve claims for the claimants in AdditionalBondClaimants union [TxSender.From()] SelectiveClaimResolution bool // Whether to only resolve claims for the claimants in AdditionalBondClaimants union [TxSender.From()]
TraceTypes []TraceType // Type of traces supported TraceTypes []types.TraceType // Type of traces supported
RollupRpc string // L2 Rollup RPC Url RollupRpc string // L2 Rollup RPC Url
...@@ -152,7 +114,7 @@ func NewConfig( ...@@ -152,7 +114,7 @@ func NewConfig(
l2RollupRpc string, l2RollupRpc string,
l2EthRpc string, l2EthRpc string,
datadir string, datadir string,
supportedTraceTypes ...TraceType, supportedTraceTypes ...types.TraceType,
) Config { ) Config {
return Config{ return Config{
L1EthRpc: l1EthRpc, L1EthRpc: l1EthRpc,
...@@ -174,7 +136,7 @@ func NewConfig( ...@@ -174,7 +136,7 @@ func NewConfig(
Datadir: datadir, Datadir: datadir,
Cannon: vm.Config{ Cannon: vm.Config{
VmType: TraceTypeCannon.String(), VmType: types.TraceTypeCannon,
L1: l1EthRpc, L1: l1EthRpc,
L1Beacon: l1BeaconApi, L1Beacon: l1BeaconApi,
L2: l2EthRpc, L2: l2EthRpc,
...@@ -182,7 +144,7 @@ func NewConfig( ...@@ -182,7 +144,7 @@ func NewConfig(
InfoFreq: DefaultCannonInfoFreq, InfoFreq: DefaultCannonInfoFreq,
}, },
Asterisc: vm.Config{ Asterisc: vm.Config{
VmType: TraceTypeAsterisc.String(), VmType: types.TraceTypeAsterisc,
L1: l1EthRpc, L1: l1EthRpc,
L1Beacon: l1BeaconApi, L1Beacon: l1BeaconApi,
L2: l2EthRpc, L2: l2EthRpc,
...@@ -193,7 +155,7 @@ func NewConfig( ...@@ -193,7 +155,7 @@ func NewConfig(
} }
} }
func (c Config) TraceTypeEnabled(t TraceType) bool { func (c Config) TraceTypeEnabled(t types.TraceType) bool {
return slices.Contains(c.TraceTypes, t) return slices.Contains(c.TraceTypes, t)
} }
...@@ -222,7 +184,7 @@ func (c Config) Check() error { ...@@ -222,7 +184,7 @@ func (c Config) Check() error {
if c.MaxConcurrency == 0 { if c.MaxConcurrency == 0 {
return ErrMaxConcurrencyZero return ErrMaxConcurrencyZero
} }
if c.TraceTypeEnabled(TraceTypeCannon) || c.TraceTypeEnabled(TraceTypePermissioned) { if c.TraceTypeEnabled(types.TraceTypeCannon) || c.TraceTypeEnabled(types.TraceTypePermissioned) {
if c.Cannon.VmBin == "" { if c.Cannon.VmBin == "" {
return ErrMissingCannonBin return ErrMissingCannonBin
} }
...@@ -260,7 +222,7 @@ func (c Config) Check() error { ...@@ -260,7 +222,7 @@ func (c Config) Check() error {
return ErrMissingCannonInfoFreq return ErrMissingCannonInfoFreq
} }
} }
if c.TraceTypeEnabled(TraceTypeAsterisc) { if c.TraceTypeEnabled(types.TraceTypeAsterisc) {
if c.Asterisc.VmBin == "" { if c.Asterisc.VmBin == "" {
return ErrMissingAsteriscBin return ErrMissingAsteriscBin
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
...@@ -32,8 +33,8 @@ var ( ...@@ -32,8 +33,8 @@ var (
validAsteriscAbsolutPreStateBaseURL, _ = url.Parse("http://localhost/bar/") validAsteriscAbsolutPreStateBaseURL, _ = url.Parse("http://localhost/bar/")
) )
var cannonTraceTypes = []TraceType{TraceTypeCannon, TraceTypePermissioned} var cannonTraceTypes = []types.TraceType{types.TraceTypeCannon, types.TraceTypePermissioned}
var asteriscTraceTypes = []TraceType{TraceTypeAsterisc} var asteriscTraceTypes = []types.TraceType{types.TraceTypeAsterisc}
func applyValidConfigForCannon(cfg *Config) { func applyValidConfigForCannon(cfg *Config) {
cfg.Cannon.VmBin = validCannonBin cfg.Cannon.VmBin = validCannonBin
...@@ -49,12 +50,12 @@ func applyValidConfigForAsterisc(cfg *Config) { ...@@ -49,12 +50,12 @@ func applyValidConfigForAsterisc(cfg *Config) {
cfg.Asterisc.Network = validAsteriscNetwork cfg.Asterisc.Network = validAsteriscNetwork
} }
func validConfig(traceType TraceType) Config { func validConfig(traceType types.TraceType) Config {
cfg := NewConfig(validGameFactoryAddress, validL1EthRpc, validL1BeaconUrl, validRollupRpc, validL2Rpc, validDatadir, traceType) cfg := NewConfig(validGameFactoryAddress, validL1EthRpc, validL1BeaconUrl, validRollupRpc, validL2Rpc, validDatadir, traceType)
if traceType == TraceTypeCannon || traceType == TraceTypePermissioned { if traceType == types.TraceTypeCannon || traceType == types.TraceTypePermissioned {
applyValidConfigForCannon(&cfg) applyValidConfigForCannon(&cfg)
} }
if traceType == TraceTypeAsterisc { if traceType == types.TraceTypeAsterisc {
applyValidConfigForAsterisc(&cfg) applyValidConfigForAsterisc(&cfg)
} }
return cfg return cfg
...@@ -62,7 +63,7 @@ func validConfig(traceType TraceType) Config { ...@@ -62,7 +63,7 @@ func validConfig(traceType TraceType) Config {
// TestValidConfigIsValid checks that the config provided by validConfig is actually valid // TestValidConfigIsValid checks that the config provided by validConfig is actually valid
func TestValidConfigIsValid(t *testing.T) { func TestValidConfigIsValid(t *testing.T) {
for _, traceType := range TraceTypes { for _, traceType := range types.TraceTypes {
traceType := traceType traceType := traceType
t.Run(traceType.String(), func(t *testing.T) { t.Run(traceType.String(), func(t *testing.T) {
err := validConfig(traceType).Check() err := validConfig(traceType).Check()
...@@ -73,38 +74,38 @@ func TestValidConfigIsValid(t *testing.T) { ...@@ -73,38 +74,38 @@ func TestValidConfigIsValid(t *testing.T) {
func TestTxMgrConfig(t *testing.T) { func TestTxMgrConfig(t *testing.T) {
t.Run("Invalid", func(t *testing.T) { t.Run("Invalid", func(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(types.TraceTypeCannon)
config.TxMgrConfig = txmgr.CLIConfig{} config.TxMgrConfig = txmgr.CLIConfig{}
require.Equal(t, config.Check().Error(), "must provide a L1 RPC url") require.Equal(t, config.Check().Error(), "must provide a L1 RPC url")
}) })
} }
func TestL1EthRpcRequired(t *testing.T) { func TestL1EthRpcRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(types.TraceTypeCannon)
config.L1EthRpc = "" config.L1EthRpc = ""
require.ErrorIs(t, config.Check(), ErrMissingL1EthRPC) require.ErrorIs(t, config.Check(), ErrMissingL1EthRPC)
} }
func TestL1BeaconRequired(t *testing.T) { func TestL1BeaconRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(types.TraceTypeCannon)
config.L1Beacon = "" config.L1Beacon = ""
require.ErrorIs(t, config.Check(), ErrMissingL1Beacon) require.ErrorIs(t, config.Check(), ErrMissingL1Beacon)
} }
func TestGameFactoryAddressRequired(t *testing.T) { func TestGameFactoryAddressRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(types.TraceTypeCannon)
config.GameFactoryAddress = common.Address{} config.GameFactoryAddress = common.Address{}
require.ErrorIs(t, config.Check(), ErrMissingGameFactoryAddress) require.ErrorIs(t, config.Check(), ErrMissingGameFactoryAddress)
} }
func TestSelectiveClaimResolutionNotRequired(t *testing.T) { func TestSelectiveClaimResolutionNotRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(types.TraceTypeCannon)
require.Equal(t, false, config.SelectiveClaimResolution) require.Equal(t, false, config.SelectiveClaimResolution)
require.NoError(t, config.Check()) require.NoError(t, config.Check())
} }
func TestGameAllowlistNotRequired(t *testing.T) { func TestGameAllowlistNotRequired(t *testing.T) {
config := validConfig(TraceTypeCannon) config := validConfig(types.TraceTypeCannon)
config.GameAllowlist = []common.Address{} config.GameAllowlist = []common.Address{}
require.NoError(t, config.Check()) require.NoError(t, config.Check())
} }
...@@ -322,33 +323,33 @@ func TestAsteriscRequiredArgs(t *testing.T) { ...@@ -322,33 +323,33 @@ func TestAsteriscRequiredArgs(t *testing.T) {
} }
func TestDatadirRequired(t *testing.T) { func TestDatadirRequired(t *testing.T) {
config := validConfig(TraceTypeAlphabet) config := validConfig(types.TraceTypeAlphabet)
config.Datadir = "" config.Datadir = ""
require.ErrorIs(t, config.Check(), ErrMissingDatadir) require.ErrorIs(t, config.Check(), ErrMissingDatadir)
} }
func TestMaxConcurrency(t *testing.T) { func TestMaxConcurrency(t *testing.T) {
t.Run("Required", func(t *testing.T) { t.Run("Required", func(t *testing.T) {
config := validConfig(TraceTypeAlphabet) config := validConfig(types.TraceTypeAlphabet)
config.MaxConcurrency = 0 config.MaxConcurrency = 0
require.ErrorIs(t, config.Check(), ErrMaxConcurrencyZero) require.ErrorIs(t, config.Check(), ErrMaxConcurrencyZero)
}) })
t.Run("DefaultToNumberOfCPUs", func(t *testing.T) { t.Run("DefaultToNumberOfCPUs", func(t *testing.T) {
config := validConfig(TraceTypeAlphabet) config := validConfig(types.TraceTypeAlphabet)
require.EqualValues(t, runtime.NumCPU(), config.MaxConcurrency) require.EqualValues(t, runtime.NumCPU(), config.MaxConcurrency)
}) })
} }
func TestHttpPollInterval(t *testing.T) { func TestHttpPollInterval(t *testing.T) {
t.Run("Default", func(t *testing.T) { t.Run("Default", func(t *testing.T) {
config := validConfig(TraceTypeAlphabet) config := validConfig(types.TraceTypeAlphabet)
require.EqualValues(t, DefaultPollInterval, config.PollInterval) require.EqualValues(t, DefaultPollInterval, config.PollInterval)
}) })
} }
func TestRollupRpcRequired(t *testing.T) { func TestRollupRpcRequired(t *testing.T) {
for _, traceType := range TraceTypes { for _, traceType := range types.TraceTypes {
traceType := traceType traceType := traceType
t.Run(traceType.String(), func(t *testing.T) { t.Run(traceType.String(), func(t *testing.T) {
config := validConfig(traceType) config := validConfig(traceType)
...@@ -359,8 +360,8 @@ func TestRollupRpcRequired(t *testing.T) { ...@@ -359,8 +360,8 @@ func TestRollupRpcRequired(t *testing.T) {
} }
func TestRequireConfigForMultipleTraceTypesForCannon(t *testing.T) { func TestRequireConfigForMultipleTraceTypesForCannon(t *testing.T) {
cfg := validConfig(TraceTypeCannon) cfg := validConfig(types.TraceTypeCannon)
cfg.TraceTypes = []TraceType{TraceTypeCannon, TraceTypeAlphabet} cfg.TraceTypes = []types.TraceType{types.TraceTypeCannon, types.TraceTypeAlphabet}
// Set all required options and check its valid // Set all required options and check its valid
cfg.RollupRpc = validRollupRpc cfg.RollupRpc = validRollupRpc
require.NoError(t, cfg.Check()) require.NoError(t, cfg.Check())
...@@ -377,8 +378,8 @@ func TestRequireConfigForMultipleTraceTypesForCannon(t *testing.T) { ...@@ -377,8 +378,8 @@ func TestRequireConfigForMultipleTraceTypesForCannon(t *testing.T) {
} }
func TestRequireConfigForMultipleTraceTypesForAsterisc(t *testing.T) { func TestRequireConfigForMultipleTraceTypesForAsterisc(t *testing.T) {
cfg := validConfig(TraceTypeAsterisc) cfg := validConfig(types.TraceTypeAsterisc)
cfg.TraceTypes = []TraceType{TraceTypeAsterisc, TraceTypeAlphabet} cfg.TraceTypes = []types.TraceType{types.TraceTypeAsterisc, types.TraceTypeAlphabet}
// Set all required options and check its valid // Set all required options and check its valid
cfg.RollupRpc = validRollupRpc cfg.RollupRpc = validRollupRpc
require.NoError(t, cfg.Check()) require.NoError(t, cfg.Check())
...@@ -395,10 +396,10 @@ func TestRequireConfigForMultipleTraceTypesForAsterisc(t *testing.T) { ...@@ -395,10 +396,10 @@ func TestRequireConfigForMultipleTraceTypesForAsterisc(t *testing.T) {
} }
func TestRequireConfigForMultipleTraceTypesForCannonAndAsterisc(t *testing.T) { func TestRequireConfigForMultipleTraceTypesForCannonAndAsterisc(t *testing.T) {
cfg := validConfig(TraceTypeCannon) cfg := validConfig(types.TraceTypeCannon)
applyValidConfigForAsterisc(&cfg) applyValidConfigForAsterisc(&cfg)
cfg.TraceTypes = []TraceType{TraceTypeCannon, TraceTypeAsterisc, TraceTypeAlphabet, TraceTypeFast} cfg.TraceTypes = []types.TraceType{types.TraceTypeCannon, types.TraceTypeAsterisc, types.TraceTypeAlphabet, types.TraceTypeFast}
// Set all required options and check its valid // Set all required options and check its valid
cfg.RollupRpc = validRollupRpc cfg.RollupRpc = validRollupRpc
require.NoError(t, cfg.Check()) require.NoError(t, cfg.Check())
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-service/flags" "github.com/ethereum-optimism/optimism/op-service/flags"
"github.com/ethereum-optimism/superchain-registry/superchain" "github.com/ethereum-optimism/superchain-registry/superchain"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -61,9 +62,9 @@ var ( ...@@ -61,9 +62,9 @@ var (
} }
TraceTypeFlag = &cli.StringSliceFlag{ TraceTypeFlag = &cli.StringSliceFlag{
Name: "trace-type", Name: "trace-type",
Usage: "The trace types to support. Valid options: " + openum.EnumString(config.TraceTypes), Usage: "The trace types to support. Valid options: " + openum.EnumString(types.TraceTypes),
EnvVars: prefixEnvVars("TRACE_TYPE"), EnvVars: prefixEnvVars("TRACE_TYPE"),
Value: cli.NewStringSlice(config.TraceTypeCannon.String()), Value: cli.NewStringSlice(types.TraceTypeCannon.String()),
} }
DatadirFlag = &cli.StringFlag{ DatadirFlag = &cli.StringFlag{
Name: "datadir", Name: "datadir",
...@@ -339,7 +340,7 @@ func CheckAsteriscFlags(ctx *cli.Context) error { ...@@ -339,7 +340,7 @@ func CheckAsteriscFlags(ctx *cli.Context) error {
return nil return nil
} }
func CheckRequired(ctx *cli.Context, traceTypes []config.TraceType) error { func CheckRequired(ctx *cli.Context, traceTypes []types.TraceType) error {
for _, f := range requiredFlags { for _, f := range requiredFlags {
if !ctx.IsSet(f.Names()[0]) { if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0]) return fmt.Errorf("flag %s is required", f.Names()[0])
...@@ -351,26 +352,26 @@ func CheckRequired(ctx *cli.Context, traceTypes []config.TraceType) error { ...@@ -351,26 +352,26 @@ func CheckRequired(ctx *cli.Context, traceTypes []config.TraceType) error {
} }
for _, traceType := range traceTypes { for _, traceType := range traceTypes {
switch traceType { switch traceType {
case config.TraceTypeCannon, config.TraceTypePermissioned: case types.TraceTypeCannon, types.TraceTypePermissioned:
if err := CheckCannonFlags(ctx); err != nil { if err := CheckCannonFlags(ctx); err != nil {
return err return err
} }
case config.TraceTypeAsterisc: case types.TraceTypeAsterisc:
if err := CheckAsteriscFlags(ctx); err != nil { if err := CheckAsteriscFlags(ctx); err != nil {
return err return err
} }
case config.TraceTypeAlphabet, config.TraceTypeFast: case types.TraceTypeAlphabet, types.TraceTypeFast:
default: default:
return fmt.Errorf("invalid trace type. must be one of %v", config.TraceTypes) return fmt.Errorf("invalid trace type. must be one of %v", types.TraceTypes)
} }
} }
return nil return nil
} }
func parseTraceTypes(ctx *cli.Context) ([]config.TraceType, error) { func parseTraceTypes(ctx *cli.Context) ([]types.TraceType, error) {
var traceTypes []config.TraceType var traceTypes []types.TraceType
for _, typeName := range ctx.StringSlice(TraceTypeFlag.Name) { for _, typeName := range ctx.StringSlice(TraceTypeFlag.Name) {
traceType := new(config.TraceType) traceType := new(types.TraceType)
if err := traceType.Set(typeName); err != nil { if err := traceType.Set(typeName); err != nil {
return nil, err return nil, err
} }
...@@ -514,7 +515,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro ...@@ -514,7 +515,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro
AdditionalBondClaimants: claimants, AdditionalBondClaimants: claimants,
RollupRpc: ctx.String(RollupRpcFlag.Name), RollupRpc: ctx.String(RollupRpcFlag.Name),
Cannon: vm.Config{ Cannon: vm.Config{
VmType: config.TraceTypeCannon.String(), VmType: types.TraceTypeCannon,
L1: l1EthRpc, L1: l1EthRpc,
L1Beacon: l1Beacon, L1Beacon: l1Beacon,
L2: l2Rpc, L2: l2Rpc,
...@@ -530,7 +531,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro ...@@ -530,7 +531,7 @@ func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, erro
CannonAbsolutePreStateBaseURL: cannonPrestatesURL, CannonAbsolutePreStateBaseURL: cannonPrestatesURL,
Datadir: ctx.String(DatadirFlag.Name), Datadir: ctx.String(DatadirFlag.Name),
Asterisc: vm.Config{ Asterisc: vm.Config{
VmType: config.TraceTypeAsterisc.String(), VmType: types.TraceTypeAsterisc,
L1: l1EthRpc, L1: l1EthRpc,
L1Beacon: l1Beacon, L1Beacon: l1Beacon,
L2: l2Rpc, L2: l2Rpc,
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"math/big" "math/big"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
...@@ -76,7 +77,7 @@ func (f *DisputeGameFactoryContract) GetGame(ctx context.Context, idx uint64, bl ...@@ -76,7 +77,7 @@ func (f *DisputeGameFactoryContract) GetGame(ctx context.Context, idx uint64, bl
return f.decodeGame(idx, result), nil return f.decodeGame(idx, result), nil
} }
func (f *DisputeGameFactoryContract) GetGameImpl(ctx context.Context, gameType uint32) (common.Address, error) { func (f *DisputeGameFactoryContract) GetGameImpl(ctx context.Context, gameType faultTypes.GameType) (common.Address, error) {
defer f.metrics.StartContractRequest("GetGameImpl")() defer f.metrics.StartContractRequest("GetGameImpl")()
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameImpls, gameType)) result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGameImpls, gameType))
if err != nil { if err != nil {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
"github.com/ethereum-optimism/optimism/op-service/sources/batching" "github.com/ethereum-optimism/optimism/op-service/sources/batching"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
...@@ -190,7 +191,7 @@ func TestGetGameFromParameters(t *testing.T) { ...@@ -190,7 +191,7 @@ func TestGetGameFromParameters(t *testing.T) {
func TestGetGameImpl(t *testing.T) { func TestGetGameImpl(t *testing.T) {
stubRpc, factory := setupDisputeGameFactoryTest(t) stubRpc, factory := setupDisputeGameFactoryTest(t)
gameType := uint32(3) gameType := faultTypes.CannonGameType
gameImplAddr := common.Address{0xaa} gameImplAddr := common.Address{0xaa}
stubRpc.SetResponse( stubRpc.SetResponse(
factoryAddr, factoryAddr,
...@@ -198,7 +199,7 @@ func TestGetGameImpl(t *testing.T) { ...@@ -198,7 +199,7 @@ func TestGetGameImpl(t *testing.T) {
rpcblock.Latest, rpcblock.Latest,
[]interface{}{gameType}, []interface{}{gameType},
[]interface{}{gameImplAddr}) []interface{}{gameImplAddr})
actual, err := factory.GetGameImpl(context.Background(), gameType) actual, err := factory.GetGameImpl(context.Background(), faultTypes.CannonGameType)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, gameImplAddr, actual) require.Equal(t, gameImplAddr, actual)
} }
......
...@@ -30,8 +30,8 @@ import ( ...@@ -30,8 +30,8 @@ import (
type CloseFunc func() type CloseFunc func()
type Registry interface { type Registry interface {
RegisterGameType(gameType uint32, creator scheduler.PlayerCreator) RegisterGameType(gameType faultTypes.GameType, creator scheduler.PlayerCreator)
RegisterBondContract(gameType uint32, creator claims.BondContractCreator) RegisterBondContract(gameType faultTypes.GameType, creator claims.BondContractCreator)
} }
type OracleRegistry interface { type OracleRegistry interface {
...@@ -73,27 +73,27 @@ func RegisterGameTypes( ...@@ -73,27 +73,27 @@ func RegisterGameTypes(
} }
syncValidator := newSyncStatusValidator(rollupClient) syncValidator := newSyncStatusValidator(rollupClient)
if cfg.TraceTypeEnabled(config.TraceTypeCannon) { if cfg.TraceTypeEnabled(faultTypes.TraceTypeCannon) {
if err := registerCannon(faultTypes.CannonGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, cfg, syncValidator, rollupClient, txSender, gameFactory, caller, l2Client, l1HeaderSource, selective, claimants); err != nil { if err := registerCannon(faultTypes.CannonGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, cfg, syncValidator, rollupClient, txSender, gameFactory, caller, l2Client, l1HeaderSource, selective, claimants); err != nil {
return nil, fmt.Errorf("failed to register cannon game type: %w", err) return nil, fmt.Errorf("failed to register cannon game type: %w", err)
} }
} }
if cfg.TraceTypeEnabled(config.TraceTypePermissioned) { if cfg.TraceTypeEnabled(faultTypes.TraceTypePermissioned) {
if err := registerCannon(faultTypes.PermissionedGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, cfg, syncValidator, rollupClient, txSender, gameFactory, caller, l2Client, l1HeaderSource, selective, claimants); err != nil { if err := registerCannon(faultTypes.PermissionedGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, cfg, syncValidator, rollupClient, txSender, gameFactory, caller, l2Client, l1HeaderSource, selective, claimants); err != nil {
return nil, fmt.Errorf("failed to register permissioned cannon game type: %w", err) return nil, fmt.Errorf("failed to register permissioned cannon game type: %w", err)
} }
} }
if cfg.TraceTypeEnabled(config.TraceTypeAsterisc) { if cfg.TraceTypeEnabled(faultTypes.TraceTypeAsterisc) {
if err := registerAsterisc(faultTypes.AsteriscGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, cfg, syncValidator, rollupClient, txSender, gameFactory, caller, l2Client, l1HeaderSource, selective, claimants); err != nil { if err := registerAsterisc(faultTypes.AsteriscGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, cfg, syncValidator, rollupClient, txSender, gameFactory, caller, l2Client, l1HeaderSource, selective, claimants); err != nil {
return nil, fmt.Errorf("failed to register asterisc game type: %w", err) return nil, fmt.Errorf("failed to register asterisc game type: %w", err)
} }
} }
if cfg.TraceTypeEnabled(config.TraceTypeFast) { if cfg.TraceTypeEnabled(faultTypes.TraceTypeFast) {
if err := registerAlphabet(faultTypes.FastGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, syncValidator, rollupClient, l2Client, txSender, gameFactory, caller, l1HeaderSource, selective, claimants); err != nil { if err := registerAlphabet(faultTypes.FastGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, syncValidator, rollupClient, l2Client, txSender, gameFactory, caller, l1HeaderSource, selective, claimants); err != nil {
return nil, fmt.Errorf("failed to register fast game type: %w", err) return nil, fmt.Errorf("failed to register fast game type: %w", err)
} }
} }
if cfg.TraceTypeEnabled(config.TraceTypeAlphabet) { if cfg.TraceTypeEnabled(faultTypes.TraceTypeAlphabet) {
if err := registerAlphabet(faultTypes.AlphabetGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, syncValidator, rollupClient, l2Client, txSender, gameFactory, caller, l1HeaderSource, selective, claimants); err != nil { if err := registerAlphabet(faultTypes.AlphabetGameType, registry, oracles, ctx, systemClock, l1Clock, logger, m, syncValidator, rollupClient, l2Client, txSender, gameFactory, caller, l1HeaderSource, selective, claimants); err != nil {
return nil, fmt.Errorf("failed to register alphabet game type: %w", err) return nil, fmt.Errorf("failed to register alphabet game type: %w", err)
} }
...@@ -102,7 +102,7 @@ func RegisterGameTypes( ...@@ -102,7 +102,7 @@ func RegisterGameTypes(
} }
func registerAlphabet( func registerAlphabet(
gameType uint32, gameType faultTypes.GameType,
registry Registry, registry Registry,
oracles OracleRegistry, oracles OracleRegistry,
ctx context.Context, ctx context.Context,
...@@ -167,7 +167,7 @@ func registerAlphabet( ...@@ -167,7 +167,7 @@ func registerAlphabet(
return nil return nil
} }
func registerOracle(ctx context.Context, m metrics.Metricer, oracles OracleRegistry, gameFactory *contracts.DisputeGameFactoryContract, caller *batching.MultiCaller, gameType uint32) error { func registerOracle(ctx context.Context, m metrics.Metricer, oracles OracleRegistry, gameFactory *contracts.DisputeGameFactoryContract, caller *batching.MultiCaller, gameType faultTypes.GameType) error {
implAddr, err := gameFactory.GetGameImpl(ctx, gameType) implAddr, err := gameFactory.GetGameImpl(ctx, gameType)
if err != nil { if err != nil {
return fmt.Errorf("failed to load implementation for game type %v: %w", gameType, err) return fmt.Errorf("failed to load implementation for game type %v: %w", gameType, err)
...@@ -185,7 +185,7 @@ func registerOracle(ctx context.Context, m metrics.Metricer, oracles OracleRegis ...@@ -185,7 +185,7 @@ func registerOracle(ctx context.Context, m metrics.Metricer, oracles OracleRegis
} }
func registerAsterisc( func registerAsterisc(
gameType uint32, gameType faultTypes.GameType,
registry Registry, registry Registry,
oracles OracleRegistry, oracles OracleRegistry,
ctx context.Context, ctx context.Context,
...@@ -278,7 +278,7 @@ func registerAsterisc( ...@@ -278,7 +278,7 @@ func registerAsterisc(
} }
func registerCannon( func registerCannon(
gameType uint32, gameType faultTypes.GameType,
registry Registry, registry Registry,
oracles OracleRegistry, oracles OracleRegistry,
ctx context.Context, ctx context.Context,
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/utils"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -19,7 +20,7 @@ type Metricer interface { ...@@ -19,7 +20,7 @@ type Metricer interface {
} }
type Config struct { type Config struct {
VmType string VmType types.TraceType
L1 string L1 string
L1Beacon string L1Beacon string
L2 string L2 string
...@@ -121,6 +122,6 @@ func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64 ...@@ -121,6 +122,6 @@ func (e *Executor) DoGenerateProof(ctx context.Context, dir string, begin uint64
e.logger.Info("Generating trace", "proof", end, "cmd", e.cfg.VmBin, "args", strings.Join(args, ", ")) e.logger.Info("Generating trace", "proof", end, "cmd", e.cfg.VmBin, "args", strings.Join(args, ", "))
execStart := time.Now() execStart := time.Now()
err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.cfg.VmBin, args...) err = e.cmdExecutor(ctx, e.logger.New("proof", end), e.cfg.VmBin, args...)
e.metrics.RecordVmExecutionTime(e.cfg.VmType, time.Since(execStart)) e.metrics.RecordVmExecutionTime(e.cfg.VmType.String(), time.Since(execStart))
return err return err
} }
...@@ -3,6 +3,8 @@ package types ...@@ -3,6 +3,8 @@ package types
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"math"
"math/big" "math/big"
"time" "time"
...@@ -18,14 +20,94 @@ var ( ...@@ -18,14 +20,94 @@ var (
ErrL2BlockNumberValid = errors.New("l2 block number is valid") ErrL2BlockNumberValid = errors.New("l2 block number is valid")
) )
type GameType uint32
const (
CannonGameType GameType = 0
PermissionedGameType GameType = 1
AsteriscGameType GameType = 2
FastGameType GameType = 254
AlphabetGameType GameType = 255
UnknownGameType GameType = math.MaxUint32
)
func (t GameType) MarshalText() ([]byte, error) {
return []byte(t.String()), nil
}
func (t GameType) String() string {
switch t {
case CannonGameType:
return "cannon"
case PermissionedGameType:
return "permissioned"
case AsteriscGameType:
return "asterisc"
case FastGameType:
return "fast"
case AlphabetGameType:
return "alphabet"
default:
return fmt.Sprintf("<invalid: %d>", t)
}
}
type TraceType string
const ( const (
CannonGameType uint32 = 0 TraceTypeAlphabet TraceType = "alphabet"
PermissionedGameType uint32 = 1 TraceTypeFast TraceType = "fast"
AsteriscGameType uint32 = 2 TraceTypeCannon TraceType = "cannon"
FastGameType uint32 = 254 TraceTypeAsterisc TraceType = "asterisc"
AlphabetGameType uint32 = 255 TraceTypePermissioned TraceType = "permissioned"
) )
var TraceTypes = []TraceType{TraceTypeAlphabet, TraceTypeCannon, TraceTypePermissioned, TraceTypeAsterisc, TraceTypeFast}
func (t TraceType) String() string {
return string(t)
}
// Set implements the Set method required by the [cli.Generic] interface.
func (t *TraceType) Set(value string) error {
if !ValidTraceType(TraceType(value)) {
return fmt.Errorf("unknown trace type: %q", value)
}
*t = TraceType(value)
return nil
}
func (t *TraceType) Clone() any {
cpy := *t
return &cpy
}
func ValidTraceType(value TraceType) bool {
for _, t := range TraceTypes {
if t == value {
return true
}
}
return false
}
func (t TraceType) GameType() GameType {
switch t {
case TraceTypeCannon:
return CannonGameType
case TraceTypePermissioned:
return PermissionedGameType
case TraceTypeAsterisc:
return AsteriscGameType
case TraceTypeFast:
return FastGameType
case TraceTypeAlphabet:
return AlphabetGameType
default:
return UnknownGameType
}
}
type ClockReader interface { type ClockReader interface {
Now() time.Time Now() time.Time
} }
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/claims"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-challenger/game/scheduler" "github.com/ethereum-optimism/optimism/op-challenger/game/scheduler"
"github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/ethereum-optimism/optimism/op-challenger/game/types"
) )
...@@ -12,27 +13,27 @@ import ( ...@@ -12,27 +13,27 @@ import (
var ErrUnsupportedGameType = errors.New("unsupported game type") var ErrUnsupportedGameType = errors.New("unsupported game type")
type GameTypeRegistry struct { type GameTypeRegistry struct {
types map[uint32]scheduler.PlayerCreator types map[faultTypes.GameType]scheduler.PlayerCreator
bondCreators map[uint32]claims.BondContractCreator bondCreators map[faultTypes.GameType]claims.BondContractCreator
} }
func NewGameTypeRegistry() *GameTypeRegistry { func NewGameTypeRegistry() *GameTypeRegistry {
return &GameTypeRegistry{ return &GameTypeRegistry{
types: make(map[uint32]scheduler.PlayerCreator), types: make(map[faultTypes.GameType]scheduler.PlayerCreator),
bondCreators: make(map[uint32]claims.BondContractCreator), bondCreators: make(map[faultTypes.GameType]claims.BondContractCreator),
} }
} }
// RegisterGameType registers a scheduler.PlayerCreator to use for a specific game type. // RegisterGameType registers a scheduler.PlayerCreator to use for a specific game type.
// Panics if the same game type is registered multiple times, since this indicates a significant programmer error. // Panics if the same game type is registered multiple times, since this indicates a significant programmer error.
func (r *GameTypeRegistry) RegisterGameType(gameType uint32, creator scheduler.PlayerCreator) { func (r *GameTypeRegistry) RegisterGameType(gameType faultTypes.GameType, creator scheduler.PlayerCreator) {
if _, ok := r.types[gameType]; ok { if _, ok := r.types[gameType]; ok {
panic(fmt.Errorf("duplicate creator registered for game type: %v", gameType)) panic(fmt.Errorf("duplicate creator registered for game type: %v", gameType))
} }
r.types[gameType] = creator r.types[gameType] = creator
} }
func (r *GameTypeRegistry) RegisterBondContract(gameType uint32, creator claims.BondContractCreator) { func (r *GameTypeRegistry) RegisterBondContract(gameType faultTypes.GameType, creator claims.BondContractCreator) {
if _, ok := r.bondCreators[gameType]; ok { if _, ok := r.bondCreators[gameType]; ok {
panic(fmt.Errorf("duplicate bond contract registered for game type: %v", gameType)) panic(fmt.Errorf("duplicate bond contract registered for game type: %v", gameType))
} }
...@@ -41,7 +42,7 @@ func (r *GameTypeRegistry) RegisterBondContract(gameType uint32, creator claims. ...@@ -41,7 +42,7 @@ func (r *GameTypeRegistry) RegisterBondContract(gameType uint32, creator claims.
// CreatePlayer creates a new game player for the given game, using the specified directory for persisting data. // CreatePlayer creates a new game player for the given game, using the specified directory for persisting data.
func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) { func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (scheduler.GamePlayer, error) {
creator, ok := r.types[game.GameType] creator, ok := r.types[faultTypes.GameType(game.GameType)]
if !ok { if !ok {
return nil, fmt.Errorf("%w: %v", ErrUnsupportedGameType, game.GameType) return nil, fmt.Errorf("%w: %v", ErrUnsupportedGameType, game.GameType)
} }
...@@ -49,7 +50,7 @@ func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (sc ...@@ -49,7 +50,7 @@ func (r *GameTypeRegistry) CreatePlayer(game types.GameMetadata, dir string) (sc
} }
func (r *GameTypeRegistry) CreateBondContract(game types.GameMetadata) (claims.BondContract, error) { func (r *GameTypeRegistry) CreateBondContract(game types.GameMetadata) (claims.BondContract, error) {
creator, ok := r.bondCreators[game.GameType] creator, ok := r.bondCreators[faultTypes.GameType(game.GameType)]
if !ok { if !ok {
return nil, fmt.Errorf("%w: %v", ErrUnsupportedGameType, game.GameType) return nil, fmt.Errorf("%w: %v", ErrUnsupportedGameType, game.GameType)
} }
......
...@@ -50,7 +50,7 @@ func (g *GameCallerCreator) CreateContract(ctx context.Context, game gameTypes.G ...@@ -50,7 +50,7 @@ func (g *GameCallerCreator) CreateContract(ctx context.Context, game gameTypes.G
if fdg, ok := g.cache.Get(game.Proxy); ok { if fdg, ok := g.cache.Get(game.Proxy); ok {
return fdg, nil return fdg, nil
} }
switch game.GameType { switch faultTypes.GameType(game.GameType) {
case faultTypes.CannonGameType, faultTypes.PermissionedGameType, faultTypes.AsteriscGameType, faultTypes.AlphabetGameType: case faultTypes.CannonGameType, faultTypes.PermissionedGameType, faultTypes.AsteriscGameType, faultTypes.AlphabetGameType:
fdg, err := contracts.NewFaultDisputeGameContract(ctx, g.m, game.Proxy, g.caller) fdg, err := contracts.NewFaultDisputeGameContract(ctx, g.m, game.Proxy, g.caller)
if err != nil { if err != nil {
......
...@@ -29,15 +29,15 @@ func TestMetadataCreator_CreateContract(t *testing.T) { ...@@ -29,15 +29,15 @@ func TestMetadataCreator_CreateContract(t *testing.T) {
}{ }{
{ {
name: "validCannonGameType", name: "validCannonGameType",
game: types.GameMetadata{GameType: faultTypes.CannonGameType, Proxy: fdgAddr}, game: types.GameMetadata{GameType: uint32(faultTypes.CannonGameType), Proxy: fdgAddr},
}, },
{ {
name: "validAsteriscGameType", name: "validAsteriscGameType",
game: types.GameMetadata{GameType: faultTypes.AsteriscGameType, Proxy: fdgAddr}, game: types.GameMetadata{GameType: uint32(faultTypes.AsteriscGameType), Proxy: fdgAddr},
}, },
{ {
name: "validAlphabetGameType", name: "validAlphabetGameType",
game: types.GameMetadata{GameType: faultTypes.AlphabetGameType, Proxy: fdgAddr}, game: types.GameMetadata{GameType: uint32(faultTypes.AlphabetGameType), Proxy: fdgAddr},
}, },
{ {
name: "InvalidGameType", name: "InvalidGameType",
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
challenger "github.com/ethereum-optimism/optimism/op-challenger" challenger "github.com/ethereum-optimism/optimism/op-challenger"
"github.com/ethereum-optimism/optimism/op-challenger/config" "github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
...@@ -122,20 +123,20 @@ func applyCannonConfig(c *config.Config, t *testing.T, rollupCfg *rollup.Config, ...@@ -122,20 +123,20 @@ func applyCannonConfig(c *config.Config, t *testing.T, rollupCfg *rollup.Config,
func WithCannon(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis) Option { func WithCannon(t *testing.T, rollupCfg *rollup.Config, l2Genesis *core.Genesis) Option {
return func(c *config.Config) { return func(c *config.Config) {
c.TraceTypes = append(c.TraceTypes, config.TraceTypeCannon) c.TraceTypes = append(c.TraceTypes, types.TraceTypeCannon)
applyCannonConfig(c, t, rollupCfg, l2Genesis) applyCannonConfig(c, t, rollupCfg, l2Genesis)
} }
} }
func WithAlphabet() Option { func WithAlphabet() Option {
return func(c *config.Config) { return func(c *config.Config) {
c.TraceTypes = append(c.TraceTypes, config.TraceTypeAlphabet) c.TraceTypes = append(c.TraceTypes, types.TraceTypeAlphabet)
} }
} }
func WithFastGames() Option { func WithFastGames() Option {
return func(c *config.Config) { return func(c *config.Config) {
c.TraceTypes = append(c.TraceTypes, config.TraceTypeFast) c.TraceTypes = append(c.TraceTypes, types.TraceTypeFast)
} }
} }
......
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