flags.go 23.1 KB
Newer Older
1 2 3 4
package flags

import (
	"fmt"
5
	"net/url"
6
	"runtime"
7
	"slices"
8
	"strings"
9

10
	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm"
11
	"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
12 13
	"github.com/ethereum-optimism/optimism/op-service/flags"
	"github.com/ethereum-optimism/superchain-registry/superchain"
14
	"github.com/ethereum/go-ethereum/common"
15
	"github.com/ethereum/go-ethereum/log"
16 17
	"github.com/urfave/cli/v2"

18
	"github.com/ethereum-optimism/optimism/op-challenger/config"
19
	"github.com/ethereum-optimism/optimism/op-node/chaincfg"
20
	opservice "github.com/ethereum-optimism/optimism/op-service"
21
	openum "github.com/ethereum-optimism/optimism/op-service/enum"
22
	oplog "github.com/ethereum-optimism/optimism/op-service/log"
23
	opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics"
24
	"github.com/ethereum-optimism/optimism/op-service/oppprof"
25
	"github.com/ethereum-optimism/optimism/op-service/txmgr"
26 27
)

28
const EnvVarPrefix = "OP_CHALLENGER"
29

30
func prefixEnvVars(name string) []string {
31
	return opservice.PrefixEnvVar(EnvVarPrefix, name)
32 33
}

34
var (
35
	faultDisputeVMs = []types.TraceType{types.TraceTypeCannon, types.TraceTypeAsterisc, types.TraceTypeAsteriscKona, types.TraceTypeSuperCannon}
36
	// Required Flags
37 38 39 40
	L1EthRpcFlag = &cli.StringFlag{
		Name:    "l1-eth-rpc",
		Usage:   "HTTP provider URL for L1.",
		EnvVars: prefixEnvVars("L1_ETH_RPC"),
41
	}
42 43 44 45 46
	L1BeaconFlag = &cli.StringFlag{
		Name:    "l1-beacon",
		Usage:   "Address of L1 Beacon API endpoint to use",
		EnvVars: prefixEnvVars("L1_BEACON"),
	}
47 48 49 50 51
	SupervisorRpcFlag = &cli.StringFlag{
		Name:    "supervisor-rpc",
		Usage:   "Provider URL for supervisor RPC",
		EnvVars: prefixEnvVars("SUPERVISOR_RPC"),
	}
52 53 54 55 56
	RollupRpcFlag = &cli.StringFlag{
		Name:    "rollup-rpc",
		Usage:   "HTTP provider URL for the rollup node",
		EnvVars: prefixEnvVars("ROLLUP_RPC"),
	}
57 58 59 60 61
	NetworkFlag = &cli.StringSliceFlag{
		Name:    flags.NetworkFlagName,
		Usage:   fmt.Sprintf("Predefined network selection. Available networks: %s", strings.Join(chaincfg.AvailableNetworks(), ", ")),
		EnvVars: prefixEnvVars("NETWORK"),
	}
62 63 64 65 66
	FactoryAddressFlag = &cli.StringFlag{
		Name:    "game-factory-address",
		Usage:   "Address of the fault game factory contract.",
		EnvVars: prefixEnvVars("GAME_FACTORY_ADDRESS"),
	}
67 68 69 70 71
	GameAllowlistFlag = &cli.StringSliceFlag{
		Name: "game-allowlist",
		Usage: "List of Fault Game contract addresses the challenger is allowed to play. " +
			"If empty, the challenger will play all games.",
		EnvVars: prefixEnvVars("GAME_ALLOWLIST"),
72
	}
73
	TraceTypeFlag = &cli.StringSliceFlag{
asnared's avatar
asnared committed
74
		Name:    "trace-type",
75
		Usage:   "The trace types to support. Valid options: " + openum.EnumString(types.TraceTypes),
asnared's avatar
asnared committed
76
		EnvVars: prefixEnvVars("TRACE_TYPE"),
77
		Value:   cli.NewStringSlice(types.TraceTypeCannon.String(), types.TraceTypeAsteriscKona.String()),
78
	}
79 80 81 82 83
	DatadirFlag = &cli.StringFlag{
		Name:    "datadir",
		Usage:   "Directory to store data generated as part of responding to games",
		EnvVars: prefixEnvVars("DATADIR"),
	}
84
	// Optional Flags
85 86 87 88 89 90
	MaxConcurrencyFlag = &cli.UintFlag{
		Name:    "max-concurrency",
		Usage:   "Maximum number of threads to use when progressing games",
		EnvVars: prefixEnvVars("MAX_CONCURRENCY"),
		Value:   uint(runtime.NumCPU()),
	}
91
	L2EthRpcFlag = &cli.StringSliceFlag{
92
		Name:    "l2-eth-rpc",
93
		Usage:   "URLs of L2 JSON-RPC endpoints to use (eth and debug namespace required)",
94
		EnvVars: prefixEnvVars("L2_ETH_RPC"),
95
	}
96 97 98 99 100 101
	MaxPendingTransactionsFlag = &cli.Uint64Flag{
		Name:    "max-pending-tx",
		Usage:   "The maximum number of pending transactions. 0 for no limit.",
		Value:   config.DefaultMaxPendingTx,
		EnvVars: prefixEnvVars("MAX_PENDING_TX"),
	}
102 103 104 105 106 107
	HTTPPollInterval = &cli.DurationFlag{
		Name:    "http-poll-interval",
		Usage:   "Polling interval for latest-block subscription when using an HTTP RPC provider.",
		EnvVars: prefixEnvVars("HTTP_POLL_INTERVAL"),
		Value:   config.DefaultPollInterval,
	}
108 109 110 111 112
	AdditionalBondClaimants = &cli.StringSliceFlag{
		Name:    "additional-bond-claimants",
		Usage:   "List of addresses to claim bonds for, in addition to the configured transaction sender",
		EnvVars: prefixEnvVars("ADDITIONAL_BOND_CLAIMANTS"),
	}
113 114 115 116 117 118 119 120 121
	PreStatesURLFlag = NewVMFlag("prestates-url", EnvVarPrefix, faultDisputeVMs, func(name string, envVars []string, traceTypeInfo string) cli.Flag {
		return &cli.StringFlag{
			Name: name,
			Usage: "Base URL to absolute prestates to use when generating trace data. " +
				"Prestates in this directory should be name as <commitment>.bin.gz <commitment>.json.gz or <commitment>.json " +
				traceTypeInfo,
			EnvVars: envVars,
		}
	})
122
	RollupConfigFlag = NewVMFlag("rollup-config", EnvVarPrefix, faultDisputeVMs, func(name string, envVars []string, traceTypeInfo string) cli.Flag {
123
		return &cli.StringSliceFlag{
124 125 126 127 128 129
			Name:    name,
			Usage:   "Rollup chain parameters " + traceTypeInfo,
			EnvVars: envVars,
		}
	})
	L2GenesisFlag = NewVMFlag("l2-genesis", EnvVarPrefix, faultDisputeVMs, func(name string, envVars []string, traceTypeInfo string) cli.Flag {
130
		return &cli.StringSliceFlag{
131
			Name:    name,
132
			Usage:   "Paths to the op-geth genesis file " + traceTypeInfo,
133 134 135
			EnvVars: envVars,
		}
	})
136 137 138 139 140 141 142 143
	CannonL2CustomFlag = &cli.BoolFlag{
		Name: "cannon-l2-custom",
		Usage: "Notify the op-program host that the L2 chain uses custom config to be loaded via the preimage oracle. " +
			"WARNING: This is incompatible with on-chain testing and must only be used for testing purposes.",
		EnvVars: prefixEnvVars("CANNON_L2_CUSTOM"),
		Value:   false,
		Hidden:  true,
	}
144 145 146 147 148
	CannonBinFlag = &cli.StringFlag{
		Name:    "cannon-bin",
		Usage:   "Path to cannon executable to use when generating trace data (cannon trace type only)",
		EnvVars: prefixEnvVars("CANNON_BIN"),
	}
149 150 151 152 153
	CannonServerFlag = &cli.StringFlag{
		Name:    "cannon-server",
		Usage:   "Path to executable to use as pre-image oracle server when generating trace data (cannon trace type only)",
		EnvVars: prefixEnvVars("CANNON_SERVER"),
	}
154 155 156 157 158
	CannonPreStateFlag = &cli.StringFlag{
		Name:    "cannon-prestate",
		Usage:   "Path to absolute prestate to use when generating trace data (cannon trace type only)",
		EnvVars: prefixEnvVars("CANNON_PRESTATE"),
	}
159 160 161 162 163 164
	CannonSnapshotFreqFlag = &cli.UintFlag{
		Name:    "cannon-snapshot-freq",
		Usage:   "Frequency of cannon snapshots to generate in VM steps (cannon trace type only)",
		EnvVars: prefixEnvVars("CANNON_SNAPSHOT_FREQ"),
		Value:   config.DefaultCannonSnapshotFreq,
	}
165 166 167 168 169 170
	CannonInfoFreqFlag = &cli.UintFlag{
		Name:    "cannon-info-freq",
		Usage:   "Frequency of cannon info log messages to generate in VM steps (cannon trace type only)",
		EnvVars: prefixEnvVars("CANNON_INFO_FREQ"),
		Value:   config.DefaultCannonInfoFreq,
	}
171 172 173 174 175 176 177 178 179 180
	AsteriscBinFlag = &cli.StringFlag{
		Name:    "asterisc-bin",
		Usage:   "Path to asterisc executable to use when generating trace data (asterisc trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_BIN"),
	}
	AsteriscServerFlag = &cli.StringFlag{
		Name:    "asterisc-server",
		Usage:   "Path to executable to use as pre-image oracle server when generating trace data (asterisc trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_SERVER"),
	}
181 182 183 184 185
	AsteriscKonaServerFlag = &cli.StringFlag{
		Name:    "asterisc-kona-server",
		Usage:   "Path to kona executable to use as pre-image oracle server when generating trace data (asterisc-kona trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_KONA_SERVER"),
	}
186 187 188 189 190
	AsteriscPreStateFlag = &cli.StringFlag{
		Name:    "asterisc-prestate",
		Usage:   "Path to absolute prestate to use when generating trace data (asterisc trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_PRESTATE"),
	}
191 192 193 194 195
	AsteriscKonaPreStateFlag = &cli.StringFlag{
		Name:    "asterisc-kona-prestate",
		Usage:   "Path to absolute prestate to use when generating trace data (asterisc-kona trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_KONA_PRESTATE"),
	}
196 197 198 199 200 201 202 203 204 205 206 207
	AsteriscSnapshotFreqFlag = &cli.UintFlag{
		Name:    "asterisc-snapshot-freq",
		Usage:   "Frequency of asterisc snapshots to generate in VM steps (asterisc trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_SNAPSHOT_FREQ"),
		Value:   config.DefaultAsteriscSnapshotFreq,
	}
	AsteriscInfoFreqFlag = &cli.UintFlag{
		Name:    "asterisc-info-freq",
		Usage:   "Frequency of asterisc info log messages to generate in VM steps (asterisc trace type only)",
		EnvVars: prefixEnvVars("ASTERISC_INFO_FREQ"),
		Value:   config.DefaultAsteriscInfoFreq,
	}
208
	GameWindowFlag = &cli.DurationFlag{
209 210 211
		Name: "game-window",
		Usage: "The time window which the challenger will look for games to progress and claim bonds. " +
			"This should include a buffer for the challenger to claim bonds for games outside the maximum game duration.",
212 213 214
		EnvVars: prefixEnvVars("GAME_WINDOW"),
		Value:   config.DefaultGameWindow,
	}
215 216 217 218 219
	SelectiveClaimResolutionFlag = &cli.BoolFlag{
		Name:    "selective-claim-resolution",
		Usage:   "Only resolve claims for the configured claimants",
		EnvVars: prefixEnvVars("SELECTIVE_CLAIM_RESOLUTION"),
	}
220 221 222 223 224 225
	UnsafeAllowInvalidPrestate = &cli.BoolFlag{
		Name:    "unsafe-allow-invalid-prestate",
		Usage:   "Allow responding to games where the absolute prestate is configured incorrectly. THIS IS UNSAFE!",
		EnvVars: prefixEnvVars("UNSAFE_ALLOW_INVALID_PRESTATE"),
		Hidden:  true, // Hidden as this is an unsafe flag added only for testing purposes
	}
226 227 228 229 230
)

// requiredFlags are checked by [CheckRequired]
var requiredFlags = []cli.Flag{
	L1EthRpcFlag,
231
	DatadirFlag,
232
	L1BeaconFlag,
233 234 235
}

// optionalFlags is a list of unchecked cli flags
asnared's avatar
asnared committed
236
var optionalFlags = []cli.Flag{
237
	RollupRpcFlag,
238 239
	NetworkFlag,
	FactoryAddressFlag,
240
	TraceTypeFlag,
241
	MaxConcurrencyFlag,
242
	SupervisorRpcFlag,
243
	L2EthRpcFlag,
244
	MaxPendingTransactionsFlag,
245
	HTTPPollInterval,
246
	AdditionalBondClaimants,
247
	GameAllowlistFlag,
248
	CannonL2CustomFlag,
249
	CannonBinFlag,
250
	CannonServerFlag,
251
	CannonPreStateFlag,
252
	CannonSnapshotFreqFlag,
253
	CannonInfoFreqFlag,
254 255
	AsteriscBinFlag,
	AsteriscServerFlag,
256
	AsteriscKonaServerFlag,
257
	AsteriscPreStateFlag,
258
	AsteriscKonaPreStateFlag,
259 260
	AsteriscSnapshotFreqFlag,
	AsteriscInfoFreqFlag,
261
	GameWindowFlag,
262
	SelectiveClaimResolutionFlag,
263
	UnsafeAllowInvalidPrestate,
asnared's avatar
asnared committed
264
}
265 266

func init() {
267
	optionalFlags = append(optionalFlags, oplog.CLIFlags(EnvVarPrefix)...)
268
	optionalFlags = append(optionalFlags, PreStatesURLFlag.Flags()...)
269 270
	optionalFlags = append(optionalFlags, RollupConfigFlag.Flags()...)
	optionalFlags = append(optionalFlags, L2GenesisFlag.Flags()...)
271 272 273
	optionalFlags = append(optionalFlags, txmgr.CLIFlagsWithDefaults(EnvVarPrefix, txmgr.DefaultChallengerFlagValues)...)
	optionalFlags = append(optionalFlags, opmetrics.CLIFlags(EnvVarPrefix)...)
	optionalFlags = append(optionalFlags, oppprof.CLIFlags(EnvVarPrefix)...)
274 275 276 277 278 279 280

	Flags = append(requiredFlags, optionalFlags...)
}

// Flags contains the list of configuration options available to the binary.
var Flags []cli.Flag

281 282 283 284 285 286 287 288
func checkOutputProviderFlags(ctx *cli.Context) error {
	if !ctx.IsSet(RollupRpcFlag.Name) {
		return fmt.Errorf("flag %v is required", RollupRpcFlag.Name)
	}
	return nil
}

func CheckCannonBaseFlags(ctx *cli.Context) error {
289 290 291 292
	if !ctx.IsSet(flags.NetworkFlagName) &&
		!(RollupConfigFlag.IsSet(ctx, types.TraceTypeCannon) && L2GenesisFlag.IsSet(ctx, types.TraceTypeCannon)) {
		return fmt.Errorf("flag %v or %v and %v is required",
			flags.NetworkFlagName, RollupConfigFlag.EitherFlagName(types.TraceTypeCannon), L2GenesisFlag.EitherFlagName(types.TraceTypeCannon))
293 294
	}
	if ctx.IsSet(flags.NetworkFlagName) &&
295
		(RollupConfigFlag.IsSet(ctx, types.TraceTypeCannon) || L2GenesisFlag.IsSet(ctx, types.TraceTypeCannon) || ctx.Bool(CannonL2CustomFlag.Name)) {
296
		return fmt.Errorf("flag %v can not be used with %v, %v or %v",
297
			flags.NetworkFlagName, RollupConfigFlag.SourceFlagName(ctx, types.TraceTypeCannon), L2GenesisFlag.SourceFlagName(ctx, types.TraceTypeCannon), CannonL2CustomFlag.Name)
298
	}
299 300 301
	if ctx.Bool(CannonL2CustomFlag.Name) && !(RollupConfigFlag.IsSet(ctx, types.TraceTypeCannon) && L2GenesisFlag.IsSet(ctx, types.TraceTypeCannon)) {
		return fmt.Errorf("flag %v and %v must be set when %v is true",
			RollupConfigFlag.EitherFlagName(types.TraceTypeCannon), L2GenesisFlag.EitherFlagName(types.TraceTypeCannon), CannonL2CustomFlag.Name)
302 303 304 305 306 307 308
	}
	if !ctx.IsSet(CannonBinFlag.Name) {
		return fmt.Errorf("flag %s is required", CannonBinFlag.Name)
	}
	if !ctx.IsSet(CannonServerFlag.Name) {
		return fmt.Errorf("flag %s is required", CannonServerFlag.Name)
	}
309
	if !PreStatesURLFlag.IsSet(ctx, types.TraceTypeCannon) && !ctx.IsSet(CannonPreStateFlag.Name) {
310
		return fmt.Errorf("flag %s or %s is required", PreStatesURLFlag.EitherFlagName(types.TraceTypeCannon), CannonPreStateFlag.Name)
311
	}
312 313 314
	return nil
}

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
func CheckSuperCannonFlags(ctx *cli.Context) error {
	if !ctx.IsSet(SupervisorRpcFlag.Name) {
		return fmt.Errorf("flag %v is required", SupervisorRpcFlag.Name)
	}
	if err := CheckCannonBaseFlags(ctx); err != nil {
		return err
	}
	return nil
}

func CheckCannonFlags(ctx *cli.Context) error {
	if err := checkOutputProviderFlags(ctx); err != nil {
		return err
	}
	if err := CheckCannonBaseFlags(ctx); err != nil {
		return err
	}
	return nil
}

335
func CheckAsteriscBaseFlags(ctx *cli.Context, traceType types.TraceType) error {
336 337 338
	if err := checkOutputProviderFlags(ctx); err != nil {
		return err
	}
339 340 341 342
	if !ctx.IsSet(flags.NetworkFlagName) &&
		!(RollupConfigFlag.IsSet(ctx, traceType) && L2GenesisFlag.IsSet(ctx, traceType)) {
		return fmt.Errorf("flag %v or %v and %v is required",
			flags.NetworkFlagName, RollupConfigFlag.EitherFlagName(traceType), L2GenesisFlag.EitherFlagName(traceType))
343 344
	}
	if ctx.IsSet(flags.NetworkFlagName) &&
345
		(RollupConfigFlag.IsSet(ctx, traceType) || L2GenesisFlag.IsSet(ctx, traceType)) {
346
		return fmt.Errorf("flag %v can not be used with %v and %v",
347
			flags.NetworkFlagName, RollupConfigFlag.SourceFlagName(ctx, traceType), L2GenesisFlag.SourceFlagName(ctx, traceType))
348 349 350 351
	}
	if !ctx.IsSet(AsteriscBinFlag.Name) {
		return fmt.Errorf("flag %s is required", AsteriscBinFlag.Name)
	}
352 353 354 355
	return nil
}

func CheckAsteriscFlags(ctx *cli.Context) error {
356
	if err := CheckAsteriscBaseFlags(ctx, types.TraceTypeAsterisc); err != nil {
357 358
		return err
	}
359 360 361
	if !ctx.IsSet(AsteriscServerFlag.Name) {
		return fmt.Errorf("flag %s is required", AsteriscServerFlag.Name)
	}
362
	if !PreStatesURLFlag.IsSet(ctx, types.TraceTypeAsterisc) && !ctx.IsSet(AsteriscPreStateFlag.Name) {
363
		return fmt.Errorf("flag %s or %s is required", PreStatesURLFlag.EitherFlagName(types.TraceTypeAsterisc), AsteriscPreStateFlag.Name)
364
	}
365 366 367
	return nil
}

368
func CheckAsteriscKonaFlags(ctx *cli.Context) error {
369
	if err := CheckAsteriscBaseFlags(ctx, types.TraceTypeAsteriscKona); err != nil {
370 371 372 373 374
		return err
	}
	if !ctx.IsSet(AsteriscKonaServerFlag.Name) {
		return fmt.Errorf("flag %s is required", AsteriscKonaServerFlag.Name)
	}
375
	if !PreStatesURLFlag.IsSet(ctx, types.TraceTypeAsteriscKona) && !ctx.IsSet(AsteriscKonaPreStateFlag.Name) {
376
		return fmt.Errorf("flag %s or %s is required", PreStatesURLFlag.EitherFlagName(types.TraceTypeAsteriscKona), AsteriscKonaPreStateFlag.Name)
377 378 379 380
	}
	return nil
}

381
func CheckRequired(ctx *cli.Context, traceTypes []types.TraceType) error {
382
	for _, f := range requiredFlags {
383 384
		if !ctx.IsSet(f.Names()[0]) {
			return fmt.Errorf("flag %s is required", f.Names()[0])
385 386
		}
	}
387
	if !ctx.IsSet(L2EthRpcFlag.Name) {
388 389
		return fmt.Errorf("flag %s is required", L2EthRpcFlag.Name)
	}
390 391
	for _, traceType := range traceTypes {
		switch traceType {
392
		case types.TraceTypeCannon, types.TraceTypePermissioned:
393 394 395
			if err := CheckCannonFlags(ctx); err != nil {
				return err
			}
396
		case types.TraceTypeAsterisc:
397 398 399
			if err := CheckAsteriscFlags(ctx); err != nil {
				return err
			}
400 401 402 403
		case types.TraceTypeAsteriscKona:
			if err := CheckAsteriscKonaFlags(ctx); err != nil {
				return err
			}
404 405 406 407
		case types.TraceTypeSuperCannon:
			if err := CheckSuperCannonFlags(ctx); err != nil {
				return err
			}
408
		case types.TraceTypeAlphabet, types.TraceTypeFast:
409 410 411
			if err := checkOutputProviderFlags(ctx); err != nil {
				return err
			}
412
		default:
413
			return fmt.Errorf("invalid trace type %v. must be one of %v", traceType, types.TraceTypes)
asnared's avatar
asnared committed
414
		}
415 416 417 418
	}
	return nil
}

419 420
func parseTraceTypes(ctx *cli.Context) ([]types.TraceType, error) {
	var traceTypes []types.TraceType
421
	for _, typeName := range ctx.StringSlice(TraceTypeFlag.Name) {
422
		traceType := new(types.TraceType)
423 424
		if err := traceType.Set(typeName); err != nil {
			return nil, err
425
		}
426 427
		if !slices.Contains(traceTypes, *traceType) {
			traceTypes = append(traceTypes, *traceType)
Andreas Bigger's avatar
Andreas Bigger committed
428
		}
asnared's avatar
asnared committed
429
	}
430
	return traceTypes, nil
431
}
432

433
func FactoryAddress(ctx *cli.Context) (common.Address, error) {
434
	// Use FactoryAddressFlag in preference to Network. Allows overriding the default dispute game factory.
435 436 437 438 439 440 441
	if ctx.IsSet(FactoryAddressFlag.Name) {
		gameFactoryAddress, err := opservice.ParseAddress(ctx.String(FactoryAddressFlag.Name))
		if err != nil {
			return common.Address{}, err
		}
		return gameFactoryAddress, nil
	}
442 443 444 445 446 447 448 449 450 451 452 453 454
	networks := ctx.StringSlice(flags.NetworkFlagName)
	if len(networks) > 1 {
		return common.Address{}, fmt.Errorf("flag %v required when multiple networks specified", FactoryAddressFlag.Name)
	}
	if len(networks) == 0 {
		return common.Address{}, fmt.Errorf("flag %v or %v is required", FactoryAddressFlag.Name, flags.NetworkFlagName)
	}
	network := networks[0]
	chainCfg := chaincfg.ChainByName(network)
	if chainCfg == nil {
		var opts []string
		for _, cfg := range superchain.OPChains {
			opts = append(opts, cfg.Chain+"-"+cfg.Superchain)
455
		}
456 457 458 459 460 461 462 463
		return common.Address{}, fmt.Errorf("unknown chain: %v (Valid options: %v)", network, strings.Join(opts, ", "))
	}
	addrs, ok := superchain.Addresses[chainCfg.ChainID]
	if !ok {
		return common.Address{}, fmt.Errorf("no addresses available for chain %v", network)
	}
	if addrs.DisputeGameFactoryProxy == (superchain.Address{}) {
		return common.Address{}, fmt.Errorf("dispute factory proxy not available for chain %v", network)
464
	}
465
	return common.Address(addrs.DisputeGameFactoryProxy), nil
466 467
}

468
// NewConfigFromCLI parses the Config from the provided flags or environment variables.
469
func NewConfigFromCLI(ctx *cli.Context, logger log.Logger) (*config.Config, error) {
470 471 472 473 474
	traceTypes, err := parseTraceTypes(ctx)
	if err != nil {
		return nil, err
	}
	if err := CheckRequired(ctx, traceTypes); err != nil {
475 476
		return nil, err
	}
477
	gameFactoryAddress, err := FactoryAddress(ctx)
478 479 480
	if err != nil {
		return nil, err
	}
481 482 483 484 485 486 487 488
	var allowedGames []common.Address
	if ctx.StringSlice(GameAllowlistFlag.Name) != nil {
		for _, addr := range ctx.StringSlice(GameAllowlistFlag.Name) {
			gameAddress, err := opservice.ParseAddress(addr)
			if err != nil {
				return nil, err
			}
			allowedGames = append(allowedGames, gameAddress)
489 490
		}
	}
491 492

	txMgrConfig := txmgr.ReadCLIConfig(ctx)
493 494
	metricsConfig := opmetrics.ReadCLIConfig(ctx)
	pprofConfig := oppprof.ReadCLIConfig(ctx)
495

496 497 498 499
	maxConcurrency := ctx.Uint(MaxConcurrencyFlag.Name)
	if maxConcurrency == 0 {
		return nil, fmt.Errorf("%v must not be 0", MaxConcurrencyFlag.Name)
	}
500 501 502 503 504 505 506 507 508 509
	var claimants []common.Address
	if ctx.IsSet(AdditionalBondClaimants.Name) {
		for _, addrStr := range ctx.StringSlice(AdditionalBondClaimants.Name) {
			claimant, err := opservice.ParseAddress(addrStr)
			if err != nil {
				return nil, fmt.Errorf("invalid additional claimant: %w", err)
			}
			claimants = append(claimants, claimant)
		}
	}
510 511 512 513 514 515 516 517 518

	getPrestatesUrl := func(traceType types.TraceType) (*url.URL, error) {
		var preStatesURL *url.URL
		if PreStatesURLFlag.IsSet(ctx, traceType) {
			val := PreStatesURLFlag.String(ctx, traceType)
			preStatesURL, err = url.Parse(val)
			if err != nil {
				return nil, fmt.Errorf("invalid %v (%v): %w", PreStatesURLFlag.SourceFlagName(ctx, traceType), val, err)
			}
519
		}
520
		return preStatesURL, nil
521
	}
522
	cannonPreStatesURL, err := getPrestatesUrl(types.TraceTypeCannon)
523 524 525
	if err != nil {
		return nil, err
	}
526 527 528
	asteriscPreStatesURL, err := getPrestatesUrl(types.TraceTypeAsterisc)
	if err != nil {
		return nil, err
529
	}
530 531 532
	asteriscKonaPreStatesURL, err := getPrestatesUrl(types.TraceTypeAsteriscKona)
	if err != nil {
		return nil, err
533
	}
534
	networks := ctx.StringSlice(flags.NetworkFlagName)
535 536
	l1EthRpc := ctx.String(L1EthRpcFlag.Name)
	l1Beacon := ctx.String(L1BeaconFlag.Name)
537
	l2Rpcs := ctx.StringSlice(L2EthRpcFlag.Name)
538 539
	return &config.Config{
		// Required Flags
540 541 542 543 544 545 546
		L1EthRpc:                l1EthRpc,
		L1Beacon:                l1Beacon,
		TraceTypes:              traceTypes,
		GameFactoryAddress:      gameFactoryAddress,
		GameAllowlist:           allowedGames,
		GameWindow:              ctx.Duration(GameWindowFlag.Name),
		MaxConcurrency:          maxConcurrency,
547
		L2Rpcs:                  l2Rpcs,
548 549 550 551
		MaxPendingTx:            ctx.Uint64(MaxPendingTransactionsFlag.Name),
		PollInterval:            ctx.Duration(HTTPPollInterval.Name),
		AdditionalBondClaimants: claimants,
		RollupRpc:               ctx.String(RollupRpcFlag.Name),
552
		SupervisorRPC:           ctx.String(SupervisorRpcFlag.Name),
553
		Cannon: vm.Config{
554 555 556 557 558 559 560 561 562 563 564 565 566 567
			VmType:            types.TraceTypeCannon,
			L1:                l1EthRpc,
			L1Beacon:          l1Beacon,
			L2s:               l2Rpcs,
			VmBin:             ctx.String(CannonBinFlag.Name),
			Server:            ctx.String(CannonServerFlag.Name),
			Networks:          networks,
			L2Custom:          ctx.Bool(CannonL2CustomFlag.Name),
			RollupConfigPaths: RollupConfigFlag.StringSlice(ctx, types.TraceTypeCannon),
			L2GenesisPaths:    L2GenesisFlag.StringSlice(ctx, types.TraceTypeCannon),
			SnapshotFreq:      ctx.Uint(CannonSnapshotFreqFlag.Name),
			InfoFreq:          ctx.Uint(CannonInfoFreqFlag.Name),
			DebugInfo:         true,
			BinarySnapshots:   true,
568 569
		},
		CannonAbsolutePreState:        ctx.String(CannonPreStateFlag.Name),
570
		CannonAbsolutePreStateBaseURL: cannonPreStatesURL,
571 572
		Datadir:                       ctx.String(DatadirFlag.Name),
		Asterisc: vm.Config{
573 574 575 576 577 578 579 580 581 582 583 584
			VmType:            types.TraceTypeAsterisc,
			L1:                l1EthRpc,
			L1Beacon:          l1Beacon,
			L2s:               l2Rpcs,
			VmBin:             ctx.String(AsteriscBinFlag.Name),
			Server:            ctx.String(AsteriscServerFlag.Name),
			Networks:          networks,
			RollupConfigPaths: RollupConfigFlag.StringSlice(ctx, types.TraceTypeAsterisc),
			L2GenesisPaths:    L2GenesisFlag.StringSlice(ctx, types.TraceTypeAsterisc),
			SnapshotFreq:      ctx.Uint(AsteriscSnapshotFreqFlag.Name),
			InfoFreq:          ctx.Uint(AsteriscInfoFreqFlag.Name),
			BinarySnapshots:   true,
585
		},
586 587
		AsteriscAbsolutePreState:        ctx.String(AsteriscPreStateFlag.Name),
		AsteriscAbsolutePreStateBaseURL: asteriscPreStatesURL,
588
		AsteriscKona: vm.Config{
589 590 591 592 593 594 595 596 597 598 599 600
			VmType:            types.TraceTypeAsteriscKona,
			L1:                l1EthRpc,
			L1Beacon:          l1Beacon,
			L2s:               l2Rpcs,
			VmBin:             ctx.String(AsteriscBinFlag.Name),
			Server:            ctx.String(AsteriscKonaServerFlag.Name),
			Networks:          networks,
			RollupConfigPaths: RollupConfigFlag.StringSlice(ctx, types.TraceTypeAsteriscKona),
			L2GenesisPaths:    L2GenesisFlag.StringSlice(ctx, types.TraceTypeAsteriscKona),
			SnapshotFreq:      ctx.Uint(AsteriscSnapshotFreqFlag.Name),
			InfoFreq:          ctx.Uint(AsteriscInfoFreqFlag.Name),
			BinarySnapshots:   true,
601 602 603 604 605 606 607 608
		},
		AsteriscKonaAbsolutePreState:        ctx.String(AsteriscKonaPreStateFlag.Name),
		AsteriscKonaAbsolutePreStateBaseURL: asteriscKonaPreStatesURL,
		TxMgrConfig:                         txMgrConfig,
		MetricsConfig:                       metricsConfig,
		PprofConfig:                         pprofConfig,
		SelectiveClaimResolution:            ctx.Bool(SelectiveClaimResolutionFlag.Name),
		AllowInvalidPrestate:                ctx.Bool(UnsafeAllowInvalidPrestate.Name),
609 610
	}, nil
}