diff --git a/.gitignore b/.gitignore index 5f3e460bf..3b448baef 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,7 @@ go.work.sum /main dist/ cache/ + +# Ignore customized configuration files +*.toml +!*.toml.template diff --git a/README.md b/README.md index 107242b71..42e6eb731 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ L2s: Predeploy Contracts Spec ( https://specs.optimism.io/protocol/predeploys.ht - L1StandardBridge: 0x2D8543c236a4d626f54B51Fa8bc229a257C5143E ``` - ### 4. Start testing multichain features 🚀 For **getting up to speed with supersim**, watch the [**🎥 Supersim 101 Training Session video**](https://www.youtube.com/live/Kh4fNshcl5Y?t=30s) @@ -200,6 +199,40 @@ L2s: Predeploy Contracts Spec ( https://specs.optimism.io/protocol/predeploys.ht - L1CrossDomainMessenger: 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 - L1StandardBridge: 0x3e2Ea9B92B7E48A52296fD261dc26fd995284631 ``` +## ⚙️ Configuration + +#### Option 1: Use the .toml.template File +The configuration file for Supersim is provided as a `.toml.template` file. This ensures you can start with a pre-defined setup while allowing you to customize it to your needs. + +#### Copy the .toml.template file to .toml: +Rename the template file to remove the `.template` extension. Use the following command: +```sh +cp config.toml.template config.toml +``` +#### Edit the .toml File: +Open `config.toml` in your preferred text editor (e.g., VS Code, Vim, or Nano) and customize the parameters to suit your setup. For example: +```toml +# Supersim Configuration File Example + +# L1 instance settings +# Host address for the L1 instance +# Default: "127.0.0.1" +l1.host = "0.0.0.0" + +# Listening port for the L1 instance. `0` binds to any available port +# Default: 8545 +l1.port = 3000 +``` + +Save the file. + + +#### Option 2: Configure via CLI +Supersim can also be configured directly via the CLI without editing a configuration file. For example, you can specify the required parameters when starting Supersim: +```sh +supersim --l1.host "0.0.0.0" --l1.port 3000 --l2.host "0.0.0.0" --l2.starting.port 3001 + +``` ### Development diff --git a/config.toml.template b/config.toml.template new file mode 100644 index 000000000..3f11febd2 --- /dev/null +++ b/config.toml.template @@ -0,0 +1,33 @@ +Title = "Supersim Configuration" +Desc = "Configuration for the Supersim application." + + +# Admin server settings +admin_port = 8420 # Listening port for the admin server (Default: 8420) + +# Interoperability settings +interop_autorelay = false # Automatically relay messages sent to the L2ToL2CrossDomainMessenger (Default: false) +interop_delay = 0 # Delay before relaying messages sent to the L2ToL2CrossDomainMessenger (Default: 0) + +# L1 instance settings +l1_host = "127.0.0.1" # Host address for the L1 instance (Default: "127.0.0.1") +l1_port = 8545 # Listening port for the L1 instance. `0` binds to any available port (Default: 8545) + +# L2 instance settings +l2_host = "127.0.0.1" # Host address for L2 instances (Default: "127.0.0.1") +l2_starting_port = 9545 # Starting port to increment from for L2 chains. `0` binds each chain to any available port (Default: 9545) + +# Logging settings +log_color = false # Color the log output if in terminal mode (Default: false) +log_format = "text" # Format the log output. Supported formats: 'text', 'terminal', 'logfmt', 'json', 'json-pretty' (Default: "text") +log_level = "INFO" # The lowest log level that will be output (Default: "INFO") +log_pid = false # Show pid in the log (Default: false) +logs_directory = "" # Directory to store logs + +[commands] + +[fork] +# l1_fork_height = 0 # L1 height to fork the superchain (bounds L2 time). `0` for latest +# chains = ["automata", "base"] # Chains to fork in the superchain +# network = "mainnet" # Superchain network. Options: sepolia-dev-0, mainnet, sepolia +# interop_enabled = true \ No newline at end of file diff --git a/config/cli.go b/config/cli.go index c6a7f73ab..0dde9d9ab 100644 --- a/config/cli.go +++ b/config/cli.go @@ -2,9 +2,11 @@ package config import ( "fmt" + "os" "strings" opservice "github.com/ethereum-optimism/optimism/op-service" + "github.com/pelletier/go-toml/v2" "net" "regexp" @@ -130,55 +132,38 @@ func ForkCLIFlags(envPrefix string) []cli.Flag { } type ForkCLIConfig struct { - L1ForkHeight uint64 - Network string - Chains []string + L1ForkHeight uint64 `toml:"l1_fork_height"` + Network string `toml:"network"` + Chains []string `toml:"chains"` - InteropEnabled bool + InteropEnabled bool `toml:"interop_enabled"` } type CLIConfig struct { - AdminPort uint64 + AdminPort uint64 `toml:"admin_port"` - L1Port uint64 - L2StartingPort uint64 + L1Port uint64 `toml:"l1_port"` + L2StartingPort uint64 `toml:"l2_starting_port"` - InteropAutoRelay bool - InteropDelay uint64 + InteropAutoRelay bool `toml:"interop_autorelay"` + InteropDelay uint64 `toml:"interop_delay"` - LogsDirectory string + LogsDirectory string `toml:"logs_directory"` - ForkConfig *ForkCLIConfig + ForkConfig *ForkCLIConfig `toml:"fork"` - L1Host string - L2Host string + L1Host string `toml:"l1_host"` + L2Host string `toml:"l2_host"` } func ReadCLIConfig(ctx *cli.Context) (*CLIConfig, error) { - cfg := &CLIConfig{ - AdminPort: ctx.Uint64(AdminPortFlagName), + cfg := &CLIConfig{} - L1Port: ctx.Uint64(L1PortFlagName), - L2StartingPort: ctx.Uint64(L2StartingPortFlagName), - - InteropAutoRelay: ctx.Bool(InteropAutoRelayFlagName), - InteropDelay: ctx.Uint64(InteropDelayFlagName), - - LogsDirectory: ctx.String(LogsDirectoryFlagName), - - L1Host: ctx.String(L1HostFlagName), - L2Host: ctx.String(L2HostFlagName), + if err := applyTOMLConfig(cfg); err != nil { + return nil, err } - if ctx.Command.Name == ForkCommandName { - cfg.ForkConfig = &ForkCLIConfig{ - L1ForkHeight: ctx.Uint64(L1ForkHeightFlagName), - Network: ctx.String(NetworkFlagName), - Chains: ctx.StringSlice(ChainsFlagName), - - InteropEnabled: ctx.Bool(InteropEnabledFlagName), - } - } + populateFromCLIContext(cfg, ctx) return cfg, cfg.Check() } @@ -244,3 +229,71 @@ func validateHost(host string) error { return nil } + +func applyTOMLConfig(cfg *CLIConfig) error { + tomlCfgFile := "config.toml" + if _, err := os.Stat(tomlCfgFile); err == nil { + content, err := os.ReadFile(tomlCfgFile) + if err != nil { + return fmt.Errorf("failed to read TOML config: %w", err) + } + + if err := toml.Unmarshal(content, &cfg); err != nil { + return fmt.Errorf("error parsing TOML: %v", err) + } + } else if !os.IsNotExist(err) { + return fmt.Errorf("error checking TOML config file: %w", err) + } + + return nil +} + +func populateFromCLIContext(cfg *CLIConfig, ctx *cli.Context) { + adminPort := ctx.Uint64(AdminPortFlagName) + if adminPort != 0 { + cfg.AdminPort = adminPort + } + + l1Port := ctx.Uint64(L1PortFlagName) + if l1Port != 0 { + cfg.L1Port = l1Port + } + + l2StartingPort := ctx.Uint64(L2StartingPortFlagName) + if l2StartingPort != 0 { + cfg.L2StartingPort = l2StartingPort + } + + if ctx.Bool(InteropAutoRelayFlagName) { + cfg.InteropAutoRelay = true + } + + interopDelay := ctx.Uint64(InteropDelayFlagName) + if interopDelay != 0 { + cfg.InteropDelay = interopDelay + } + + logsDirectory := ctx.String(LogsDirectoryFlagName) + if len(logsDirectory) != 0 { + cfg.LogsDirectory = logsDirectory + } + + l1Host := ctx.String(L1HostFlagName) + if len(l1Host) != 0 { + cfg.L1Host = l1Host + } + + l2Host := ctx.String(L2HostFlagName) + if len(l2Host) != 0 { + cfg.L2Host = l2Host + } + + if ctx.Command.Name == ForkCommandName { + cfg.ForkConfig = &ForkCLIConfig{ + L1ForkHeight: ctx.Uint64(L1ForkHeightFlagName), + Network: ctx.String(NetworkFlagName), + Chains: ctx.StringSlice(ChainsFlagName), + InteropEnabled: ctx.Bool(InteropEnabledFlagName), + } + } +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 000000000..150752f59 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,74 @@ +package config + +import ( + "os" + "path/filepath" + "testing" + + "github.com/urfave/cli/v2" +) + +func TestReadCLIConfig(t *testing.T) { + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("unable to determine executable path: %v", err) + } + + cfgPath := filepath.Join(cwd, "config.toml") + + tomlContent := ` + admin_port = 8420 + l1_port = 8545 + l2_starting_port = 9545 + interop_autorelay = true + interop_delay = 0 + logs_directory = "/var/logs" + l1_host = "127.0.0.1" + l2_host = "127.0.0.1" + [fork] + l1_fork_height = 0 + chains = ["automata", "base"] # Chains to fork in the superchain + network = "mainnet" # Superchain network. Options: sepolia-dev-0, mainnet, sepolia + interop_enabled = true + ` + + tmpFile, err := os.Create(cfgPath) + if err != nil { + t.Fatalf("failed to create config.toml file: %v", err) + } + defer os.Remove(cfgPath) + + if _, err := tmpFile.WriteString(tomlContent); err != nil { + t.Fatalf("failed to write to config.toml file: %v", err) + } + tmpFile.Close() + + ctx := cli.NewContext(nil, nil, nil) + + cfg, err := ReadCLIConfig(ctx) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if cfg.AdminPort != 8420 { + t.Errorf("expected AdminPort to be 8420, got %d", cfg.AdminPort) + } + if cfg.L1Port != 8545 { + t.Errorf("expected L1Port to be 8545, got %d", cfg.L1Port) + } + if cfg.L2StartingPort != 9545 { + t.Errorf("expected L2StartingPort to be 9545, got %d", cfg.L2StartingPort) + } + if cfg.InteropAutoRelay != true { + t.Errorf("expected InteropAutoRelay to be true, got %v", cfg.InteropAutoRelay) + } + if cfg.LogsDirectory != "/var/logs" { + t.Errorf("expected LogsDirectory to be '/var/logs', got '%s'", cfg.LogsDirectory) + } + if cfg.L1Host != "127.0.0.1" { + t.Errorf("expected L1Host to be '127.0.0.1', got '%s'", cfg.L1Host) + } + if cfg.L2Host != "127.0.0.1" { + t.Errorf("expected L2Host to be '127.0.0.1', got '%s'", cfg.L2Host) + } +} diff --git a/go.mod b/go.mod index b19d1a1a8..53b326eb4 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.20.5 // indirect diff --git a/go.sum b/go.sum index 51a5c45aa..4a34b5e07 100644 --- a/go.sum +++ b/go.sum @@ -304,6 +304,8 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=