From 0fd7d9385fabcdc862b5a05072244ca658f4d555 Mon Sep 17 00:00:00 2001 From: Sam Bukowski Date: Tue, 6 Aug 2024 15:30:52 -0600 Subject: [PATCH] Feature: `devrunner` config refactor (#146) * networks config refactor * services versions are now controlled by the networks config * dev version command now reads network config * init now downloads all services for all networks * wip refactor of process runner creation * implement generic service start based on network config * Errorf typo * dynamic base config * add panic on unknown network name, comment updates * file renames * update binaries config to structs * move service version config values * comment updates * created GetUserHomeDirOrPanic helper function * NewBaseConfig to DefaultBaseConfig * update var names, remove unneeded func --- modules/cli/cmd/devrunner/config/base.go | 145 +++++++ .../config/binaries_config_darwin_amd64.go | 38 +- .../config/binaries_config_darwin_arm64.go | 38 +- .../config/binaries_config_linux_amd64.go | 38 +- modules/cli/cmd/devrunner/config/constants.go | 8 + modules/cli/cmd/devrunner/config/helpers.go | 4 +- modules/cli/cmd/devrunner/config/networks.go | 390 +++++++----------- .../cmd/devrunner/config/service_versions.go | 9 - modules/cli/cmd/devrunner/init.go | 55 +-- modules/cli/cmd/devrunner/purge.go | 20 +- modules/cli/cmd/devrunner/reset.go | 41 +- modules/cli/cmd/devrunner/run.go | 319 +++++++------- modules/cli/cmd/devrunner/version.go | 36 +- modules/cli/cmd/helpers.go | 10 + modules/cli/cmd/sequencer/config.go | 8 +- modules/cli/cmd/sequencer/createaccount.go | 9 +- 16 files changed, 639 insertions(+), 529 deletions(-) create mode 100644 modules/cli/cmd/devrunner/config/base.go delete mode 100644 modules/cli/cmd/devrunner/config/service_versions.go diff --git a/modules/cli/cmd/devrunner/config/base.go b/modules/cli/cmd/devrunner/config/base.go new file mode 100644 index 00000000..0fa76570 --- /dev/null +++ b/modules/cli/cmd/devrunner/config/base.go @@ -0,0 +1,145 @@ +package config + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/astriaorg/astria-cli-go/modules/cli/cmd" + "github.com/pelletier/go-toml/v2" + log "github.com/sirupsen/logrus" + "github.com/spf13/viper" +) + +// BaseConfig is a map of string key-value pairs that represent the base +// configuration for all services in the Astria stack. The key-values pairs are +// also parsed into environment variables for the services to consume. A map was +// used here to allow for dynamically adding new configuration options to the +// config toml file. +type BaseConfig map[string]string + +// DefaultBaseConfig returns a BaseConfig with default values. +func DefaultBaseConfig(instanceName string) BaseConfig { + homeDir := cmd.GetUserHomeDirOrPanic() + return map[string]string{ + // conductor + "astria_conductor_celestia_block_time_ms": "1200", + "astria_conductor_celestia_bearer_token": "", + "astria_conductor_celestia_node_http_url": "http://127.0.0.1:26658", + "astria_conductor_execution_rpc_url": "http://127.0.0.1:50051", + "astria_conductor_execution_commit_level": "SoftOnly", + "astria_conductor_log": "astria_conductor=info", + "astria_conductor_no_otel": "true", + "astria_conductor_force_stdout": "true", + "astria_conductor_pretty_print": "true", + "astria_conductor_sequencer_grpc_url": "http://127.0.0.1:8080", + "astria_conductor_sequencer_cometbft_url": "http://127.0.0.1:26657", + "astria_conductor_sequencer_block_time_ms": "2000", + "astria_conductor_sequencer_requests_per_second": "500", + "astria_conductor_no_metrics": "true", + "astria_conductor_metrics_http_listener_addr": "127.0.0.1:9000", + + // sequencer + "astria_sequencer_listen_addr": "127.0.0.1:26658", + "astria_sequencer_db_filepath": filepath.Join(homeDir, ".astria", instanceName, DataDirName, "astria_sequencer_db"), + "astria_sequencer_enable_mint": "false", + "astria_sequencer_grpc_addr": "127.0.0.1:8080", + "astria_sequencer_log": "astria_sequencer=info", + "astria_sequencer_no_otel": "true", + "astria_sequencer_force_stdout": "true", + "astria_sequencer_no_metrics": "true", + "astria_sequencer_metrics_http_listener_addr": "127.0.0.1:9000", + "astria_sequencer_pretty_print": "true", + + // composer + "astria_composer_log": "astria_composer=info", + "astria_composer_no_otel": "true", + "astria_composer_force_stdout": "true", + "astria_composer_pretty_print": "true", + "astria_composer_api_listen_addr": "0.0.0.0:0", + "astria_composer_sequencer_url": "http://127.0.0.1:26657", + "astria_composer_sequencer_chain_id": "astria-dusk-" + duskNum, + "astria_composer_rollups": "astriachain::ws://127.0.0.1:8546", + "astria_composer_private_key_file": filepath.Join(homeDir, ".astria", instanceName, DefaultConfigDirName, "composer_dev_priv_key"), + "astria_composer_sequencer_address_prefix": "astria", + "astria_composer_max_submit_interval_ms": "2000", + "astria_composer_max_bytes_per_bundle": "200000", + "astria_composer_bundle_queue_capacity": "40000", + "astria_composer_no_metrics": "true", + "astria_composer_metrics_http_listener_addr": "127.0.0.1:9000", + "astria_composer_grpc_addr": "0.0.0.0:0", + "astria_composer_fee_asset": "nria", + + // ANSI + "no_color": "", + + // otel + "otel_exporter_otlp_endpoint": "http://localhost:4317", + "otel_exporter_otlp_traces_endpoint": "http://localhost:4317/v1/traces", + "otel_exporter_otlp_traces_timeout": "10", + "otel_exporter_otlp_traces_compression": "gzip", + "otel_exporter_otlp_headers": "key1=value1,key2=value2", + "otel_exporter_otlp_trace_headers": "key1=value1,key2=value2", + } +} + +// CreateBaseConfig creates a base configuration file at +// the given path, populating the file with the service defaults. +// It will skip initialization if the file already exists. It will panic if the +// file cannot be created or written to. +func CreateBaseConfig(path, instance string) { + _, err := os.Stat(path) + if err == nil { + log.Infof("%s already exists. Skipping initialization.\n", path) + return + } + // create an instance of the Config struct with some data + config := DefaultBaseConfig(instance) + + // open a file for writing + file, err := os.Create(path) + if err != nil { + panic(err) + } + defer file.Close() + + // encode the struct to TOML and write to the file + if err := toml.NewEncoder(file).Encode(config); err != nil { + panic(err) + } + log.Infof("New network config file created successfully: %s\n", path) +} + +// LoadBaseConfigOrPanic loads the BaseConfig from the given path. If the file +// cannot be loaded or parsed, the function will panic. +func LoadBaseConfigOrPanic(path string) BaseConfig { + viper.SetConfigFile(path) + + if err := viper.ReadInConfig(); err != nil { + log.Fatalf("Error reading config file, %s", err) + panic(err) + } + + // var config BaseConfig + config := make(map[string]string) + if err := viper.Unmarshal(&config); err != nil { + log.Fatalf("Unable to decode into struct, %v", err) + panic(err) + } + + return config +} + +// ToSlice creates a []string of "key=value" pairs out of a BaseConfig. +// The variable name will become the env var key and that variable's value will +// be the value. +func (b BaseConfig) ToSlice() []string { + var output []string + + for key, value := range b { + output = append(output, fmt.Sprintf("%s=%s", strings.ToUpper(key), value)) + } + + return output +} diff --git a/modules/cli/cmd/devrunner/config/binaries_config_darwin_amd64.go b/modules/cli/cmd/devrunner/config/binaries_config_darwin_amd64.go index 140f219a..f8058ae6 100644 --- a/modules/cli/cmd/devrunner/config/binaries_config_darwin_amd64.go +++ b/modules/cli/cmd/devrunner/config/binaries_config_darwin_amd64.go @@ -3,13 +3,37 @@ package config type Binary struct { - Name string - Url string + Name string + Version string + Url string } -var Binaries = []Binary{ - {"cometbft", "https://github.com/cometbft/cometbft/releases/download/v" + CometbftVersion + "/cometbft_" + CometbftVersion + "_darwin_amd64.tar.gz"}, - {"astria-sequencer", "https://github.com/astriaorg/astria/releases/download/sequencer-v" + AstriaSequencerVersion + "/astria-sequencer-x86_64-apple-darwin.tar.gz"}, - {"astria-composer", "https://github.com/astriaorg/astria/releases/download/composer-v" + AstriaComposerVersion + "/astria-composer-x86_64-apple-darwin.tar.gz"}, - {"astria-conductor", "https://github.com/astriaorg/astria/releases/download/conductor-v" + AstriaConductorVersion + "/astria-conductor-x86_64-apple-darwin.tar.gz"}, +type Binaries struct { + CometBFT Binary + AstriaSequencer Binary + AstriaComposer Binary + AstriaConductor Binary +} + +var KnownBinaries = Binaries{ + CometBFT: Binary{ + Name: "cometbft", + Version: "v" + CometbftVersion, + Url: "https://github.com/cometbft/cometbft/releases/download/v" + CometbftVersion + "/cometbft_" + CometbftVersion + "_darwin_amd64.tar.gz", + }, + AstriaSequencer: Binary{ + Name: "astria-sequencer", + Version: "v" + AstriaSequencerVersion, + Url: "https://github.com/astriaorg/astria/releases/download/sequencer-v" + AstriaSequencerVersion + "/astria-sequencer-x86_64-apple-darwin.tar.gz", + }, + AstriaComposer: Binary{ + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + Url: "https://github.com/astriaorg/astria/releases/download/composer-v" + AstriaComposerVersion + "/astria-composer-x86_64-apple-darwin.tar.gz", + }, + AstriaConductor: Binary{ + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + Url: "https://github.com/astriaorg/astria/releases/download/conductor-v" + AstriaConductorVersion + "/astria-conductor-x86_64-apple-darwin.tar.gz", + }, } diff --git a/modules/cli/cmd/devrunner/config/binaries_config_darwin_arm64.go b/modules/cli/cmd/devrunner/config/binaries_config_darwin_arm64.go index c6da8a5f..4510ef18 100644 --- a/modules/cli/cmd/devrunner/config/binaries_config_darwin_arm64.go +++ b/modules/cli/cmd/devrunner/config/binaries_config_darwin_arm64.go @@ -3,13 +3,37 @@ package config type Binary struct { - Name string - Url string + Name string + Version string + Url string } -var Binaries = []Binary{ - {"cometbft", "https://github.com/cometbft/cometbft/releases/download/v" + CometbftVersion + "/cometbft_" + CometbftVersion + "_darwin_arm64.tar.gz"}, - {"astria-sequencer", "https://github.com/astriaorg/astria/releases/download/sequencer-v" + AstriaSequencerVersion + "/astria-sequencer-aarch64-apple-darwin.tar.gz"}, - {"astria-composer", "https://github.com/astriaorg/astria/releases/download/composer-v" + AstriaComposerVersion + "/astria-composer-aarch64-apple-darwin.tar.gz"}, - {"astria-conductor", "https://github.com/astriaorg/astria/releases/download/conductor-v" + AstriaConductorVersion + "/astria-conductor-aarch64-apple-darwin.tar.gz"}, +type Binaries struct { + CometBFT Binary + AstriaSequencer Binary + AstriaComposer Binary + AstriaConductor Binary +} + +var KnownBinaries = Binaries{ + CometBFT: Binary{ + Name: "cometbft", + Version: "v" + CometbftVersion, + Url: "https://github.com/cometbft/cometbft/releases/download/v" + CometbftVersion + "/cometbft_" + CometbftVersion + "_darwin_arm64.tar.gz", + }, + AstriaSequencer: Binary{ + Name: "astria-sequencer", + Version: "v" + AstriaSequencerVersion, + Url: "https://github.com/astriaorg/astria/releases/download/sequencer-v" + AstriaSequencerVersion + "/astria-sequencer-aarch64-apple-darwin.tar.gz", + }, + AstriaComposer: Binary{ + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + Url: "https://github.com/astriaorg/astria/releases/download/composer-v" + AstriaComposerVersion + "/astria-composer-aarch64-apple-darwin.tar.gz", + }, + AstriaConductor: Binary{ + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + Url: "https://github.com/astriaorg/astria/releases/download/conductor-v" + AstriaConductorVersion + "/astria-conductor-aarch64-apple-darwin.tar.gz", + }, } diff --git a/modules/cli/cmd/devrunner/config/binaries_config_linux_amd64.go b/modules/cli/cmd/devrunner/config/binaries_config_linux_amd64.go index 0846f1ba..d0e522eb 100644 --- a/modules/cli/cmd/devrunner/config/binaries_config_linux_amd64.go +++ b/modules/cli/cmd/devrunner/config/binaries_config_linux_amd64.go @@ -3,13 +3,37 @@ package config type Binary struct { - Name string - Url string + Name string + Version string + Url string } -var Binaries = []Binary{ - {"cometbft", "https://github.com/cometbft/cometbft/releases/download/v" + CometbftVersion + "/cometbft_" + CometbftVersion + "_linux_amd64.tar.gz"}, - {"astria-sequencer", "https://github.com/astriaorg/astria/releases/download/sequencer-v" + AstriaSequencerVersion + "/astria-sequencer-x86_64-unknown-linux-gnu.tar.gz"}, - {"astria-composer", "https://github.com/astriaorg/astria/releases/download/composer-v" + AstriaComposerVersion + "/astria-composer-x86_64-unknown-linux-gnu.tar.gz"}, - {"astria-conductor", "https://github.com/astriaorg/astria/releases/download/conductor-v" + AstriaConductorVersion + "/astria-conductor-x86_64-unknown-linux-gnu.tar.gz"}, +type Binaries struct { + CometBFT Binary + AstriaSequencer Binary + AstriaComposer Binary + AstriaConductor Binary +} + +var KnownBinaries = Binaries{ + CometBFT: Binary{ + Name: "cometbft", + Version: "v" + CometbftVersion, + Url: "https://github.com/cometbft/cometbft/releases/download/v" + CometbftVersion + "/cometbft_" + CometbftVersion + "_linux_amd64.tar.gz", + }, + AstriaSequencer: Binary{ + Name: "astria-sequencer", + Version: "v" + AstriaSequencerVersion, + Url: "https://github.com/astriaorg/astria/releases/download/sequencer-v" + AstriaSequencerVersion + "/astria-sequencer-x86_64-unknown-linux-gnu.tar.gz", + }, + AstriaComposer: Binary{ + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + Url: "https://github.com/astriaorg/astria/releases/download/composer-v" + AstriaComposerVersion + "/astria-composer-x86_64-unknown-linux-gnu.tar.gz", + }, + AstriaConductor: Binary{ + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + Url: "https://github.com/astriaorg/astria/releases/download/conductor-v" + AstriaConductorVersion + "/astria-conductor-x86_64-unknown-linux-gnu.tar.gz", + }, } diff --git a/modules/cli/cmd/devrunner/config/constants.go b/modules/cli/cmd/devrunner/config/constants.go index 38291779..da055a0f 100644 --- a/modules/cli/cmd/devrunner/config/constants.go +++ b/modules/cli/cmd/devrunner/config/constants.go @@ -1,6 +1,8 @@ package config const ( + duskNum = "9" + dawnNum = "0" BinariesDirName = "bin" DataDirName = "data" DefaultBaseConfigName = "base-config.toml" @@ -13,4 +15,10 @@ const ( DefaultServiceLogLevel = "info" DefaultTargetNetwork = "local" LocalNativeDenom = "nria" + + // NOTE - do not include the 'v' at the beginning of the version number + CometbftVersion = "0.38.8" + AstriaSequencerVersion = "0.15.0" + AstriaComposerVersion = "0.8.1" + AstriaConductorVersion = "0.19.0" ) diff --git a/modules/cli/cmd/devrunner/config/helpers.go b/modules/cli/cmd/devrunner/config/helpers.go index c2f9a117..727c437d 100644 --- a/modules/cli/cmd/devrunner/config/helpers.go +++ b/modules/cli/cmd/devrunner/config/helpers.go @@ -179,12 +179,12 @@ func RecreateCometbftAndSequencerGenesisData(path, localNetworkName, localNative } // InitCometbft initializes CometBFT for running a local sequencer. -func InitCometbft(defaultDir string, dataDirName string, binDirName string, configDirName string) { +func InitCometbft(defaultDir string, dataDirName string, binDirName string, binVersion string, configDirName string) { log.Info("Initializing CometBFT for running local sequencer:") cometbftDataPath := filepath.Join(defaultDir, dataDirName, ".cometbft") // verify that cometbft was downloaded and extracted to the correct location - cometbftCmdPath := filepath.Join(defaultDir, binDirName, "cometbft") + cometbftCmdPath := filepath.Join(defaultDir, binDirName, "cometbft-v"+binVersion) if !util.PathExists(cometbftCmdPath) { log.Error("Error: cometbft binary not found here", cometbftCmdPath) log.Error("\tCannot continue with initialization.") diff --git a/modules/cli/cmd/devrunner/config/networks.go b/modules/cli/cmd/devrunner/config/networks.go index 68144dee..34fdfef7 100644 --- a/modules/cli/cmd/devrunner/config/networks.go +++ b/modules/cli/cmd/devrunner/config/networks.go @@ -4,9 +4,7 @@ import ( "fmt" "os" "path/filepath" - "reflect" "regexp" - "strings" log "github.com/sirupsen/logrus" @@ -14,184 +12,132 @@ import ( "github.com/spf13/viper" ) -const duskNum = "9" -const dawnNum = "0" - -type BaseConfig struct { - // conductor - Astria_conductor_celestia_block_time_ms int `mapstructure:"astria_conductor_celestia_block_time_ms" toml:"astria_conductor_celestia_block_time_ms"` - Astria_conductor_celestia_bearer_token string `mapstructure:"astria_conductor_celestia_bearer_token" toml:"astria_conductor_celestia_bearer_token"` - Astria_conductor_celestia_node_http_url string `mapstructure:"astria_conductor_celestia_node_http_url" toml:"astria_conductor_celestia_node_http_url"` - Astria_conductor_execution_rpc_url string `mapstructure:"astria_conductor_execution_rpc_url" toml:"astria_conductor_execution_rpc_url"` - Astria_conductor_execution_commit_level string `mapstructure:"astria_conductor_execution_commit_level" toml:"astria_conductor_execution_commit_level"` - Astria_conductor_log string `mapstructure:"astria_conductor_log" toml:"astria_conductor_log"` - Astria_conductor_no_otel bool `mapstructure:"astria_conductor_no_otel" toml:"astria_conductor_no_otel"` - Astria_conductor_force_stdout bool `mapstructure:"astria_conductor_force_stdout" toml:"astria_conductor_force_stdout"` - Astria_conductor_pretty_print bool `mapstructure:"astria_conductor_pretty_print" toml:"astria_conductor_pretty_print"` - Astria_conductor_sequencer_grpc_url string `mapstructure:"astria_conductor_sequencer_grpc_url" toml:"astria_conductor_sequencer_grpc_url"` - Astria_conductor_sequencer_cometbft_url string `mapstructure:"astria_conductor_sequencer_cometbft_url" toml:"astria_conductor_sequencer_cometbft_url"` - Astria_conductor_sequencer_block_time_ms int `mapstructure:"astria_conductor_sequencer_block_time_ms" toml:"astria_conductor_sequencer_block_time_ms"` - Astria_conductor_sequencer_requests_per_second int `mapstructure:"astria_conductor_sequencer_requests_per_second" toml:"astria_conductor_sequencer_requests_per_second"` - Astria_conductor_no_metrics bool `mapstructure:"astria_conductor_no_metrics" toml:"astria_conductor_no_metrics"` - Astria_conductor_metrics_http_listener_addr string `mapstructure:"astria_conductor_metrics_http_listener_addr" toml:"astria_conductor_metrics_http_listener_addr"` - - // sequencer - Astria_sequencer_listen_addr string `mapstructure:"astria_sequencer_listen_addr" toml:"astria_sequencer_listen_addr"` - Astria_sequencer_db_filepath string `mapstructure:"astria_sequencer_db_filepath" toml:"astria_sequencer_db_filepath"` - Astria_sequencer_enable_mint bool `mapstructure:"astria_sequencer_enable_mint" toml:"astria_sequencer_enable_mint"` - Astria_sequencer_grpc_addr string `mapstructure:"astria_sequencer_grpc_addr" toml:"astria_sequencer_grpc_addr"` - Astria_sequencer_log string `mapstructure:"astria_sequencer_log" toml:"astria_sequencer_log"` - Astria_sequencer_no_otel bool `mapstructure:"astria_sequencer_no_otel" toml:"astria_sequencer_no_otel"` - Astria_sequencer_force_stdout bool `mapstructure:"astria_sequencer_force_stdout" toml:"astria_sequencer_force_stdout"` - Astria_sequencer_no_metrics bool `mapstructure:"astria_sequencer_no_metrics" toml:"astria_sequencer_no_metrics"` - Astria_sequencer_metrics_http_listener_addr string `mapstructure:"astria_sequencer_metrics_http_listener_addr" toml:"astria_sequencer_metrics_http_listener_addr"` - Astria_sequencer_pretty_print bool `mapstructure:"astria_sequencer_pretty_print" toml:"astria_sequencer_pretty_print"` - - // composer - Astria_composer_log string `mapstructure:"astria_composer_log" toml:"astria_composer_log"` - Astria_composer_no_otel bool `mapstructure:"astria_composer_no_otel" toml:"astria_composer_no_otel"` - Astria_composer_force_stdout bool `mapstructure:"astria_composer_force_stdout" toml:"astria_composer_force_stdout"` - Astria_composer_pretty_print bool `mapstructure:"astria_composer_pretty_print" toml:"astria_composer_pretty_print"` - Astria_composer_api_listen_addr string `mapstructure:"astria_composer_api_listen_addr" toml:"astria_composer_api_listen_addr"` - Astria_composer_sequencer_url string `mapstructure:"astria_composer_sequencer_url" toml:"astria_composer_sequencer_url"` - Astria_composer_sequencer_chain_id string `mapstructure:"astria_composer_sequencer_chain_id" toml:"astria_composer_sequencer_chain_id"` - Astria_composer_rollups string `mapstructure:"astria_composer_rollups" toml:"astria_composer_rollups"` - Astria_composer_private_key_file string `mapstructure:"astria_composer_private_key_file" toml:"astria_composer_private_key_file"` - Astria_composer_sequencer_address_prefix string `mapstructure:"astria_composer_sequencer_address_prefix" toml:"astria_composer_sequencer_address_prefix"` - Astria_composer_max_submit_interval_ms int `mapstructure:"astria_composer_max_submit_interval_ms" toml:"astria_composer_max_submit_interval_ms"` - Astria_composer_max_bytes_per_bundle int `mapstructure:"astria_composer_max_bytes_per_bundle" toml:"astria_composer_max_bytes_per_bundle"` - Astria_composer_bundle_queue_capacity int `mapstructure:"astria_composer_bundle_queue_capacity" toml:"astria_composer_bundle_queue_capacity"` - Astria_composer_no_metrics bool `mapstructure:"astria_composer_no_metrics" toml:"astria_composer_no_metrics"` - Astria_composer_metrics_http_listener_addr string `mapstructure:"astria_composer_metrics_http_listener_addr" toml:"astria_composer_metrics_http_listener_addr"` - Astria_composer_grpc_addr string `mapstructure:"astria_composer_grpc_addr" toml:"astria_composer_grpc_addr"` - Astria_composer_fee_asset string `mapstructure:"astria_composer_fee_asset" toml:"astria_composer_fee_asset"` - - // global - No_color string `mapstructure:"no_color" toml:"no_color"` - - // otel - Otel_exporter_otlp_endpoint string `mapstructure:"otel_exporter_otlp_endpoint" toml:"otel_exporter_otlp_endpoint"` - Otel_exporter_otlp_traces_endpoint string `mapstructure:"otel_exporter_otlp_traces_endpoint" toml:"otel_exporter_otlp_traces_endpoint"` - Otel_exporter_otlp_traces_timeout int `mapstructure:"otel_exporter_otlp_traces_timeout" toml:"otel_exporter_otlp_traces_timeout"` - Otel_exporter_otlp_traces_compression string `mapstructure:"otel_exporter_otlp_traces_compression" toml:"otel_exporter_otlp_traces_compression"` - Otel_exporter_otlp_headers string `mapstructure:"otel_exporter_otlp_headers" toml:"otel_exporter_otlp_headers"` - Otel_exporter_otlp_trace_headers string `mapstructure:"otel_exporter_otlp_trace_headers" toml:"otel_exporter_otlp_trace_headers"` -} - -func NewBaseConfig(instanceName string) BaseConfig { - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - return BaseConfig{ - Astria_conductor_celestia_block_time_ms: 1200, - Astria_conductor_celestia_bearer_token: "", - Astria_conductor_celestia_node_http_url: "http://127.0.0.1:26658", - Astria_conductor_execution_rpc_url: "http://127.0.0.1:50051", - Astria_conductor_execution_commit_level: "SoftOnly", - Astria_conductor_log: "astria_conductor=info", - Astria_conductor_no_otel: true, - Astria_conductor_force_stdout: true, - Astria_conductor_pretty_print: true, - Astria_conductor_sequencer_grpc_url: "http://127.0.0.1:8080", - Astria_conductor_sequencer_cometbft_url: "http://127.0.0.1:26657", - Astria_conductor_sequencer_block_time_ms: 2000, - Astria_conductor_sequencer_requests_per_second: 500, - Astria_conductor_no_metrics: true, - Astria_conductor_metrics_http_listener_addr: "127.0.0.1:9000", - - Astria_sequencer_listen_addr: "127.0.0.1:26658", - Astria_sequencer_db_filepath: filepath.Join(homePath, ".astria", instanceName, DataDirName, "astria_sequencer_db"), - Astria_sequencer_enable_mint: false, - Astria_sequencer_grpc_addr: "127.0.0.1:8080", - Astria_sequencer_log: "astria_sequencer=info", - Astria_sequencer_no_otel: true, - Astria_sequencer_force_stdout: true, - Astria_sequencer_no_metrics: true, - Astria_sequencer_metrics_http_listener_addr: "127.0.0.1:9000", - Astria_sequencer_pretty_print: true, - - Astria_composer_log: "astria_composer=info", - Astria_composer_no_otel: true, - Astria_composer_force_stdout: true, - Astria_composer_pretty_print: true, - Astria_composer_api_listen_addr: "0.0.0.0:0", - Astria_composer_sequencer_url: "http://127.0.0.1:26657", - Astria_composer_sequencer_chain_id: "astria-dusk-" + duskNum, - Astria_composer_rollups: "astriachain::ws://127.0.0.1:8546", - Astria_composer_private_key_file: filepath.Join(homePath, ".astria", instanceName, DefaultConfigDirName, "composer_dev_priv_key"), - Astria_composer_sequencer_address_prefix: "astria", - Astria_composer_max_submit_interval_ms: 2000, - Astria_composer_max_bytes_per_bundle: 200000, - Astria_composer_bundle_queue_capacity: 40000, - Astria_composer_no_metrics: true, - Astria_composer_metrics_http_listener_addr: "127.0.0.1:9000", - Astria_composer_grpc_addr: "0.0.0.0:0", - Astria_composer_fee_asset: "nria", - - No_color: "", - - Otel_exporter_otlp_endpoint: "http://localhost:4317", - Otel_exporter_otlp_traces_endpoint: "http://localhost:4317/v1/traces", - Otel_exporter_otlp_traces_timeout: 10, - Otel_exporter_otlp_traces_compression: "gzip", - Otel_exporter_otlp_headers: "key1=value1,key2=value2", - Otel_exporter_otlp_trace_headers: "key1=value1,key2=value2", - } -} - // NetworkConfigs is the struct that holds the configuration for all individual Astria networks. type NetworkConfigs struct { - Local NetworkConfig `mapstructure:"local" toml:"local"` - Dusk NetworkConfig `mapstructure:"dusk" toml:"dusk"` - Dawn NetworkConfig `mapstructure:"dawn" toml:"dawn"` - Mainnet NetworkConfig `mapstructure:"mainnet" toml:"mainnet"` + Configs map[string]NetworkConfig `mapstructure:"networks" toml:"networks"` +} + +type ServiceConfig struct { + Name string `mapstructure:"name" toml:"name"` + Version string `mapstructure:"version" toml:"version"` + DownloadURL string `mapstructure:"download_url" toml:"download_url"` + LocalPath string `mapstructure:"local_path" toml:"local_path"` + // TODO: implement generic args? } // NetworkConfig is the struct that holds the configuration for an individual Astria network. type NetworkConfig struct { - SequencerChainId string `mapstructure:"sequencer_chain_id" toml:"sequencer_chain_id"` - SequencerGRPC string `mapstructure:"sequencer_grpc" toml:"sequencer_grpc"` - SequencerRPC string `mapstructure:"sequencer_rpc" toml:"sequencer_rpc"` - RollupName string `mapstructure:"rollup_name" toml:"rollup_name"` - NativeDenom string `mapstructure:"default_denom" toml:"default_denom"` + SequencerChainId string `mapstructure:"sequencer_chain_id" toml:"sequencer_chain_id"` + SequencerGRPC string `mapstructure:"sequencer_grpc" toml:"sequencer_grpc"` + SequencerRPC string `mapstructure:"sequencer_rpc" toml:"sequencer_rpc"` + RollupName string `mapstructure:"rollup_name" toml:"rollup_name"` + NativeDenom string `mapstructure:"default_denom" toml:"default_denom"` + Services map[string]ServiceConfig `mapstructure:"services" toml:"services"` } // DefaultNetworksConfigs returns a NetworksConfig struct populated with all // network defaults. -func DefaultNetworksConfigs() NetworkConfigs { - config := NetworkConfigs{ - Local: NetworkConfig{ - SequencerChainId: "sequencer-test-chain-0", - SequencerGRPC: "http://127.0.0.1:8080", - SequencerRPC: "http://127.0.0.1:26657", - RollupName: "astria-test-chain", - NativeDenom: "nria", - }, - Dusk: NetworkConfig{ - SequencerChainId: "astria-dusk-" + duskNum, - SequencerGRPC: "https://grpc.sequencer.dusk-" + duskNum + ".devnet.astria.org/", - SequencerRPC: "https://rpc.sequencer.dusk-" + duskNum + ".devnet.astria.org/", - RollupName: "", - NativeDenom: "nria", - }, - Dawn: NetworkConfig{ - SequencerChainId: "astria-dawn-" + dawnNum, - SequencerGRPC: "https://grpc.sequencer.dawn-" + dawnNum + ".devnet.astria.org/", - SequencerRPC: "https://rpc.sequencer.dawn-" + dawnNum + ".devnet.astria.org/", - RollupName: "", - NativeDenom: "ibc/channel0/utia", - }, - Mainnet: NetworkConfig{ - SequencerChainId: "astria", - SequencerGRPC: "https://grpc.sequencer.astria.org/", - SequencerRPC: "https://rpc.sequencer.astria.org/", - RollupName: "", - NativeDenom: "ibc/channel0/utia", +func DefaultNetworksConfigs(defaultBinDir string) NetworkConfigs { + return NetworkConfigs{ + Configs: map[string]NetworkConfig{ + "local": { + SequencerChainId: "sequencer-test-chain-0", + SequencerGRPC: "http://127.0.0.1:8080", + SequencerRPC: "http://127.0.0.1:26657", + RollupName: "astria-test-chain", + NativeDenom: "nria", + Services: map[string]ServiceConfig{ + "conductor": { + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + DownloadURL: KnownBinaries.AstriaConductor.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-conductor-v"+AstriaConductorVersion), + }, + "composer": { + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + DownloadURL: KnownBinaries.AstriaComposer.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-composer-v"+AstriaComposerVersion), + }, + "sequencer": { + Name: "astria-sequencer", + Version: "v" + AstriaSequencerVersion, + DownloadURL: KnownBinaries.AstriaSequencer.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-sequencer-v"+AstriaSequencerVersion), + }, + "cometbft": { + Name: "cometbft", + Version: "v" + CometbftVersion, + DownloadURL: KnownBinaries.CometBFT.Url, + LocalPath: filepath.Join(defaultBinDir, "cometbft-v"+CometbftVersion), + }, + }, + }, + "dusk": { + SequencerChainId: "astria-dusk-" + duskNum, + SequencerGRPC: "https://grpc.sequencer.dusk-" + duskNum + ".devnet.astria.org/", + SequencerRPC: "https://rpc.sequencer.dusk-" + duskNum + ".devnet.astria.org/", + RollupName: "", + NativeDenom: "nria", + Services: map[string]ServiceConfig{ + "conductor": { + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + DownloadURL: KnownBinaries.AstriaConductor.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-conductor-v"+AstriaConductorVersion), + }, + "composer": { + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + DownloadURL: KnownBinaries.AstriaComposer.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-composer-v"+AstriaComposerVersion), + }, + }, + }, + "dawn": { + SequencerChainId: "astria-dawn-" + dawnNum, + SequencerGRPC: "https://grpc.sequencer.dawn-" + dawnNum + ".devnet.astria.org/", + SequencerRPC: "https://rpc.sequencer.dawn-" + dawnNum + ".devnet.astria.org/", + RollupName: "", + NativeDenom: "ibc/channel0/utia", + Services: map[string]ServiceConfig{ + "conductor": { + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + DownloadURL: KnownBinaries.AstriaConductor.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-conductor-v"+AstriaConductorVersion), + }, + "composer": { + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + DownloadURL: KnownBinaries.AstriaComposer.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-composer-v"+AstriaComposerVersion), + }, + }, + }, + "mainnet": { + SequencerChainId: "astria", + SequencerGRPC: "https://grpc.sequencer.astria.org/", + SequencerRPC: "https://rpc.sequencer.astria.org/", + RollupName: "", + NativeDenom: "ibc/channel0/utia", + Services: map[string]ServiceConfig{ + "conductor": { + Name: "astria-conductor", + Version: "v" + AstriaConductorVersion, + DownloadURL: KnownBinaries.AstriaConductor.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-conductor-v"+AstriaConductorVersion), + }, + "composer": { + Name: "astria-composer", + Version: "v" + AstriaComposerVersion, + DownloadURL: KnownBinaries.AstriaComposer.Url, + LocalPath: filepath.Join(defaultBinDir, "astria-composer-v"+AstriaComposerVersion), + }, + }, + }, }, } - return config } // LoadNetworkConfigsOrPanic loads the NetworksConfig from the given path. If the file @@ -213,53 +159,30 @@ func LoadNetworkConfigsOrPanic(path string) NetworkConfigs { return config } -// CreateNetworksConfig creates a networks configuration file at -// the given path, populating the file with the network defaults, and overriding -// the default local denom and local sequencer network chain id . -// It will skip initialization if the file already exists. It will panic if the -// file cannot be created or written to. -func CreateNetworksConfig(path, localSequencerChainId, localNativeDenom string) { - - _, err := os.Stat(path) - if err == nil { - log.Infof("%s already exists. Skipping initialization.\n", path) - return - } - // create an instance of the Config struct with some data - config := DefaultNetworksConfigs() - config.Local.NativeDenom = localNativeDenom - config.Local.SequencerChainId = localSequencerChainId - - // open a file for writing - file, err := os.Create(path) - if err != nil { - panic(err) - } - defer file.Close() - - // encode the struct to TOML and write to the file - if err := toml.NewEncoder(file).Encode(config); err != nil { - panic(err) - } - log.Infof("New network config file created successfully: %s\n", path) -} - -// CreateBaseConfig creates a base configuration file at -// the given path, populating the file with the service defaults. -// It will skip initialization if the file already exists. It will panic if the -// file cannot be created or written to. -func CreateBaseConfig(path, instance string) { - - _, err := os.Stat(path) +// CreateNetworksConfig creates a networks configuration file and populates it +// with the network defaults. The binPath is required to accommodate which CLI +// instance this particular networks config is for and to build the proper paths +// to the binaries that will be used for the given instance. The configPath is +// provided for the same reason; which instance is this config file for and +// where to put it. This function will also override the default local denom and local +// sequencer network chain id based on the command line flags provided. It will +// skip initialization if the file already exists. It will panic if the file +// cannot be created or written to. +func CreateNetworksConfig(binPath, configPath, localSequencerChainId, localNativeDenom string) { + _, err := os.Stat(configPath) if err == nil { - log.Infof("%s already exists. Skipping initialization.\n", path) + log.Infof("%s already exists. Skipping initialization.\n", configPath) return } // create an instance of the Config struct with some data - config := NewBaseConfig(instance) + config := DefaultNetworksConfigs(binPath) + local := config.Configs["local"] + local.NativeDenom = localNativeDenom + local.SequencerChainId = localSequencerChainId + config.Configs["local"] = local // open a file for writing - file, err := os.Create(path) + file, err := os.Create(configPath) if err != nil { panic(err) } @@ -269,48 +192,7 @@ func CreateBaseConfig(path, instance string) { if err := toml.NewEncoder(file).Encode(config); err != nil { panic(err) } - log.Infof("New network config file created successfully: %s\n", path) -} - -// LoadBaseConfigOrPanic loads the BaseConfig from the given path. If the file -// cannot be loaded or parsed, the function will panic. -func LoadBaseConfigOrPanic(path string) BaseConfig { - viper.SetConfigFile(path) - - if err := viper.ReadInConfig(); err != nil { - log.Fatalf("Error reading config file, %s", err) - panic(err) - } - - var config BaseConfig - if err := viper.Unmarshal(&config); err != nil { - log.Fatalf("Unable to decode into struct, %v", err) - panic(err) - } - - return config -} - -// ToSlice creates a []string of "key=value" pairs out of a BaseConfig. -// The variable name will become the env var key and that variable's value will -// be the value. -func (b BaseConfig) ToSlice() []string { - val := reflect.ValueOf(b) - typ := reflect.TypeOf(b) - - var output []string - // ensure the provided variable is a struct - for i := 0; i < val.NumField(); i++ { - field := typ.Field(i) - value := val.Field(i) - if value.Kind() == reflect.String { - output = append(output, fmt.Sprintf("%s=%s", strings.ToUpper(field.Name), value.String())) - } else { - output = append(output, fmt.Sprintf("%s=%v", strings.ToUpper(field.Name), value.Interface())) - } - } - - return output + log.Infof("New network config file created successfully: %s\n", configPath) } // GetEndpointOverrides returns a slice of environment variables for supporting @@ -319,7 +201,11 @@ func (b BaseConfig) ToSlice() []string { // the default environment variables for the network configuration. It uses the // BaseConfig to properly update the ASTRIA_COMPOSER_ROLLUPS env var. func (n NetworkConfig) GetEndpointOverrides(bc BaseConfig) []string { - rollupEndpoint := bc.Astria_composer_rollups + rollupEndpoint, exists := bc["astria_composer_rollups"] + if !exists { + log.Error("ASTRIA_COMPOSER_ROLLUPS not found in BaseConfig") + panic(fmt.Errorf("ASTRIA_COMPOSER_ROLLUPS not found in BaseConfig")) + } // get the rollup ws endpoint pattern := `ws{1,2}:\/\/.*:\d+` re, err := regexp.Compile(pattern) diff --git a/modules/cli/cmd/devrunner/config/service_versions.go b/modules/cli/cmd/devrunner/config/service_versions.go deleted file mode 100644 index 7e8ee175..00000000 --- a/modules/cli/cmd/devrunner/config/service_versions.go +++ /dev/null @@ -1,9 +0,0 @@ -package config - -// NOTE - do not include the 'v' at the beginning of the version number -const ( - CometbftVersion = "0.38.8" - AstriaSequencerVersion = "0.15.0" - AstriaComposerVersion = "0.8.1" - AstriaConductorVersion = "0.19.0" -) diff --git a/modules/cli/cmd/devrunner/init.go b/modules/cli/cmd/devrunner/init.go index 999e128c..5a1c26ea 100644 --- a/modules/cli/cmd/devrunner/init.go +++ b/modules/cli/cmd/devrunner/init.go @@ -3,6 +3,7 @@ package devrunner import ( "archive/tar" "compress/gzip" + "fmt" "io" "net/http" "os" @@ -41,13 +42,9 @@ func runInitialization(c *cobra.Command, args []string) { localNetworkName := flagHandler.GetValue("local-network-name") config.IsSequencerChainIdValidOrPanic(localNetworkName) - localDefaultDenom := flagHandler.GetValue("local-native-denom") + localDenom := flagHandler.GetValue("local-native-denom") - homeDir, err := os.UserHomeDir() - if err != nil { - log.Error("error getting home dir:", err) - return - } + homeDir := cmd.GetUserHomeDirOrPanic() // TODO: make the default home dir configurable defaultDir := filepath.Join(homeDir, ".astria") instanceDir := filepath.Join(defaultDir, instance) @@ -55,8 +52,14 @@ func runInitialization(c *cobra.Command, args []string) { log.Info("Creating new instance in:", instanceDir) cmd.CreateDirOrPanic(instanceDir) + // create the local bin directory for downloaded binaries + localBinPath := filepath.Join(instanceDir, config.BinariesDirName) + log.Info("Binary files for locally running a sequencer placed in: ", localBinPath) + cmd.CreateDirOrPanic(localBinPath) + networksConfigPath := filepath.Join(defaultDir, instance, config.DefaultNetworksConfigName) - config.CreateNetworksConfig(networksConfigPath, localNetworkName, localDefaultDenom) + config.CreateNetworksConfig(localBinPath, networksConfigPath, localNetworkName, localDenom) + networkConfigs := config.LoadNetworkConfigsOrPanic(networksConfigPath) configDirPath := filepath.Join(instanceDir, config.DefaultConfigDirName) cmd.CreateDirOrPanic(configDirPath) @@ -66,20 +69,22 @@ func runInitialization(c *cobra.Command, args []string) { config.CreateComposerDevPrivKeyFile(configDirPath) - config.RecreateCometbftAndSequencerGenesisData(configDirPath, localNetworkName, localDefaultDenom) + config.RecreateCometbftAndSequencerGenesisData(configDirPath, localNetworkName, localDenom) - // create the local bin directory for downloaded binaries - localBinPath := filepath.Join(instanceDir, config.BinariesDirName) - log.Info("Binary files for locally running a sequencer placed in: ", localBinPath) - cmd.CreateDirOrPanic(localBinPath) - for _, bin := range config.Binaries { - downloadAndUnpack(bin.Url, bin.Name, localBinPath) + // download and unpack all services for all networks + for label := range networkConfigs.Configs { + purpleANSI := "\033[35m" + resetANSI := "\033[0m" + log.Info(fmt.Sprint("--Downloading binaries for network: ", purpleANSI, label, resetANSI)) + for _, bin := range networkConfigs.Configs[label].Services { + downloadAndUnpack(bin.DownloadURL, bin.Version, bin.Name, localBinPath) + } } // create the data directory for cometbft and sequencer dataPath := filepath.Join(instanceDir, config.DataDirName) cmd.CreateDirOrPanic(dataPath) - config.InitCometbft(instanceDir, config.DataDirName, config.BinariesDirName, config.DefaultConfigDirName) + config.InitCometbft(instanceDir, config.DataDirName, config.BinariesDirName, config.CometbftVersion, config.DefaultConfigDirName) log.Infof("Initialization of instance \"%s\" completed successfuly.", instance) @@ -107,7 +112,7 @@ func downloadFile(url, filepath string) error { } // extractTarGz extracts a .tar.gz file to dest. -func extractTarGz(dest string, gzipStream io.Reader) error { +func extractTarGz(dest string, version string, gzipStream io.Reader) error { uncompressedStream, err := gzip.NewReader(gzipStream) if err != nil { return err @@ -128,9 +133,8 @@ func extractTarGz(dest string, gzipStream io.Reader) error { } // the target location where the dir/file should be created - target := filepath.Join(dest, header.Name) + target := filepath.Join(dest, header.Name+"-"+version) - // the following switch could also be done using if/else statements switch header.Typeflag { case tar.TypeDir: // handle directory @@ -152,16 +156,21 @@ func extractTarGz(dest string, gzipStream io.Reader) error { } } -func downloadAndUnpack(url string, packageName string, placePath string) { +func downloadAndUnpack(url, version, packageName, placePath string) { + if url == "" { + log.Infof("No source URL provided for %s. Skipping download.\n", packageName) + return + } + // check if the file already exists - if _, err := os.Stat(filepath.Join(placePath, packageName)); err == nil { + if _, err := os.Stat(filepath.Join(placePath, packageName+"-"+version)); err == nil { log.Infof("%s already exists. Skipping download.\n", packageName) return } - log.Infof("Downloading: (%s, %s)\n", packageName, url) + log.Infof("Downloading: %s, %s, %s\n", packageName, version, url) // download the file - dest := filepath.Join(placePath, packageName+".tar.gz") + dest := filepath.Join(placePath, packageName+"-"+version+".tar.gz") if err := downloadFile(url, dest); err != nil { panic(err) } @@ -173,7 +182,7 @@ func downloadAndUnpack(url string, packageName string, placePath string) { defer file.Close() // extract the contents - if err := extractTarGz(placePath, file); err != nil { + if err := extractTarGz(placePath, version, file); err != nil { panic(err) } diff --git a/modules/cli/cmd/devrunner/purge.go b/modules/cli/cmd/devrunner/purge.go index 1ce48077..1d79440c 100644 --- a/modules/cli/cmd/devrunner/purge.go +++ b/modules/cli/cmd/devrunner/purge.go @@ -32,17 +32,13 @@ func purgeBinariesCmdHandler(c *cobra.Command, _ []string) { instance := flagHandler.GetValue("instance") config.IsInstanceNameValidOrPanic(instance) - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - binDir := filepath.Join(homePath, ".astria", instance, config.BinariesDirName) + homeDir := cmd.GetUserHomeDirOrPanic() + binDir := filepath.Join(homeDir, ".astria", instance, config.BinariesDirName) log.Infof("Deleting binaries for instance '%s'", instance) // remove the state files for sequencer and Cometbft - err = os.RemoveAll(binDir) + err := os.RemoveAll(binDir) if err != nil { fmt.Println("Error removing file:", err) return @@ -67,17 +63,13 @@ func purgeAllCmdHandler(c *cobra.Command, _ []string) { instance := flagHandler.GetValue("instance") config.IsInstanceNameValidOrPanic(instance) - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - instanceDir := filepath.Join(homePath, ".astria", instance) + homeDir := cmd.GetUserHomeDirOrPanic() + instanceDir := filepath.Join(homeDir, ".astria", instance) log.Infof("Deleting instance '%s'", instance) // remove the state files for sequencer and Cometbft - err = os.RemoveAll(instanceDir) + err := os.RemoveAll(instanceDir) if err != nil { fmt.Println("Error removing file:", err) return diff --git a/modules/cli/cmd/devrunner/reset.go b/modules/cli/cmd/devrunner/reset.go index e9104f3e..fb97e3da 100644 --- a/modules/cli/cmd/devrunner/reset.go +++ b/modules/cli/cmd/devrunner/reset.go @@ -36,20 +36,16 @@ func resetConfigCmdHandler(c *cobra.Command, _ []string) { localNetworkName := flagHandler.GetValue("local-network-name") config.IsSequencerChainIdValidOrPanic(localNetworkName) - localDefaultDenom := flagHandler.GetValue("local-native-denom") + localDenom := flagHandler.GetValue("local-native-denom") - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - localConfigDir := filepath.Join(homePath, ".astria", instance, config.DefaultConfigDirName) + homeDir := cmd.GetUserHomeDirOrPanic() + localConfigDir := filepath.Join(homeDir, ".astria", instance, config.DefaultConfigDirName) baseConfigPath := filepath.Join(localConfigDir, config.DefaultBaseConfigName) log.Infof("Resetting config for instance '%s'", instance) // remove the config files - err = os.Remove(filepath.Join(localConfigDir, config.DefaultCometbftGenesisFilename)) + err := os.Remove(filepath.Join(localConfigDir, config.DefaultCometbftGenesisFilename)) if err != nil { fmt.Println("Error removing file:", err) return @@ -65,7 +61,7 @@ func resetConfigCmdHandler(c *cobra.Command, _ []string) { return } - config.RecreateCometbftAndSequencerGenesisData(localConfigDir, localNetworkName, localDefaultDenom) + config.RecreateCometbftAndSequencerGenesisData(localConfigDir, localNetworkName, localDenom) config.CreateBaseConfig(baseConfigPath, instance) log.Infof("Successfully reset config files for instance '%s'", instance) @@ -88,21 +84,18 @@ func resetNetworksCmdHandler(c *cobra.Command, _ []string) { localNetworkName := flagHandler.GetValue("local-network-name") config.IsSequencerChainIdValidOrPanic(localNetworkName) - localDefaultDenom := flagHandler.GetValue("local-native-denom") + localDenom := flagHandler.GetValue("local-native-denom") - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - networksConfigPath := filepath.Join(homePath, ".astria", instance, config.DefaultNetworksConfigName) + homeDir := cmd.GetUserHomeDirOrPanic() + networksConfigPath := filepath.Join(homeDir, ".astria", instance, config.DefaultNetworksConfigName) - err = os.Remove(networksConfigPath) + err := os.Remove(networksConfigPath) if err != nil { fmt.Println("Error removing file:", err) return } - config.CreateNetworksConfig(networksConfigPath, localNetworkName, localDefaultDenom) + localBinPath := filepath.Join(homeDir, ".astria", instance, config.BinariesDirName) + config.CreateNetworksConfig(localBinPath, networksConfigPath, localNetworkName, localDenom) } // resetStateCmd represents the 'reset state' command @@ -119,24 +112,20 @@ func resetStateCmdHandler(c *cobra.Command, _ []string) { instance := flagHandler.GetValue("instance") config.IsInstanceNameValidOrPanic(instance) - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - instanceDir := filepath.Join(homePath, ".astria", instance) + homeDir := cmd.GetUserHomeDirOrPanic() + instanceDir := filepath.Join(homeDir, ".astria", instance) dataDir := filepath.Join(instanceDir, config.DataDirName) log.Infof("Resetting state for instance '%s'", instance) // remove the state files for sequencer and Cometbft - err = os.RemoveAll(dataDir) + err := os.RemoveAll(dataDir) if err != nil { fmt.Println("Error removing file:", err) return } cmd.CreateDirOrPanic(dataDir) - config.InitCometbft(instanceDir, config.DataDirName, config.BinariesDirName, config.DefaultConfigDirName) + config.InitCometbft(instanceDir, config.DataDirName, config.BinariesDirName, config.CometbftVersion, config.DefaultConfigDirName) log.Infof("Successfully reset state for instance '%s'", instance) } diff --git a/modules/cli/cmd/devrunner/run.go b/modules/cli/cmd/devrunner/run.go index 126bb4f0..1bd17aec 100644 --- a/modules/cli/cmd/devrunner/run.go +++ b/modules/cli/cmd/devrunner/run.go @@ -1,9 +1,9 @@ package devrunner import ( + "context" "fmt" "net/http" - "os" "path/filepath" "strings" "time" @@ -42,14 +42,9 @@ func runCmdHandler(c *cobra.Command, args []string) { flagHandler := cmd.CreateCliFlagHandler(c, cmd.EnvPrefix) ctx := c.Context() - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } + homeDir := cmd.GetUserHomeDirOrPanic() - // astriaDir is the directory where all the astria instances data is stored - astriaDir := filepath.Join(homePath, ".astria") + astriaDir := filepath.Join(homeDir, ".astria") instance := flagHandler.GetValue("instance") config.IsInstanceNameValidOrPanic(instance) @@ -65,169 +60,113 @@ func runCmdHandler(c *cobra.Command, args []string) { networksConfigPath := filepath.Join(astriaDir, instance, config.DefaultNetworksConfigName) networkConfigs := config.LoadNetworkConfigsOrPanic(networksConfigPath) + // check if the network exists in the networks config + if _, ok := networkConfigs.Configs[network]; !ok { + log.Fatalf("Network %s not found in config file at %s", network, networksConfigPath) + panic("Network not found in config file") + } + // get the log level for the Astria Services using override env vars. // The log level for Cometbft is updated via command line flags and is set // in the ProcessRunnerOpts for the Cometbft ProcessRunner serviceLogLevel := flagHandler.GetValue("service-log-level") - - // we will set runners after we decide which binaries we need to run - var runners []processrunner.ProcessRunner - - // setup services based on network config - switch network { - case "local": - networkOverrides := networkConfigs.Local.GetEndpointOverrides(baseConfig) - serviceLogLevelOverrides := config.GetServiceLogLevelOverrides(serviceLogLevel) - environment := config.MergeConfigs(baseConfigEnvVars, networkOverrides, serviceLogLevelOverrides) - config.LogEnv(environment) - - log.Debug("Running local sequencer") - dataDir := filepath.Join(astriaDir, instance, config.DataDirName) - binDir := filepath.Join(astriaDir, instance, config.BinariesDirName) - - // get the binary paths - conductorPath := getFlagPath(c, "conductor-path", "conductor", filepath.Join(binDir, "astria-conductor")) - cometbftPath := getFlagPath(c, "cometbft-path", "cometbft", filepath.Join(binDir, "cometbft")) - composerPath := getFlagPath(c, "composer-path", "composer", filepath.Join(binDir, "astria-composer")) - sequencerPath := getFlagPath(c, "sequencer-path", "sequencer", filepath.Join(binDir, "astria-sequencer")) - log.Debugf("Using binaries from %s", binDir) - - // sequencer - seqRCOpts := processrunner.ReadyCheckerOpts{ - CallBackName: "Sequencer gRPC server is OK", - Callback: getSequencerOKCallback(environment), - RetryCount: 10, - RetryInterval: 100 * time.Millisecond, - HaltIfFailed: false, - } - seqReadinessCheck := processrunner.NewReadyChecker(seqRCOpts) - seqOpts := processrunner.NewProcessRunnerOpts{ - Title: "Sequencer", - BinPath: sequencerPath, - Env: environment, - Args: nil, - ReadyCheck: &seqReadinessCheck, - } - seqRunner := processrunner.NewProcessRunner(ctx, seqOpts) - - // cometbft - cometRCOpts := processrunner.ReadyCheckerOpts{ - CallBackName: "CometBFT rpc server is OK", - Callback: getCometbftOKCallback(environment), - RetryCount: 10, - RetryInterval: 100 * time.Millisecond, - HaltIfFailed: false, - } - cometReadinessCheck := processrunner.NewReadyChecker(cometRCOpts) - cometDataPath := filepath.Join(dataDir, ".cometbft") - cometOpts := processrunner.NewProcessRunnerOpts{ - Title: "Comet BFT", - BinPath: cometbftPath, - Env: environment, - Args: []string{"node", "--home", cometDataPath, "--log_level", serviceLogLevel}, - ReadyCheck: &cometReadinessCheck, - } - cometRunner := processrunner.NewProcessRunner(ctx, cometOpts) - - // composer - composerOpts := processrunner.NewProcessRunnerOpts{ - Title: "Composer", - BinPath: composerPath, - Env: environment, - Args: nil, - ReadyCheck: nil, - } - compRunner := processrunner.NewProcessRunner(ctx, composerOpts) - - // conductor - conductorOpts := processrunner.NewProcessRunnerOpts{ - Title: "Conductor", - BinPath: conductorPath, - Env: environment, - Args: nil, - ReadyCheck: nil, - } - condRunner := processrunner.NewProcessRunner(ctx, conductorOpts) - - // shouldStart acts as a control channel to start this first process - shouldStart := make(chan bool) - close(shouldStart) - err := seqRunner.Start(ctx, shouldStart) - if err != nil { - log.WithError(err).Error("Error running sequencer") - } - err = cometRunner.Start(ctx, seqRunner.GetDidStart()) - if err != nil { - log.WithError(err).Error("Error running cometbft") - } - err = compRunner.Start(ctx, cometRunner.GetDidStart()) - if err != nil { - log.WithError(err).Error("Error running composer") - } - err = condRunner.Start(ctx, compRunner.GetDidStart()) - if err != nil { - log.WithError(err).Error("Error running conductor") - } - - runners = []processrunner.ProcessRunner{seqRunner, cometRunner, compRunner, condRunner} - - case "dusk", "dawn", "mainnet": - var networkOverrides []string - if network == "dusk" { - networkOverrides = networkConfigs.Dusk.GetEndpointOverrides(baseConfig) - } else if network == "dawn" { - networkOverrides = networkConfigs.Dawn.GetEndpointOverrides(baseConfig) - } else { - networkOverrides = networkConfigs.Mainnet.GetEndpointOverrides(baseConfig) - } - serviceLogLevelOverrides := config.GetServiceLogLevelOverrides(serviceLogLevel) - environment := config.MergeConfigs(baseConfigEnvVars, networkOverrides, serviceLogLevelOverrides) - config.LogEnv(environment) - - log.Debug("Running remote sequencer") - binDir := filepath.Join(astriaDir, instance, config.BinariesDirName) - - // get the binary paths - conductorPath := getFlagPath(c, "conductor-path", "conductor", filepath.Join(binDir, "astria-conductor")) - composerPath := getFlagPath(c, "composer-path", "composer", filepath.Join(binDir, "astria-composer")) - - // composer - composerOpts := processrunner.NewProcessRunnerOpts{ - Title: "Composer", - BinPath: composerPath, - Env: environment, - Args: nil, - ReadyCheck: nil, + serviceLogLevelOverrides := config.GetServiceLogLevelOverrides(serviceLogLevel) + + networkOverrides := networkConfigs.Configs[network].GetEndpointOverrides(baseConfig) + + environment := config.MergeConfigs(baseConfigEnvVars, networkOverrides, serviceLogLevelOverrides) + config.LogEnv(environment) + + // known services + var seqRunner processrunner.ProcessRunner + var cometRunner processrunner.ProcessRunner + var compRunner processrunner.ProcessRunner + var condRunner processrunner.ProcessRunner + // generic services + var genericRunners []processrunner.ProcessRunner + + // load the services from the networks config and build the process runners + // for each service, with special treatment for "known" services like + // sequencer, composer, conductor, and cometbft + for label, service := range networkConfigs.Configs[network].Services { + switch label { + case "sequencer": + sequencerPath := getFlagPath(c, "sequencer-path", "sequencer", service.LocalPath) + seqRCOpts := processrunner.ReadyCheckerOpts{ + CallBackName: "Sequencer gRPC server is OK", + Callback: getSequencerOKCallback(environment), + RetryCount: 10, + RetryInterval: 100 * time.Millisecond, + HaltIfFailed: false, + } + seqReadinessCheck := processrunner.NewReadyChecker(seqRCOpts) + seqOpts := processrunner.NewProcessRunnerOpts{ + Title: "Sequencer", + BinPath: sequencerPath, + Env: environment, + Args: nil, + ReadyCheck: &seqReadinessCheck, + } + seqRunner = processrunner.NewProcessRunner(ctx, seqOpts) + case "composer": + composerPath := getFlagPath(c, "composer-path", "composer", service.LocalPath) + composerOpts := processrunner.NewProcessRunnerOpts{ + Title: "Composer", + BinPath: composerPath, + Env: environment, + Args: nil, + ReadyCheck: nil, + } + compRunner = processrunner.NewProcessRunner(ctx, composerOpts) + case "conductor": + conductorPath := getFlagPath(c, "conductor-path", "conductor", service.LocalPath) + conductorOpts := processrunner.NewProcessRunnerOpts{ + Title: "Conductor", + BinPath: conductorPath, + Env: environment, + Args: nil, + ReadyCheck: nil, + } + condRunner = processrunner.NewProcessRunner(ctx, conductorOpts) + case "cometbft": + cometbftPath := getFlagPath(c, "cometbft-path", "cometbft", service.LocalPath) + cometRCOpts := processrunner.ReadyCheckerOpts{ + CallBackName: "CometBFT rpc server is OK", + Callback: getCometbftOKCallback(environment), + RetryCount: 10, + RetryInterval: 100 * time.Millisecond, + HaltIfFailed: false, + } + cometReadinessCheck := processrunner.NewReadyChecker(cometRCOpts) + dataDir := filepath.Join(astriaDir, instance, config.DataDirName) + cometDataPath := filepath.Join(dataDir, ".cometbft") + cometOpts := processrunner.NewProcessRunnerOpts{ + Title: "Comet BFT", + BinPath: cometbftPath, + Env: environment, + Args: []string{"node", "--home", cometDataPath, "--log_level", serviceLogLevel}, + ReadyCheck: &cometReadinessCheck, + } + cometRunner = processrunner.NewProcessRunner(ctx, cometOpts) + default: + genericOpts := processrunner.NewProcessRunnerOpts{ + Title: service.Name, + BinPath: service.LocalPath, + Env: environment, + Args: nil, // TODO: implement generic args? + ReadyCheck: nil, + } + genericRunner := processrunner.NewProcessRunner(ctx, genericOpts) + genericRunners = append(genericRunners, genericRunner) } - compRunner := processrunner.NewProcessRunner(ctx, composerOpts) - - // conductor - conductorOpts := processrunner.NewProcessRunnerOpts{ - Title: "Conductor", - BinPath: conductorPath, - Env: environment, - Args: nil, - ReadyCheck: nil, - } - condRunner := processrunner.NewProcessRunner(ctx, conductorOpts) + } - // shouldStart acts as a control channel to start this first process - shouldStart := make(chan bool) - close(shouldStart) - err := compRunner.Start(ctx, shouldStart) - if err != nil { - log.WithError(err).Error("Error running composer") - } - err = condRunner.Start(ctx, compRunner.GetDidStart()) - if err != nil { - log.WithError(err).Error("Error running conductor") - } - runners = []processrunner.ProcessRunner{compRunner, condRunner} + // set the order of the services to start + allRunners := append([]processrunner.ProcessRunner{seqRunner, cometRunner, compRunner, condRunner}, genericRunners...) - default: - log.Fatalf("Invalid network provided: %s", network) - log.Fatalf("Valid networks are: local, dusk, dawn, mainnet") - panic("Invalid network provided") + runners, err := startProcessInOrder(ctx, allRunners...) + if err != nil { + log.WithError(err).Error("Error starting services") } // create and start ui app @@ -324,3 +263,55 @@ func getCometbftOKCallback(config []string) func() bool { } } } + +// startProcessInOrder starts the ProcessRunners in order they are provided, and +// returns an array of all successfully started services. It will skip any +// ProcessRunners that are nil. It will return an error if any of the +// ProcessRunners fail to start. +func startProcessInOrder(ctx context.Context, runners ...processrunner.ProcessRunner) ([]processrunner.ProcessRunner, error) { + if len(runners) < 1 { + return nil, fmt.Errorf("no runners provided. Nothing to start") + } + + var returnRunners []processrunner.ProcessRunner + + previousRunner := runners[0] + remainingRunners := runners[1:] + + var err error + + // start the first runner + shouldStart := make(chan bool) + close(shouldStart) + if previousRunner != nil { + err = previousRunner.Start(ctx, shouldStart) + if err != nil { + log.WithError(err).Errorf("Error running %s", previousRunner.GetTitle()) + return nil, err + } + returnRunners = append(returnRunners, previousRunner) + } + + if len(remainingRunners) == 0 { + return returnRunners, nil + } + + // start the remaining runners + for _, runner := range remainingRunners { + if runner != nil { + if previousRunner == nil { + err = runner.Start(ctx, shouldStart) + } else { + err = runner.Start(ctx, previousRunner.GetDidStart()) + } + if err != nil { + log.WithError(err).Errorf("Error running %s", runner.GetTitle()) + return nil, err + } + returnRunners = append(returnRunners, runner) + previousRunner = runner + } + } + + return returnRunners, nil +} diff --git a/modules/cli/cmd/devrunner/version.go b/modules/cli/cmd/devrunner/version.go index 76edaf45..d94fd358 100644 --- a/modules/cli/cmd/devrunner/version.go +++ b/modules/cli/cmd/devrunner/version.go @@ -2,7 +2,9 @@ package devrunner import ( "fmt" + "path/filepath" + "github.com/astriaorg/astria-cli-go/modules/cli/cmd" "github.com/astriaorg/astria-cli-go/modules/cli/cmd/devrunner/config" "github.com/spf13/cobra" ) @@ -16,13 +18,37 @@ var VersionCmd = &cobra.Command{ } func seqVersionCmdHandler(c *cobra.Command, _ []string) { - fmt.Println("Default Service Versions:") - fmt.Println("cometbft: ", "v"+config.CometbftVersion) - fmt.Println("astria-sequencer:", "v"+config.AstriaSequencerVersion) - fmt.Println("astria-composer: ", "v"+config.AstriaComposerVersion) - fmt.Println("astria-conductor:", "v"+config.AstriaConductorVersion) + flagHandler := cmd.CreateCliFlagHandler(c, cmd.EnvPrefix) + + homeDir := cmd.GetUserHomeDirOrPanic() + astriaDir := filepath.Join(homeDir, ".astria") + instance := flagHandler.GetValue("instance") + config.IsInstanceNameValidOrPanic(instance) + + network := flagHandler.GetValue("network") + + networksConfigPath := filepath.Join(astriaDir, instance, config.DefaultNetworksConfigName) + networkConfigs := config.LoadNetworkConfigsOrPanic(networksConfigPath) + + networkOverrides := networkConfigs.Configs[network] + + fmt.Println("Preset Service Versions:") + // get longest service name for padding + longestServiceName := 0 + for _, service := range networkOverrides.Services { + if len(service.Name) > longestServiceName { + longestServiceName = len(service.Name) + } + } + // print service versions + for _, service := range networkOverrides.Services { + fmt.Printf("%-*s: %s\n", longestServiceName, service.Name, service.Version) + } } func init() { devCmd.AddCommand(VersionCmd) + + flagHandler := cmd.CreateCliFlagHandler(VersionCmd, cmd.EnvPrefix) + flagHandler.BindStringFlag("network", config.DefaultTargetNetwork, "Select the network to print the service versions for.") } diff --git a/modules/cli/cmd/helpers.go b/modules/cli/cmd/helpers.go index c324b81c..65cc6265 100644 --- a/modules/cli/cmd/helpers.go +++ b/modules/cli/cmd/helpers.go @@ -34,3 +34,13 @@ func GetFieldValueByTag(obj interface{}, tagName, tagValue string) (reflect.Valu } return reflect.Value{}, false } + +// GetUserHomeDirOrPanic returns the user's home directory or panics if it cannot be found. +func GetUserHomeDirOrPanic() string { + homeDir, err := os.UserHomeDir() + if err != nil { + log.WithError(err).Error("error getting home dir") + panic(err) + } + return homeDir +} diff --git a/modules/cli/cmd/sequencer/config.go b/modules/cli/cmd/sequencer/config.go index 99172e11..376e74b6 100644 --- a/modules/cli/cmd/sequencer/config.go +++ b/modules/cli/cmd/sequencer/config.go @@ -82,12 +82,8 @@ func LoadSequencerNetworkConfigsOrPanic(path string) SequencerNetworkConfigs { // networks configuration file. The file is located in the user's home directory // in the default Astria config directory (~/.astria/). func BuildSequencerNetworkConfigsFilepath() string { - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - return filepath.Join(homePath, DefaultConfigDirName, DefaultSequencerNetworksConfigFilename) + homeDir := cmd.GetUserHomeDirOrPanic() + return filepath.Join(homeDir, DefaultConfigDirName, DefaultSequencerNetworksConfigFilename) } // CreateSequencerNetworkConfigs creates a sequencer networks configuration file at the diff --git a/modules/cli/cmd/sequencer/createaccount.go b/modules/cli/cmd/sequencer/createaccount.go index 13cc98f5..cf2d00e1 100644 --- a/modules/cli/cmd/sequencer/createaccount.go +++ b/modules/cli/cmd/sequencer/createaccount.go @@ -1,7 +1,6 @@ package sequencer import ( - "os" "path/filepath" "github.com/astriaorg/astria-cli-go/modules/cli/cmd" @@ -66,12 +65,8 @@ func createaccountCmdHandler(c *cobra.Command, _ []string) { log.WithError(err).Error("Error storing private key") panic(err) } - homePath, err := os.UserHomeDir() - if err != nil { - log.WithError(err).Error("Error getting home dir") - panic(err) - } - astriaDir := filepath.Join(homePath, ".astria") + homeDir := cmd.GetUserHomeDirOrPanic() + astriaDir := filepath.Join(homeDir, ".astria") keydir := filepath.Join(astriaDir, "keyfiles") cmd.CreateDirOrPanic(keydir)