Commit 672adac1 authored by Adrian Sutton's avatar Adrian Sutton

op-challenger: Allow multiple trace types to be specified on the CLI.

parent bbd8b86f
......@@ -91,6 +91,41 @@ func TestTraceType(t *testing.T) {
})
}
func TestMultipleTraceTypes(t *testing.T) {
t.Run("WithAllOptions", func(t *testing.T) {
argsMap := requiredArgs(config.TraceTypeCannon)
addRequiredOutputCannonArgs(argsMap)
addRequiredAlphabetArgs(argsMap)
args := toArgList(argsMap)
// Add extra trace types (cannon is already specified)
args = append(args,
"--trace-type", config.TraceTypeOutputCannon.String(),
"--trace-type", config.TraceTypeAlphabet.String())
cfg := configForArgs(t, args)
require.Equal(t, []config.TraceType{config.TraceTypeCannon, config.TraceTypeOutputCannon, config.TraceTypeAlphabet}, cfg.TraceTypes)
})
t.Run("WithSomeOptions", func(t *testing.T) {
argsMap := requiredArgs(config.TraceTypeCannon)
addRequiredAlphabetArgs(argsMap)
args := toArgList(argsMap)
// Add extra trace types (cannon is already specified)
args = append(args,
"--trace-type", config.TraceTypeAlphabet.String())
cfg := configForArgs(t, args)
require.Equal(t, []config.TraceType{config.TraceTypeCannon, config.TraceTypeAlphabet}, cfg.TraceTypes)
})
t.Run("SpecifySameOptionMultipleTimes", func(t *testing.T) {
argsMap := requiredArgs(config.TraceTypeCannon)
args := toArgList(argsMap)
// Add cannon trace type again
args = append(args, "--trace-type", config.TraceTypeCannon.String())
// We're fine with the same option being listed multiple times, just deduplicate them.
cfg := configForArgs(t, args)
require.Equal(t, []config.TraceType{config.TraceTypeCannon}, cfg.TraceTypes)
})
}
func TestGameFactoryAddress(t *testing.T) {
t.Run("Required", func(t *testing.T) {
verifyArgsInvalid(t, "flag game-factory-address is required", addRequiredArgsExcept(config.TraceTypeAlphabet, "--game-factory-address"))
......@@ -441,20 +476,32 @@ func requiredArgs(traceType config.TraceType) map[string]string {
}
switch traceType {
case config.TraceTypeAlphabet:
args["--alphabet"] = alphabetTrace
case config.TraceTypeCannon, config.TraceTypeOutputCannon:
args["--cannon-network"] = cannonNetwork
args["--cannon-bin"] = cannonBin
args["--cannon-server"] = cannonServer
args["--cannon-prestate"] = cannonPreState
args["--cannon-l2"] = cannonL2
}
if traceType == config.TraceTypeOutputCannon {
args["--rollup-rpc"] = rollupRpc
addRequiredAlphabetArgs(args)
case config.TraceTypeCannon:
addRequiredCannonArgs(args)
case config.TraceTypeOutputCannon:
addRequiredOutputCannonArgs(args)
}
return args
}
func addRequiredAlphabetArgs(args map[string]string) {
args["--alphabet"] = alphabetTrace
}
func addRequiredOutputCannonArgs(args map[string]string) {
addRequiredCannonArgs(args)
args["--rollup-rpc"] = rollupRpc
}
func addRequiredCannonArgs(args map[string]string) {
args["--cannon-network"] = cannonNetwork
args["--cannon-bin"] = cannonBin
args["--cannon-server"] = cannonServer
args["--cannon-prestate"] = cannonPreState
args["--cannon-l2"] = cannonL2
}
func toArgList(req map[string]string) []string {
var combined []string
for name, value := range req {
......
......@@ -3,6 +3,7 @@ package flags
import (
"fmt"
"runtime"
"slices"
"strings"
"github.com/ethereum/go-ethereum/common"
......@@ -44,14 +45,10 @@ var (
"If empty, the challenger will play all games.",
EnvVars: prefixEnvVars("GAME_ALLOWLIST"),
}
TraceTypeFlag = &cli.GenericFlag{
TraceTypeFlag = &cli.StringSliceFlag{
Name: "trace-type",
Usage: "The trace type. Valid options: " + openum.EnumString(config.TraceTypes),
Usage: "The trace types to support. Valid options: " + openum.EnumString(config.TraceTypes),
EnvVars: prefixEnvVars("TRACE_TYPE"),
Value: func() *config.TraceType {
out := config.TraceType("") // No default value
return &out
}(),
}
AgreeWithProposedOutputFlag = &cli.BoolFlag{
Name: "agree-with-proposed-output",
......@@ -210,38 +207,57 @@ func CheckCannonFlags(ctx *cli.Context) error {
return nil
}
func CheckRequired(ctx *cli.Context) error {
func CheckRequired(ctx *cli.Context, traceTypes []config.TraceType) error {
for _, f := range requiredFlags {
if !ctx.IsSet(f.Names()[0]) {
return fmt.Errorf("flag %s is required", f.Names()[0])
}
}
gameType := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name)))
switch gameType {
case config.TraceTypeCannon:
if err := CheckCannonFlags(ctx); err != nil {
return err
}
case config.TraceTypeAlphabet:
if !ctx.IsSet(AlphabetFlag.Name) {
return fmt.Errorf("flag %s is required", "alphabet")
for _, traceType := range traceTypes {
switch traceType {
case config.TraceTypeCannon:
if err := CheckCannonFlags(ctx); err != nil {
return err
}
case config.TraceTypeAlphabet:
if !ctx.IsSet(AlphabetFlag.Name) {
return fmt.Errorf("flag %s is required", "alphabet")
}
case config.TraceTypeOutputCannon:
if err := CheckCannonFlags(ctx); err != nil {
return err
}
if !ctx.IsSet(RollupRpcFlag.Name) {
return fmt.Errorf("flag %s is required", RollupRpcFlag.Name)
}
default:
return fmt.Errorf("invalid trace type. must be one of %v", config.TraceTypes)
}
case config.TraceTypeOutputCannon:
if err := CheckCannonFlags(ctx); err != nil {
return err
}
return nil
}
func parseTraceTypes(ctx *cli.Context) ([]config.TraceType, error) {
var traceTypes []config.TraceType
for _, typeName := range ctx.StringSlice(TraceTypeFlag.Name) {
traceType := new(config.TraceType)
if err := traceType.Set(typeName); err != nil {
return nil, err
}
if !ctx.IsSet(RollupRpcFlag.Name) {
return fmt.Errorf("flag %s is required", RollupRpcFlag.Name)
if !slices.Contains(traceTypes, *traceType) {
traceTypes = append(traceTypes, *traceType)
}
default:
return fmt.Errorf("invalid trace type. must be one of %v", config.TraceTypes)
}
return nil
return traceTypes, nil
}
// NewConfigFromCLI parses the Config from the provided flags or environment variables.
func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
if err := CheckRequired(ctx); err != nil {
traceTypes, err := parseTraceTypes(ctx)
if err != nil {
return nil, err
}
if err := CheckRequired(ctx, traceTypes); err != nil {
return nil, err
}
gameFactoryAddress, err := opservice.ParseAddress(ctx.String(FactoryAddressFlag.Name))
......@@ -263,8 +279,6 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
metricsConfig := opmetrics.ReadCLIConfig(ctx)
pprofConfig := oppprof.ReadCLIConfig(ctx)
traceTypeFlag := config.TraceType(strings.ToLower(ctx.String(TraceTypeFlag.Name)))
maxConcurrency := ctx.Uint(MaxConcurrencyFlag.Name)
if maxConcurrency == 0 {
return nil, fmt.Errorf("%v must not be 0", MaxConcurrencyFlag.Name)
......@@ -272,7 +286,7 @@ func NewConfigFromCLI(ctx *cli.Context) (*config.Config, error) {
return &config.Config{
// Required Flags
L1EthRpc: ctx.String(L1EthRpcFlag.Name),
TraceTypes: []config.TraceType{traceTypeFlag},
TraceTypes: traceTypes,
GameFactoryAddress: gameFactoryAddress,
GameAllowlist: allowedGames,
GameWindow: ctx.Duration(GameWindowFlag.Name),
......
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