diff --git a/op-program/chainconfig/chaincfg.go b/op-program/chainconfig/chaincfg.go index 7c638f669cd..77fcf1ada7d 100644 --- a/op-program/chainconfig/chaincfg.go +++ b/op-program/chainconfig/chaincfg.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "os" + "strings" "github.com/ethereum-optimism/optimism/op-service/superutil" "github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset" @@ -25,6 +26,29 @@ func OPSepoliaChainConfig() *params.ChainConfig { //go:embed configs/*json var customChainConfigFS embed.FS +func CustomChainIDs() ([]eth.ChainID, error) { + return customChainIDs(customChainConfigFS) +} + +func customChainIDs(customChainFS embed.FS) ([]eth.ChainID, error) { + entries, err := customChainFS.ReadDir("configs") + if err != nil { + return nil, fmt.Errorf("failed to list custom configs: %w", err) + } + var chainIDs []eth.ChainID + for _, entry := range entries { + if strings.HasSuffix(entry.Name(), "-genesis-l2.json") { + chainID, err := eth.ParseDecimalChainID(strings.TrimSuffix(entry.Name(), "-genesis-l2.json")) + if err != nil { + return nil, fmt.Errorf("incorrectly named genesis-l2 config (%s): %w", entry.Name(), err) + } + chainIDs = append(chainIDs, chainID) + } + } + + return chainIDs, nil +} + func RollupConfigByChainID(chainID eth.ChainID) (*rollup.Config, error) { config, err := rollup.LoadOPStackRollupConfig(eth.EvilChainIDToUInt64(chainID)) if err == nil { @@ -37,7 +61,7 @@ func rollupConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*rollup // Load custom rollup configs from embed FS file, err := customChainFS.Open(fmt.Sprintf("configs/%v-rollup.json", chainID)) if errors.Is(err, os.ErrNotExist) { - return nil, fmt.Errorf("no rollup config available for chain ID: %d", chainID) + return nil, fmt.Errorf("no rollup config available for chain ID: %v", chainID) } else if err != nil { return nil, fmt.Errorf("failed to get rollup config for chain ID %v: %w", chainID, err) } @@ -59,7 +83,7 @@ func chainConfigByChainID(chainID eth.ChainID, customChainFS embed.FS) (*params. // Load from custom chain configs from embed FS data, err := customChainFS.ReadFile(fmt.Sprintf("configs/%v-genesis-l2.json", chainID)) if errors.Is(err, os.ErrNotExist) { - return nil, fmt.Errorf("no chain config available for chain ID: %d", chainID) + return nil, fmt.Errorf("no chain config available for chain ID: %v", chainID) } else if err != nil { return nil, fmt.Errorf("failed to get chain config for chain ID %v: %w", chainID, err) } @@ -92,7 +116,7 @@ func dependencySetByChainID(chainID eth.ChainID, customChainFS embed.FS) (depset // Load custom dependency set configs from embed FS data, err := customChainFS.ReadFile("configs/depsets.json") if errors.Is(err, os.ErrNotExist) { - return nil, fmt.Errorf("no dependency set available for chain ID: %d", chainID) + return nil, fmt.Errorf("no dependency set available for chain ID: %v", chainID) } else if err != nil { return nil, fmt.Errorf("failed to get dependency set for chain ID %v: %w", chainID, err) } @@ -108,5 +132,5 @@ func dependencySetByChainID(chainID eth.ChainID, customChainFS embed.FS) (depset return depSet, nil } } - return nil, fmt.Errorf("no dependency set config includes chain ID: %d", chainID) + return nil, fmt.Errorf("no dependency set config includes chain ID: %v", chainID) } diff --git a/op-program/chainconfig/chaincfg_test.go b/op-program/chainconfig/chaincfg_test.go index a493e1b4643..be097afa3b3 100644 --- a/op-program/chainconfig/chaincfg_test.go +++ b/op-program/chainconfig/chaincfg_test.go @@ -43,3 +43,9 @@ func TestGetCustomDependencySetConfig(t *testing.T) { _, err = dependencySetByChainID(eth.ChainIDFromUInt64(900), test.TestCustomChainConfigFS) require.Error(t, err) } + +func TestListCustomChainIDs(t *testing.T) { + actual, err := customChainIDs(test.TestCustomChainConfigFS) + require.NoError(t, err) + require.Equal(t, []eth.ChainID{eth.ChainIDFromUInt64(901)}, actual) +} diff --git a/op-program/host/cmd/main.go b/op-program/host/cmd/main.go index 75dc1383983..aa083892373 100644 --- a/op-program/host/cmd/main.go +++ b/op-program/host/cmd/main.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum-optimism/optimism/op-program/host" "github.com/ethereum-optimism/optimism/op-program/host/config" "github.com/ethereum-optimism/optimism/op-program/host/flags" + "github.com/ethereum-optimism/optimism/op-program/host/subcmds" "github.com/ethereum-optimism/optimism/op-program/host/version" opservice "github.com/ethereum-optimism/optimism/op-service" oplog "github.com/ethereum-optimism/optimism/op-service/log" @@ -44,6 +45,9 @@ func run(args []string, action ConfigAction) error { app.Name = "op-program" app.Usage = "Optimism Fault Proof Program" app.Description = "The Optimism Fault Proof Program fault proof program that runs through the rollup state-transition to verify an L2 output from L1 inputs." + app.Commands = []*cli.Command{ + subcmds.ConfigsCommand, + } app.Action = func(ctx *cli.Context) error { logger, err := setupLogging(ctx) if err != nil { diff --git a/op-program/host/subcmds/configs_cmd.go b/op-program/host/subcmds/configs_cmd.go new file mode 100644 index 00000000000..5619dda2d83 --- /dev/null +++ b/op-program/host/subcmds/configs_cmd.go @@ -0,0 +1,98 @@ +package subcmds + +import ( + "fmt" + + "github.com/ethereum-optimism/optimism/op-node/chaincfg" + "github.com/ethereum-optimism/optimism/op-program/chainconfig" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum/go-ethereum/superchain" + "github.com/urfave/cli/v2" +) + +var ( + ConfigsChainIDFlag = &cli.StringFlag{ + Name: "chain-id", + Usage: "Chain ID to report chain configuration for", + } + ConfigsNetworkFlag = &cli.StringFlag{ + Name: "network", + Usage: "Network to report chain configuration for", + } +) + +var ConfigsCommand = &cli.Command{ + Name: "configs", + Usage: "List the supported chain configurations", + Description: "List the supported chain configurations.", + Action: ListConfigs, + Flags: []cli.Flag{ + ConfigsChainIDFlag, + ConfigsNetworkFlag, + }, +} + +func ListConfigs(ctx *cli.Context) error { + if ctx.IsSet(ConfigsChainIDFlag.Name) { + chainID, err := eth.ParseDecimalChainID(ctx.String(ConfigsChainIDFlag.Name)) + if err != nil { + return fmt.Errorf("invalid chain ID: %w", err) + } + if err := listChain(chainID); err != nil { + return err + } + } + if ctx.IsSet(ConfigsNetworkFlag.Name) { + if err := listNamedChain(ctx.String(ConfigsNetworkFlag.Name)); err != nil { + return err + } + } + if !ctx.IsSet(ConfigsChainIDFlag.Name) && !ctx.IsSet(ConfigsNetworkFlag.Name) { + return listAllChains() + } + return nil +} + +func listAllChains() error { + chainNames := superchain.ChainNames() + for _, name := range chainNames { + if err := listNamedChain(name); err != nil { + return err + } + } + customChainIDs, err := chainconfig.CustomChainIDs() + if err != nil { + return err + } + for _, chainID := range customChainIDs { + if err := listChain(chainID); err != nil { + return err + } + } + return nil +} + +func listNamedChain(name string) error { + ch := chaincfg.ChainByName(name) + chainID := eth.ChainIDFromUInt64(ch.ChainID) + err := listChain(chainID) + if err != nil { + return err + } + return nil +} + +func listChain(chainID eth.ChainID) error { + cfg, err := chainconfig.RollupConfigByChainID(chainID) + if err != nil { + return err + } + // Double check the L2 genesis is really available + _, err = chainconfig.ChainConfigByChainID(chainID) + if err != nil { + return err + } + description := cfg.Description(chaincfg.L2ChainIDToNetworkDisplayName) + fmt.Println(description) + return nil +}