From 805078ef13a7e3d567c8726538842a5647d56aa0 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 15:04:21 +0200 Subject: [PATCH 01/13] refactor: simplify config handling --- node/node_test.go | 6 +- pkg/cmd/init.go | 41 ++----- pkg/cmd/init_test.go | 4 +- pkg/cmd/run_node.go | 19 ++-- pkg/cmd/run_node_test.go | 25 +++-- pkg/config/addr.go | 4 +- pkg/config/config.go | 199 ++++++++++++++++------------------ pkg/config/config_test.go | 71 ++++++------ pkg/config/defaults.go | 27 +++-- pkg/config/instrumentation.go | 8 +- pkg/config/yaml.go | 199 +++++----------------------------- pkg/config/yaml_test.go | 162 +++------------------------ pkg/p2p/client.go | 4 +- 13 files changed, 225 insertions(+), 544 deletions(-) diff --git a/node/node_test.go b/node/node_test.go index 8af9b8a65d..78c326e5b5 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -58,7 +58,7 @@ func createTestComponents(t *testing.T) (coreexecutor.Executor, coresequencer.Se PrivKey: genesisValidatorKey, PubKey: genesisValidatorKey.GetPublic(), } - p2pClient, err := p2p.NewClient(rollkitconfig.DefaultNodeConfig, genesis.ChainID, nodeKey, dssync.MutexWrap(datastore.NewMapDatastore()), log.NewNopLogger(), p2p.NopMetrics()) + p2pClient, err := p2p.NewClient(rollkitconfig.DefaultConfig, genesis.ChainID, nodeKey, dssync.MutexWrap(datastore.NewMapDatastore()), log.NewNopLogger(), p2p.NopMetrics()) require.NoError(t, err) require.NotNil(t, p2pClient) ds := dssync.MutexWrap(datastore.NewMapDatastore()) @@ -238,8 +238,8 @@ func newTestNode(ctx context.Context, t *testing.T, nodeType NodeType, chainID s func TestNewNode(t *testing.T) { ctx := context.Background() chainID := "TestNewNode" - //ln := initAndStartNodeWithCleanup(ctx, t, Light, chainID) - //require.IsType(t, new(LightNode), ln) + // ln := initAndStartNodeWithCleanup(ctx, t, Light, chainID) + // require.IsType(t, new(LightNode), ln) fn := initAndStartNodeWithCleanup(ctx, t, Full, chainID) require.IsType(t, new(FullNode), fn) } diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index fc621caeb3..ace9b7caaf 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -16,25 +16,10 @@ import ( "github.com/rollkit/rollkit/pkg/signer/file" ) -// ValidateHomePath checks if the home path is valid and not already initialized -func ValidateHomePath(homePath string) error { - if homePath == "" { - return fmt.Errorf("home path is required") - } - - configFilePath := filepath.Join(homePath, rollconf.RollkitConfigYaml) - if _, err := os.Stat(configFilePath); err == nil { - return fmt.Errorf("%s file already exists in the specified directory", rollconf.RollkitConfigYaml) - } - - return nil -} - // InitializeConfig creates and initializes the configuration with default values func InitializeConfig(homePath string, aggregator bool) rollconf.Config { - config := rollconf.DefaultNodeConfig + config := rollconf.DefaultConfig config.RootDir = homePath - config.ConfigDir = homePath + "/config" config.Node.Aggregator = aggregator return config } @@ -47,7 +32,7 @@ func InitializeSigner(config *rollconf.Config, homePath string, passphrase strin } signerDir := filepath.Join(homePath, "config") - if err := os.MkdirAll(signerDir, 0750); err != nil { + if err := os.MkdirAll(signerDir, 0o750); err != nil { return nil, fmt.Errorf("failed to create signer directory: %w", err) } @@ -106,7 +91,7 @@ func InitializeGenesis(homePath string, chainID string, initialHeight uint64, pr // If os.IsNotExist(err) is true, the file doesn't exist, so we proceed. // Create the config directory if it doesn't exist (needed before saving genesis) - if err := os.MkdirAll(configDir, 0750); err != nil { + if err := os.MkdirAll(configDir, 0o750); err != nil { return fmt.Errorf("error creating config directory: %w", err) } @@ -131,29 +116,23 @@ func InitializeGenesis(homePath string, chainID string, initialHeight uint64, pr // InitCmd initializes a new rollkit.yaml file in the current directory var InitCmd = &cobra.Command{ Use: "init", - Short: fmt.Sprintf("Initialize a new %s file", rollconf.RollkitConfigYaml), - Long: fmt.Sprintf("This command initializes a new %s file in the specified directory (or current directory if not specified).", rollconf.RollkitConfigYaml), + Short: fmt.Sprintf("Initialize rollkit config"), + Long: fmt.Sprintf("This command initializes a new %s file in the specified directory (or current directory if not specified).", rollconf.ConfigName), RunE: func(cmd *cobra.Command, args []string) error { homePath, err := cmd.Flags().GetString(rollconf.FlagRootDir) if err != nil { return fmt.Errorf("error reading home flag: %w", err) } - if err := ValidateHomePath(homePath); err != nil { - return err - } - - // Make sure the home directory exists - if err := os.MkdirAll(homePath, 0750); err != nil { - return fmt.Errorf("error creating directory %s: %w", homePath, err) - } - aggregator, err := cmd.Flags().GetBool(rollconf.FlagAggregator) if err != nil { return fmt.Errorf("error reading aggregator flag: %w", err) } config := InitializeConfig(homePath, aggregator) + if err := config.Validate(); err != nil { + return fmt.Errorf("error validating config: %w", err) + } passphrase, err := cmd.Flags().GetString(rollconf.FlagSignerPassphrase) if err != nil { @@ -165,7 +144,7 @@ var InitCmd = &cobra.Command{ return err } - if err := rollconf.WriteYamlConfig(config); err != nil { + if err := config.SaveAsYaml(); err != nil { return fmt.Errorf("error writing rollkit.yaml file: %w", err) } @@ -187,7 +166,7 @@ var InitCmd = &cobra.Command{ return fmt.Errorf("error initializing genesis file: %w", err) } - fmt.Printf("Initialized %s file in %s\n", rollconf.RollkitConfigYaml, homePath) + fmt.Printf("Initialized %s file in %s\n", rollconf.ConfigName, homePath) return nil }, } diff --git a/pkg/cmd/init_test.go b/pkg/cmd/init_test.go index fc1eb2ea17..ca4f34f443 100644 --- a/pkg/cmd/init_test.go +++ b/pkg/cmd/init_test.go @@ -27,7 +27,7 @@ func TestInitCommand(t *testing.T) { require.NoError(t, os.Chdir(dir)) // Remove any existing rollkit.yaml files in the test directory - configPath := filepath.Join(dir, "config", rollconf.RollkitConfigYaml) + configPath := filepath.Join(dir, "config", rollconf.ConfigName) _ = os.Remove(configPath) // Ignore error if file doesn't exist // Create a new test-specific command @@ -55,7 +55,7 @@ func TestInitCommand(t *testing.T) { require.NoError(t, err) // Verify the config can be read - _, err = rollconf.ReadYaml(filepath.Join(dir, "config")) + _, err = rollconf.Load(cmd) require.NoError(t, err) // Read the file content directly to verify the YAML structure diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index eaf4d8f802..8c32c2bce3 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -19,6 +19,7 @@ import ( coreexecutor "github.com/rollkit/rollkit/core/execution" coresequencer "github.com/rollkit/rollkit/core/sequencer" "github.com/rollkit/rollkit/node" + "github.com/rollkit/rollkit/pkg/config" rollconf "github.com/rollkit/rollkit/pkg/config" genesispkg "github.com/rollkit/rollkit/pkg/genesis" "github.com/rollkit/rollkit/pkg/p2p" @@ -27,18 +28,15 @@ import ( "github.com/rollkit/rollkit/pkg/signer/file" ) -func ParseConfig(cmd *cobra.Command, home string) (rollconf.Config, error) { - // Load configuration with the correct order of precedence: - // DefaultNodeConfig -> Yaml -> Flags - var err error - nodeConfig, err := rollconf.LoadNodeConfig(cmd, home) +// ParseConfig is an helpers that loads the node configuration and validates it. +func ParseConfig(cmd *cobra.Command) (rollconf.Config, error) { + nodeConfig, err := rollconf.Load(cmd) if err != nil { return rollconf.Config{}, fmt.Errorf("failed to load node config: %w", err) } - // Validate the root directory - if err := rollconf.EnsureRoot(nodeConfig.RootDir); err != nil { - return rollconf.Config{}, fmt.Errorf("failed to ensure root directory: %w", err) + if err := nodeConfig.Validate(); err != nil { + return rollconf.Config{}, fmt.Errorf("failed to validate node config: %w", err) } return nodeConfig, nil @@ -100,11 +98,10 @@ func StartNode( datastore datastore.Batching, nodeConfig rollconf.Config, ) error { - ctx, cancel := context.WithCancel(cmd.Context()) defer cancel() - //create a new remote signer + // create a new remote signer var signer signer.Signer if nodeConfig.Signer.SignerType == "file" && nodeConfig.Node.Aggregator { passphrase, err := cmd.Flags().GetString(rollconf.FlagSignerPassphrase) @@ -124,7 +121,7 @@ func StartNode( metrics := node.DefaultMetricsProvider(rollconf.DefaultInstrumentationConfig()) - genesisPath := filepath.Join(nodeConfig.ConfigDir, "genesis.json") + genesisPath := filepath.Join(config.AppConfigDir, "genesis.json") genesis, err := genesispkg.LoadGenesis(genesisPath) if err != nil { return fmt.Errorf("failed to load genesis: %w", err) diff --git a/pkg/cmd/run_node_test.go b/pkg/cmd/run_node_test.go index a8b57ea6df..19de8eed78 100644 --- a/pkg/cmd/run_node_test.go +++ b/pkg/cmd/run_node_test.go @@ -88,19 +88,16 @@ func TestParseFlags(t *testing.T) { t.Fatalf("Error: %v", err) } - nodeConfig := rollconf.DefaultNodeConfig + nodeConfig := rollconf.DefaultConfig nodeConfig.RootDir = t.TempDir() newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) - - // Register root flags to be able to use --home flag - rollconf.AddGlobalFlags(newRunNodeCmd, "testapp") - + newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := newRunNodeCmd.ParseFlags(args); err != nil { t.Errorf("Error: %v", err) } - nodeConfig, err = ParseConfig(newRunNodeCmd, "custom/root/dir") + nodeConfig, err = ParseConfig(newRunNodeCmd) if err != nil { t.Errorf("Error: %v", err) } @@ -175,16 +172,17 @@ func TestAggregatorFlagInvariants(t *testing.T) { t.Fatalf("Error: %v", err) } - nodeConfig := rollconf.DefaultNodeConfig + nodeConfig := rollconf.DefaultConfig nodeConfig.RootDir = t.TempDir() newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) + newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := newRunNodeCmd.ParseFlags(args); err != nil { t.Errorf("Error: %v", err) } - nodeConfig, err = ParseConfig(newRunNodeCmd, "custom/root/dir") + nodeConfig, err = ParseConfig(newRunNodeCmd) if err != nil { t.Errorf("Error: %v", err) } @@ -207,15 +205,16 @@ func TestDefaultAggregatorValue(t *testing.T) { t.Fatalf("Error: %v", err) } - nodeConfig := rollconf.DefaultNodeConfig + nodeConfig := rollconf.DefaultConfig newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) + newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := newRunNodeCmd.ParseFlags(args); err != nil { t.Errorf("Error parsing flags: %v", err) } - nodeConfig, err = ParseConfig(newRunNodeCmd, "custom/root/dir") + nodeConfig, err = ParseConfig(newRunNodeCmd) if err != nil { t.Errorf("Error parsing config: %v", err) } @@ -227,7 +226,7 @@ func TestDefaultAggregatorValue(t *testing.T) { // TestCentralizedAddresses verifies that when centralized service flags are provided, // the configuration fields in nodeConfig are updated accordingly, ensuring that mocks are skipped. func TestCentralizedAddresses(t *testing.T) { - nodeConfig := rollconf.DefaultNodeConfig + nodeConfig := rollconf.DefaultConfig args := []string{ "start", @@ -245,11 +244,12 @@ func TestCentralizedAddresses(t *testing.T) { } cmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) + cmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := cmd.ParseFlags(args); err != nil { t.Fatalf("ParseFlags error: %v", err) } - nodeConfig, err = ParseConfig(cmd, "custom/root/dir") + nodeConfig, err = ParseConfig(cmd) if err != nil { t.Fatalf("parseConfig error: %v", err) } @@ -301,6 +301,7 @@ func newRunNodeCmd( // Add Rollkit flags rollconf.AddFlags(cmd) + rollconf.AddGlobalFlags(cmd, "testapp") return cmd } diff --git a/pkg/config/addr.go b/pkg/config/addr.go index 4c6bebd16e..c8f45fb177 100644 --- a/pkg/config/addr.go +++ b/pkg/config/addr.go @@ -7,9 +7,7 @@ import ( "github.com/multiformats/go-multiaddr" ) -var ( - errInvalidAddress = errors.New("invalid address format, expected [protocol://][@]:") -) +var errInvalidAddress = errors.New("invalid address format, expected [protocol://][@]:") // TranslateAddresses updates conf by changing Cosmos-style addresses to Multiaddr format. func TranslateAddresses(conf *Config) error { diff --git a/pkg/config/config.go b/pkg/config/config.go index b4c9d5d4ac..610fc4f66f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,15 +1,15 @@ package config import ( - "encoding/json" "errors" "fmt" + "os" + "path" "path/filepath" "reflect" "strings" "time" - "github.com/hashicorp/go-multierror" "github.com/mitchellh/mapstructure" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -23,8 +23,6 @@ const ( FlagRootDir = "home" // FlagDBPath is a flag for specifying the database path FlagDBPath = "rollkit.db_path" - // FlagChainConfigDir is a flag for specifying the chain config directory - FlagChainConfigDir = "config_dir" // FlagChainID is a flag for specifying the chain ID FlagChainID = "chain_id" @@ -124,31 +122,12 @@ const ( FlagRPCPort = "rollkit.rpc.port" ) -// DurationWrapper is a wrapper for time.Duration that implements encoding.TextMarshaler and encoding.TextUnmarshaler -// needed for YAML marshalling/unmarshalling especially for time.Duration -type DurationWrapper struct { - time.Duration -} - -// MarshalText implements encoding.TextMarshaler to format the duration as text -func (d DurationWrapper) MarshalText() ([]byte, error) { - return []byte(d.String()), nil -} - -// UnmarshalText implements encoding.TextUnmarshaler to parse the duration from text -func (d *DurationWrapper) UnmarshalText(text []byte) error { - var err error - d.Duration, err = time.ParseDuration(string(text)) - return err -} - // Config stores Rollkit configuration. type Config struct { // Base configuration - RootDir string `mapstructure:"-" yaml:"-" comment:"Root directory where rollkit files are located"` - DBPath string `mapstructure:"db_path" yaml:"db_path" comment:"Path inside the root directory where the database is located"` - ConfigDir string `mapstructure:"config_dir" yaml:"config_dir" comment:"Directory containing the rollup chain configuration"` - ChainID string `mapstructure:"chain_id" yaml:"chain_id" comment:"Chain ID for the rollup"` + RootDir string `mapstructure:"-" yaml:"-" comment:"Root directory where rollkit files are located"` + DBPath string `mapstructure:"db_path" yaml:"db_path" comment:"Path inside the root directory where the database is located"` + ChainID string `mapstructure:"chain_id" yaml:"chain_id" comment:"Chain ID for the rollup"` // P2P configuration P2P P2PConfig `mapstructure:"p2p" yaml:"p2p"` @@ -226,32 +205,44 @@ type SignerConfig struct { SignerPath string `mapstructure:"signer_path" yaml:"signer_path" comment:"Path to the signer file or address"` } -// AddGlobalFlags registers the basic configuration flags that are common across applications -// This includes logging configuration and root directory settings -func AddGlobalFlags(cmd *cobra.Command, appName string) { - cmd.PersistentFlags().String(FlagLogLevel, DefaultNodeConfig.Log.Level, "Set the log level (debug, info, warn, error)") - cmd.PersistentFlags().String(FlagLogFormat, DefaultNodeConfig.Log.Format, "Set the log format (text, json)") - cmd.PersistentFlags().Bool(FlagLogTrace, DefaultNodeConfig.Log.Trace, "Enable stack traces in error logs") - cmd.PersistentFlags().String(FlagRootDir, DefaultRootDirWithName(appName), "Root directory for application data") -} - // RPCConfig contains all RPC server configuration parameters type RPCConfig struct { Address string `mapstructure:"address" yaml:"address" comment:"Address to bind the RPC server to (host). Default: tcp://0.0.0.0"` Port uint16 `mapstructure:"port" yaml:"port" comment:"Port to bind the RPC server to. Default: 26657"` } +// Validate ensures that the root directory exists. +// It creates the directory if it does not exist. +func (c *Config) Validate() error { + if c.RootDir == "" { + return fmt.Errorf("root directory cannot be empty") + } + + if err := os.MkdirAll(c.RootDir, 0o750); err != nil { + return fmt.Errorf("could not create directory %q: %w", c.RootDir, err) + } + + return nil +} + +// AddGlobalFlags registers the basic configuration flags that are common across applications. +// This includes logging configuration and root directory settings. +// It should be used in apps that do not already define their logger and home flag. +func AddGlobalFlags(cmd *cobra.Command, defaultHome string) { + cmd.PersistentFlags().String(FlagLogLevel, DefaultConfig.Log.Level, "Set the log level (debug, info, warn, error)") + cmd.PersistentFlags().String(FlagLogFormat, DefaultConfig.Log.Format, "Set the log format (text, json)") + cmd.PersistentFlags().Bool(FlagLogTrace, DefaultConfig.Log.Trace, "Enable stack traces in error logs") + cmd.PersistentFlags().String(FlagRootDir, DefaultRootDir, "Root directory for application data") +} + // AddFlags adds Rollkit specific configuration options to cobra Command. func AddFlags(cmd *cobra.Command) { - def := DefaultNodeConfig - - // Add CI flag for testing - cmd.Flags().Bool("ci", false, "run node for ci testing") + def := DefaultConfig // Add base flags cmd.Flags().String(FlagDBPath, def.DBPath, "path for the node database") - cmd.Flags().String(FlagChainConfigDir, def.ConfigDir, "directory containing chain configuration files") cmd.Flags().String(FlagChainID, def.ChainID, "chain ID") + // Node configuration flags cmd.Flags().BoolVar(&def.Node.Aggregator, FlagAggregator, def.Node.Aggregator, "run node in aggregator mode") cmd.Flags().Bool(FlagLight, def.Node.Light, "run light client") @@ -299,68 +290,44 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().String(FlagSignerPassphrase, "", "passphrase for the signer (required for file signer and if aggregator is enabled)") } -// LoadNodeConfig loads the node configuration in the following order of precedence: +// Load loads the node configuration in the following order of precedence: // 1. DefaultNodeConfig() (lowest priority) // 2. YAML configuration file // 3. Command line flags (highest priority) -func LoadNodeConfig(cmd *cobra.Command, home string) (Config, error) { - // Create a new Viper instance to avoid conflicts with any global Viper - v := viper.New() - - // 1. Start with default configuration generated by the function - // and set defaults in Viper - config := DefaultNodeConfig - setDefaultsInViper(v, config) - - // Get the RootDir from flags *first* to ensure correct search paths - // If the flag is not set, it will use the default value already in 'config' - rootDirFromFlag, _ := cmd.Flags().GetString(FlagRootDir) // Ignore error, default is fine if flag not present - if rootDirFromFlag != "" { - config.RootDir = rootDirFromFlag // Update our config struct temporarily for path setting +func Load(cmd *cobra.Command) (Config, error) { + home, _ := cmd.Flags().GetString(FlagRootDir) + if home == "" { + home = DefaultRootDir } - // 2. Try to load YAML configuration from various locations - // First try using the current directory - v.SetConfigName(ConfigBaseName) // e.g., "rollkit" - v.SetConfigType(ConfigExtension) // e.g., "yaml" - - // Check if RootDir is determined (either default or from flag) - if config.RootDir != "" { - // Search directly in the determined root directory first - v.AddConfigPath(home) - // Then search in the default config subdirectory within that root directory - v.AddConfigPath(filepath.Join(home, config.ConfigDir)) // DefaultConfigDir is likely "config" + v := viper.New() + v.SetConfigName(ConfigDirName) + v.SetConfigType(ConfigExtension) + v.AddConfigPath(filepath.Join(home, AppConfigDir)) + v.BindPFlags(cmd.Flags()) + v.BindPFlags(cmd.PersistentFlags()) + v.AutomaticEnv() + + cfg := DefaultConfig + cfg.RootDir = filepath.Dir(v.ConfigFileUsed()) + + // get the executable name + executableName, err := os.Executable() + if err != nil { + return cfg, err } - // Try to read the config file - if err := v.ReadInConfig(); err != nil { - // If it's not a "file not found" error, return the error - var configFileNotFound viper.ConfigFileNotFoundError - if !errors.As(err, &configFileNotFound) { - return config, fmt.Errorf("error reading YAML configuration: %w", err) - } - // Otherwise, just continue with defaults - } else { - // Config file found, log it - fmt.Printf("Using config file: %s\n", v.ConfigFileUsed()) + if err := bindFlags(path.Base(executableName), cmd, v); err != nil { + return cfg, err } - // 3. Bind command line flags - var flagErrs error - cmd.Flags().VisitAll(func(f *pflag.Flag) { - // Always trim the rollkit prefix if it exists - flagName := strings.TrimPrefix(f.Name, "rollkit.") - if err := v.BindPFlag(flagName, f); err != nil { - flagErrs = multierror.Append(flagErrs, err) - } - }) - if flagErrs != nil { - return config, fmt.Errorf("unable to bind flags: %w", flagErrs) + // Read the configuration file + if err := v.ReadInConfig(); err != nil { + return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed decoding file: %w", err)) } - // 4. Unmarshal everything from Viper into the config struct - // viper.Unmarshal will respect the precedence: defaults < yaml < flags - if err := v.Unmarshal(&config, func(c *mapstructure.DecoderConfig) { + // Unmarshal directly into Config + if err := v.Unmarshal(&cfg, func(c *mapstructure.DecoderConfig) { c.TagName = "mapstructure" c.DecodeHook = mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), @@ -379,26 +346,42 @@ func LoadNodeConfig(cmd *cobra.Command, home string) (Config, error) { }, ) }); err != nil { - return config, fmt.Errorf("unable to decode configuration: %w", err) + return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed unmarshaling config: %w", err)) } - return config, nil + return cfg, nil } -// setDefaultsInViper sets all the default values from NodeConfig into Viper -func setDefaultsInViper(v *viper.Viper, config Config) { - // This function needs to marshal the config to a map and set defaults - // Example implementation sketch (might need refinement based on struct tags): - configMap := make(map[string]interface{}) - data, _ := json.Marshal(config) // Using JSON temporarily, mapstructure might be better - err := json.Unmarshal(data, &configMap) - if err != nil { - fmt.Println("error unmarshalling config", err) - } +func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("bindFlags failed: %v", r) + } + }() - for key, value := range configMap { - v.SetDefault(key, value) - } - // Note: A proper implementation would recursively handle nested structs - // and potentially use mapstructure tags used elsewhere in the loading process. + cmd.Flags().VisitAll(func(f *pflag.Flag) { + // Environment variables can't have dashes in them, so bind them to their equivalent + // keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR + err = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_")))) + if err != nil { + panic(err) + } + + err = v.BindPFlag(f.Name, f) + if err != nil { + panic(err) + } + + // Apply the viper config value to the flag when the flag is not set and + // viper has a value. + if !f.Changed && v.IsSet(f.Name) { + val := v.Get(f.Name) + err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)) + if err != nil { + panic(err) + } + } + }) + + return err } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index e03efb03c9..ec3d268cdd 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -13,13 +13,13 @@ import ( "github.com/stretchr/testify/require" ) -func TestDefaultNodeConfig(t *testing.T) { +func TestDefaultConfig(t *testing.T) { // Test that default config has expected values - def := DefaultNodeConfig + def := DefaultConfig assert.Equal(t, "data", def.DBPath) assert.Equal(t, false, def.Node.Aggregator) assert.Equal(t, false, def.Node.Light) - assert.Equal(t, DefaultNodeConfig.DA.Address, def.DA.Address) + assert.Equal(t, DefaultConfig.DA.Address, def.DA.Address) assert.Equal(t, "", def.DA.AuthToken) assert.Equal(t, float64(-1), def.DA.GasPrice) assert.Equal(t, float64(0), def.DA.GasMultiplier) @@ -51,37 +51,36 @@ func TestAddFlags(t *testing.T) { persistentFlags := cmd.PersistentFlags() // Test specific flags - assertFlagValue(t, flags, FlagDBPath, DefaultNodeConfig.DBPath) - assertFlagValue(t, flags, FlagChainConfigDir, DefaultNodeConfig.ConfigDir) + assertFlagValue(t, flags, FlagDBPath, DefaultConfig.DBPath) // Node flags - assertFlagValue(t, flags, FlagAggregator, DefaultNodeConfig.Node.Aggregator) - assertFlagValue(t, flags, FlagLight, DefaultNodeConfig.Node.Light) - assertFlagValue(t, flags, FlagBlockTime, DefaultNodeConfig.Node.BlockTime.Duration) - assertFlagValue(t, flags, FlagTrustedHash, DefaultNodeConfig.Node.TrustedHash) - assertFlagValue(t, flags, FlagLazyAggregator, DefaultNodeConfig.Node.LazyAggregator) - assertFlagValue(t, flags, FlagMaxPendingBlocks, DefaultNodeConfig.Node.MaxPendingBlocks) - assertFlagValue(t, flags, FlagLazyBlockTime, DefaultNodeConfig.Node.LazyBlockTime.Duration) - assertFlagValue(t, flags, FlagSequencerAddress, DefaultNodeConfig.Node.SequencerAddress) - assertFlagValue(t, flags, FlagSequencerRollupID, DefaultNodeConfig.Node.SequencerRollupID) - assertFlagValue(t, flags, FlagExecutorAddress, DefaultNodeConfig.Node.ExecutorAddress) + assertFlagValue(t, flags, FlagAggregator, DefaultConfig.Node.Aggregator) + assertFlagValue(t, flags, FlagLight, DefaultConfig.Node.Light) + assertFlagValue(t, flags, FlagBlockTime, DefaultConfig.Node.BlockTime.Duration) + assertFlagValue(t, flags, FlagTrustedHash, DefaultConfig.Node.TrustedHash) + assertFlagValue(t, flags, FlagLazyAggregator, DefaultConfig.Node.LazyAggregator) + assertFlagValue(t, flags, FlagMaxPendingBlocks, DefaultConfig.Node.MaxPendingBlocks) + assertFlagValue(t, flags, FlagLazyBlockTime, DefaultConfig.Node.LazyBlockTime.Duration) + assertFlagValue(t, flags, FlagSequencerAddress, DefaultConfig.Node.SequencerAddress) + assertFlagValue(t, flags, FlagSequencerRollupID, DefaultConfig.Node.SequencerRollupID) + assertFlagValue(t, flags, FlagExecutorAddress, DefaultConfig.Node.ExecutorAddress) // DA flags - assertFlagValue(t, flags, FlagDAAddress, DefaultNodeConfig.DA.Address) - assertFlagValue(t, flags, FlagDAAuthToken, DefaultNodeConfig.DA.AuthToken) - assertFlagValue(t, flags, FlagDABlockTime, DefaultNodeConfig.DA.BlockTime.Duration) - assertFlagValue(t, flags, FlagDAGasPrice, DefaultNodeConfig.DA.GasPrice) - assertFlagValue(t, flags, FlagDAGasMultiplier, DefaultNodeConfig.DA.GasMultiplier) - assertFlagValue(t, flags, FlagDAStartHeight, DefaultNodeConfig.DA.StartHeight) - assertFlagValue(t, flags, FlagDANamespace, DefaultNodeConfig.DA.Namespace) - assertFlagValue(t, flags, FlagDASubmitOptions, DefaultNodeConfig.DA.SubmitOptions) - assertFlagValue(t, flags, FlagDAMempoolTTL, DefaultNodeConfig.DA.MempoolTTL) + assertFlagValue(t, flags, FlagDAAddress, DefaultConfig.DA.Address) + assertFlagValue(t, flags, FlagDAAuthToken, DefaultConfig.DA.AuthToken) + assertFlagValue(t, flags, FlagDABlockTime, DefaultConfig.DA.BlockTime.Duration) + assertFlagValue(t, flags, FlagDAGasPrice, DefaultConfig.DA.GasPrice) + assertFlagValue(t, flags, FlagDAGasMultiplier, DefaultConfig.DA.GasMultiplier) + assertFlagValue(t, flags, FlagDAStartHeight, DefaultConfig.DA.StartHeight) + assertFlagValue(t, flags, FlagDANamespace, DefaultConfig.DA.Namespace) + assertFlagValue(t, flags, FlagDASubmitOptions, DefaultConfig.DA.SubmitOptions) + assertFlagValue(t, flags, FlagDAMempoolTTL, DefaultConfig.DA.MempoolTTL) // P2P flags - assertFlagValue(t, flags, FlagP2PListenAddress, DefaultNodeConfig.P2P.ListenAddress) - assertFlagValue(t, flags, FlagP2PSeeds, DefaultNodeConfig.P2P.Seeds) - assertFlagValue(t, flags, FlagP2PBlockedPeers, DefaultNodeConfig.P2P.BlockedPeers) - assertFlagValue(t, flags, FlagP2PAllowedPeers, DefaultNodeConfig.P2P.AllowedPeers) + assertFlagValue(t, flags, FlagP2PListenAddress, DefaultConfig.P2P.ListenAddress) + assertFlagValue(t, flags, FlagP2PSeeds, DefaultConfig.P2P.Seeds) + assertFlagValue(t, flags, FlagP2PBlockedPeers, DefaultConfig.P2P.BlockedPeers) + assertFlagValue(t, flags, FlagP2PAllowedPeers, DefaultConfig.P2P.AllowedPeers) // Instrumentation flags instrDef := DefaultInstrumentationConfig() @@ -92,14 +91,14 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagPprofListenAddr, instrDef.PprofListenAddr) // Logging flags (in persistent flags) - assertFlagValue(t, persistentFlags, FlagLogLevel, DefaultNodeConfig.Log.Level) + assertFlagValue(t, persistentFlags, FlagLogLevel, DefaultConfig.Log.Level) assertFlagValue(t, persistentFlags, FlagLogFormat, "text") assertFlagValue(t, persistentFlags, FlagLogTrace, false) // Signer flags assertFlagValue(t, flags, FlagSignerPassphrase, "") assertFlagValue(t, flags, FlagSignerType, "file") - assertFlagValue(t, flags, FlagSignerPath, DefaultNodeConfig.Signer.SignerPath) + assertFlagValue(t, flags, FlagSignerPath, DefaultConfig.Signer.SignerPath) // Count the number of flags we're explicitly checking expectedFlagCount := 41 // Update this number if you add more flag checks above @@ -122,12 +121,12 @@ func TestAddFlags(t *testing.T) { ) } -func TestLoadNodeConfig(t *testing.T) { +func TestLoad(t *testing.T) { // Create a temporary directory for the test tempDir := t.TempDir() // Create a YAML file in the temporary directory - yamlPath := filepath.Join(tempDir, RollkitConfigYaml) + yamlPath := filepath.Join(tempDir, ConfigName) yamlContent := ` node: aggregator: true @@ -142,7 +141,7 @@ signer: signer_type: "file" signer_path: "something/config" ` - err := os.WriteFile(yamlPath, []byte(yamlContent), 0600) + err := os.WriteFile(yamlPath, []byte(yamlContent), 0o600) require.NoError(t, err) // Change to the temporary directory so the config file can be found @@ -164,9 +163,11 @@ signer: // Create a command with flags cmd := &cobra.Command{Use: "test"} AddFlags(cmd) + AddGlobalFlags(cmd, "test") // Add basic flags first // Set some flags that should override YAML values flagArgs := []string{ + "--home", tempDir, "--rollkit.node.block_time", "10s", "--rollkit.da.address", "http://flag-da:26657", "--rollkit.node.light", "true", // This is not in YAML, should be set from flag @@ -176,7 +177,7 @@ signer: require.NoError(t, err) // Load the configuration - config, err := LoadNodeConfig(cmd, tempDir) + config, err := Load(cmd) require.NoError(t, err) // Verify the order of precedence: @@ -191,7 +192,7 @@ signer: assert.Equal(t, true, config.Node.Light, "Light should be set from flag") // 4. Values not in flags or YAML should remain as default - assert.Equal(t, DefaultNodeConfig.DA.BlockTime.Duration, config.DA.BlockTime.Duration, "DABlockTime should remain as default") + assert.Equal(t, DefaultConfig.DA.BlockTime.Duration, config.DA.BlockTime.Duration, "DABlockTime should remain as default") // 5. Signer values should be set from flags assert.Equal(t, "file", config.Signer.SignerType, "SignerType should be set from flag") diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index e9390ae5ae..e86e0149af 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -6,10 +6,19 @@ import ( "time" ) +const ( + // ConfigDirName is the base name of the rollkit configuration file without extension. + ConfigDirName = "rollkit" + // ConfigExtension is the file extension for the configuration file without the leading dot. + ConfigExtension = "yaml" + // ConfigPath is the filename for the rollkit configuration file. + ConfigName = ConfigDirName + "." + ConfigExtension + // AppConfigDir is the directory name for the app configuration. + AppConfigDir = "config" +) + // DefaultRootDir returns the default root directory for rollkit -func DefaultRootDir() string { - return DefaultRootDirWithName("rollkit") -} +var DefaultRootDir = DefaultRootDirWithName(ConfigDirName) // DefaultRootDirWithName returns the default root directory for an application, // based on the app name and the user's home directory @@ -18,15 +27,15 @@ func DefaultRootDirWithName(appName string) string { if err != nil { return "" } + return filepath.Join(home, "."+appName) } -// DefaultNodeConfig keeps default values of NodeConfig -var DefaultNodeConfig = Config{ - RootDir: DefaultRootDir(), - DBPath: "data", - ConfigDir: "config", - ChainID: "rollkit-test", +// DefaultConfig keeps default values of NodeConfig +var DefaultConfig = Config{ + RootDir: DefaultRootDir, + DBPath: "data", + ChainID: "rollkit-test", P2P: P2PConfig{ ListenAddress: "/ip4/0.0.0.0/tcp/7676", Seeds: "", diff --git a/pkg/config/instrumentation.go b/pkg/config/instrumentation.go index 6beb90f6d5..c4932cfbf6 100644 --- a/pkg/config/instrumentation.go +++ b/pkg/config/instrumentation.go @@ -54,11 +54,6 @@ func DefaultInstrumentationConfig() *InstrumentationConfig { } } -// TestInstrumentationConfig returns a default configuration for test environments. -func TestInstrumentationConfig() *InstrumentationConfig { - return DefaultInstrumentationConfig() -} - // ValidateBasic performs basic validation (checking param bounds, etc.) and // returns an error if any check fails. func (cfg *InstrumentationConfig) ValidateBasic() error { @@ -82,7 +77,8 @@ func (cfg *InstrumentationConfig) IsPprofEnabled() bool { // If PprofListenAddr is empty, it returns the default pprof port ":6060". func (cfg *InstrumentationConfig) GetPprofListenAddr() string { if cfg.PprofListenAddr == "" { - return ":6060" + return DefaultInstrumentationConfig().PprofListenAddr } + return cfg.PprofListenAddr } diff --git a/pkg/config/yaml.go b/pkg/config/yaml.go index d00f59f95e..8ad6556666 100644 --- a/pkg/config/yaml.go +++ b/pkg/config/yaml.go @@ -8,140 +8,45 @@ import ( "time" "github.com/goccy/go-yaml" - "github.com/mitchellh/mapstructure" - "github.com/spf13/viper" ) -// ConfigBaseName is the base name of the rollkit configuration file without extension. -const ConfigBaseName = "rollkit" - -// ConfigExtension is the file extension for the configuration file without the leading dot. -const ConfigExtension = "yaml" - -// RollkitConfigYaml is the filename for the rollkit configuration file. -const RollkitConfigYaml = ConfigBaseName + "." + ConfigExtension - -// ErrReadYaml is the error returned when reading the rollkit.yaml file fails. -var ErrReadYaml = fmt.Errorf("reading %s", RollkitConfigYaml) - -// ReadYaml reads the YAML configuration from the rollkit.yaml file and returns the parsed Config. -// If dir is provided, it will look for the config file in that directory. -func ReadYaml(dir string) (config Config, err error) { - // Configure Viper to search for the configuration file - v := viper.New() - v.SetConfigName(ConfigBaseName) - v.SetConfigType(ConfigExtension) - - if dir != "" { - // If a directory is provided, look for the config file there - v.AddConfigPath(dir) - } else { - // Otherwise, search for the configuration file in the current directory and its parents - startDir, err := os.Getwd() - if err != nil { - err = fmt.Errorf("%w: getting current dir: %w", ErrReadYaml, err) - return config, err - } - - configPath, err := findConfigFile(startDir) - if err != nil { - err = fmt.Errorf("%w: %w", ErrReadYaml, err) - return config, err - } - - v.SetConfigFile(configPath) - } - - // Set default values - config = DefaultNodeConfig - - // Read the configuration file - if err = v.ReadInConfig(); err != nil { - err = fmt.Errorf("%w decoding file: %w", ErrReadYaml, err) - return - } - - // Unmarshal directly into Config - if err = v.Unmarshal(&config, func(c *mapstructure.DecoderConfig) { - c.TagName = "mapstructure" - c.DecodeHook = mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToSliceHookFunc(","), - func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { - if t == reflect.TypeOf(DurationWrapper{}) && f.Kind() == reflect.String { - if str, ok := data.(string); ok { - duration, err := time.ParseDuration(str) - if err != nil { - return nil, err - } - return DurationWrapper{Duration: duration}, nil - } - } - return data, nil - }, - ) - }); err != nil { - err = fmt.Errorf("%w unmarshaling config: %w", ErrReadYaml, err) - return - } - - // Set the root directory - if dir != "" { - config.RootDir = dir - } else { - config.RootDir = filepath.Dir(v.ConfigFileUsed()) - } - - // Add configPath to ConfigDir if it is a relative path - if config.ConfigDir != "" && !filepath.IsAbs(config.ConfigDir) { - config.ConfigDir = filepath.Join(config.RootDir, config.ConfigDir) - } - - return +// DurationWrapper is a wrapper for time.Duration that implements encoding.TextMarshaler and encoding.TextUnmarshaler +// needed for YAML marshalling/unmarshalling especially for time.Duration +type DurationWrapper struct { + time.Duration } -// findConfigFile searches for the rollkit.yaml file starting from the given -// directory and moving up the directory tree. It returns the full path to -// the rollkit.yaml file or an error if it was not found. -func findConfigFile(startDir string) (string, error) { - dir := startDir - for { - configPath := filepath.Join(dir, RollkitConfigYaml) - if _, err := os.Stat(configPath); err == nil { - return configPath, nil - } +// MarshalText implements encoding.TextMarshaler to format the duration as text +func (d DurationWrapper) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} - parentDir := filepath.Dir(dir) - if parentDir == dir { - break - } - dir = parentDir - } - return "", fmt.Errorf("no %s found", RollkitConfigYaml) +// UnmarshalText implements encoding.TextUnmarshaler to parse the duration from text +func (d *DurationWrapper) UnmarshalText(text []byte) error { + var err error + d.Duration, err = time.ParseDuration(string(text)) + return err } -// WriteYamlConfig writes the YAML configuration to the rollkit.yaml file. -// It ensures the directory exists and writes the configuration with proper permissions. -func WriteYamlConfig(config Config) error { - // Configure the output file - configPath := filepath.Join(config.RootDir, "config", RollkitConfigYaml) +// ErrReadYaml is the error returned when reading the rollkit.yaml file fails. +var ErrReadYaml = fmt.Errorf("reading %s", ConfigName) - // Ensure the directory exists - if err := os.MkdirAll(filepath.Dir(configPath), 0750); err != nil { - return err +// SaveAsYaml saves the current configuration to a YAML file. +func (c *Config) SaveAsYaml() error { + configPath := filepath.Join(DefaultConfig.RootDir, AppConfigDir, ConfigName) + if err := os.MkdirAll(filepath.Dir(configPath), 0o750); err != nil { + return fmt.Errorf("could not create directory %q: %w", filepath.Dir(configPath), err) } - // Marshal the config to YAML with comments + // helper function to add comments yamlCommentMap := yaml.CommentMap{} - - // Helper function to add comments addComment := func(path string, comment string) { yamlCommentMap[path] = []*yaml.Comment{ yaml.HeadComment(comment), } } - // Helper function to process struct fields recursively + // helper function to process struct fields recursively var processFields func(t reflect.Type, prefix string) processFields = func(t reflect.Type, prefix string) { for i := 0; i < t.NumField(); i++ { @@ -185,67 +90,13 @@ func WriteYamlConfig(config Config) error { } } - // Process the Config struct + // process structs fields and comments processFields(reflect.TypeOf(Config{}), "") - data, err := yaml.MarshalWithOptions(config, yaml.WithComment(yamlCommentMap)) + data, err := yaml.MarshalWithOptions(c, yaml.WithComment(yamlCommentMap)) if err != nil { return fmt.Errorf("error marshaling YAML data: %w", err) } - // Write the YAML data to the file - if err := os.WriteFile(configPath, data, 0600); err != nil { - return fmt.Errorf("error writing %s file: %w", RollkitConfigYaml, err) - } - - return nil -} - -// EnsureRoot ensures that the root directory exists. -func EnsureRoot(rootDir string) error { - if rootDir == "" { - return fmt.Errorf("root directory cannot be empty") - } - - if err := os.MkdirAll(rootDir, 0750); err != nil { - return fmt.Errorf("could not create directory %q: %w", rootDir, err) - } - - return nil -} - -// CreateInitialConfig creates the initial config file with default values and optional customizations. -// It will: -// 1. Create the root directory if it doesn't exist -// 2. Look for a chain config directory -// 3. Create a config file with default values and any found values -// 4. Apply any customizations provided -func CreateInitialConfig(rootDir string, opts ...func(*Config)) error { - // Ensure root directory exists - if err := os.MkdirAll(rootDir, 0750); err != nil { - return fmt.Errorf("error creating directory %s: %w", rootDir, err) - } - - // Check if config file already exists - configPath := filepath.Join(rootDir, "config", RollkitConfigYaml) - if _, err := os.Stat(configPath); err == nil { - return fmt.Errorf("%s file already exists in %s", RollkitConfigYaml, rootDir) - } - - // Create config with default values - config := DefaultNodeConfig - config.RootDir = rootDir - - // Apply any customizations - for _, opt := range opts { - opt(&config) - } - - // Write the config file - if err := WriteYamlConfig(config); err != nil { - return fmt.Errorf("error writing %s file: %w", RollkitConfigYaml, err) - } - - fmt.Printf("Created %s file in %s\n", RollkitConfigYaml, rootDir) - return nil + return os.WriteFile(configPath, data, 0o600) } diff --git a/pkg/config/yaml_test.go b/pkg/config/yaml_test.go index 2d9cf3a3bd..678c0d4ea4 100644 --- a/pkg/config/yaml_test.go +++ b/pkg/config/yaml_test.go @@ -4,100 +4,11 @@ import ( "os" "path/filepath" "testing" - "time" + "github.com/spf13/cobra" "github.com/stretchr/testify/require" ) -func TestReadYaml(t *testing.T) { - testCases := []struct { - name string - setup func(t *testing.T, dir string) error - validate func(t *testing.T, cfg Config, err error) - }{ - { - name: "reads YAML configuration from file", - setup: func(t *testing.T, dir string) error { - cfg := DefaultNodeConfig - cfg.RootDir = dir - return WriteYamlConfig(cfg) - }, - validate: func(t *testing.T, cfg Config, err error) { - require.NoError(t, err) - require.Equal(t, DefaultNodeConfig.P2P.ListenAddress, cfg.P2P.ListenAddress) - }, - }, - { - name: "loads nodeconfig values from YAML file", - setup: func(t *testing.T, dir string) error { - cfg := DefaultNodeConfig - cfg.RootDir = dir - cfg.Node.Aggregator = true - cfg.Node.Light = true - return WriteYamlConfig(cfg) - }, - validate: func(t *testing.T, cfg Config, err error) { - require.NoError(t, err) - require.True(t, cfg.Node.Aggregator) - require.True(t, cfg.Node.Light) - }, - }, - { - name: "returns error if config file not found", - setup: func(t *testing.T, dir string) error { - return nil - }, - validate: func(t *testing.T, cfg Config, err error) { - require.Error(t, err) - require.Contains(t, err.Error(), "Config File \"rollkit\" Not Found") - }, - }, - { - name: "sets RootDir even if empty yaml", - setup: func(t *testing.T, dir string) error { - // Create empty YAML file - err := os.MkdirAll(filepath.Join(dir, "config"), 0755) - require.NoError(t, err) - return os.WriteFile(filepath.Join(dir, "config", RollkitConfigYaml), []byte(""), 0600) - }, - validate: func(t *testing.T, cfg Config, err error) { - require.NoError(t, err) - require.NotEmpty(t, cfg.RootDir) - }, - }, - { - name: "returns error if config file cannot be decoded", - setup: func(t *testing.T, dir string) error { - // Create invalid YAML file - err := os.MkdirAll(filepath.Join(dir, "config"), 0755) - require.NoError(t, err) - return os.WriteFile(filepath.Join(dir, "config", RollkitConfigYaml), []byte("invalid: yaml: content"), 0600) - }, - validate: func(t *testing.T, cfg Config, err error) { - require.Error(t, err) - require.Contains(t, err.Error(), "decoding file") - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - // Create a temporary directory for each test case - tempDir := t.TempDir() - - // Setup the test case - err := tc.setup(t, tempDir) - require.NoError(t, err) - - // Read the config - cfg, err := ReadYaml(filepath.Join(tempDir, "config")) - - // Validate the result - tc.validate(t, cfg, err) - }) - } -} - func TestYamlConfigOperations(t *testing.T) { testCases := []struct { name string @@ -107,37 +18,33 @@ func TestYamlConfigOperations(t *testing.T) { { name: "Write and read custom config values", setup: func(t *testing.T, dir string) *Config { - cfg := DefaultNodeConfig + cfg := DefaultConfig cfg.RootDir = dir - cfg.P2P.ListenAddress = DefaultNodeConfig.P2P.ListenAddress + cfg.P2P.ListenAddress = DefaultConfig.P2P.ListenAddress cfg.P2P.Seeds = "seed1.example.com:26656,seed2.example.com:26656" - // Write the config file - err := WriteYamlConfig(cfg) - require.NoError(t, err) + require.NoError(t, cfg.SaveAsYaml()) return &cfg }, validate: func(t *testing.T, cfg *Config) { - require.Equal(t, DefaultNodeConfig.P2P.ListenAddress, cfg.P2P.ListenAddress) + require.Equal(t, DefaultConfig.P2P.ListenAddress, cfg.P2P.ListenAddress) require.Equal(t, "seed1.example.com:26656,seed2.example.com:26656", cfg.P2P.Seeds) }, }, { name: "Initialize default config values", setup: func(t *testing.T, dir string) *Config { - cfg := DefaultNodeConfig + cfg := DefaultConfig cfg.RootDir = dir - // Write the config file - err := WriteYamlConfig(cfg) - require.NoError(t, err) + require.NoError(t, cfg.SaveAsYaml()) return &cfg }, validate: func(t *testing.T, cfg *Config) { - require.Equal(t, DefaultNodeConfig.P2P.ListenAddress, cfg.P2P.ListenAddress) - require.Equal(t, DefaultNodeConfig.P2P.Seeds, cfg.P2P.Seeds) + require.Equal(t, DefaultConfig.P2P.ListenAddress, cfg.P2P.ListenAddress) + require.Equal(t, DefaultConfig.P2P.Seeds, cfg.P2P.Seeds) }, }, } @@ -153,12 +60,15 @@ func TestYamlConfigOperations(t *testing.T) { tc.setup(t, tempDir) // Verify the config file exists - configPath := filepath.Join(path, RollkitConfigYaml) + configPath := filepath.Join(path, ConfigName) _, err := os.Stat(configPath) require.NoError(t, err, "Config file should exist") - // Read the config back - cfg, err := ReadYaml(path) + // Read the config + cmd := &cobra.Command{Use: "test"} + AddFlags(cmd) + AddGlobalFlags(cmd, "test") + cfg, err := Load(cmd) require.NoError(t, err) // Validate the config @@ -166,45 +76,3 @@ func TestYamlConfigOperations(t *testing.T) { }) } } - -func TestCreateInitialConfig(t *testing.T) { - // Create a temporary directory for testing - tempDir := t.TempDir() - - // Test creating initial config with default values - err := CreateInitialConfig(tempDir) - require.NoError(t, err) - - // Verify the config file exists - configPath := filepath.Join(tempDir, "config", RollkitConfigYaml) - _, err = os.Stat(configPath) - require.NoError(t, err) - - // Read the config back - cfg, err := ReadYaml(filepath.Join(tempDir, "config")) - require.NoError(t, err) - - // Verify root directory is set correctly - require.Equal(t, filepath.Join(tempDir, "config"), cfg.RootDir) - - // Test creating config with customizations - tempDir2 := t.TempDir() - err = CreateInitialConfig(tempDir2, func(cfg *Config) { - cfg.Node.Aggregator = true - cfg.Node.BlockTime.Duration = 5 * time.Second - }) - require.NoError(t, err) - - // Read the customized config - cfg, err = ReadYaml(filepath.Join(tempDir2, "config")) - require.NoError(t, err) - - // Verify customizations were applied - require.True(t, cfg.Node.Aggregator) - require.Equal(t, 5*time.Second, cfg.Node.BlockTime.Duration) - - // Test error when config file already exists - err = CreateInitialConfig(tempDir) - require.Error(t, err) - require.Contains(t, err.Error(), "file already exists") -} diff --git a/pkg/p2p/client.go b/pkg/p2p/client.go index 6664852ac2..d571860aa0 100644 --- a/pkg/p2p/client.go +++ b/pkg/p2p/client.go @@ -62,7 +62,6 @@ type Client struct { // NewClient creates new Client object. // // Basic checks on parameters are done, and default parameters are provided for unset-configuration -// TODO(tzdybal): consider passing entire config, not just P2P config, to reduce number of arguments func NewClient( conf config.Config, chainID string, @@ -76,7 +75,7 @@ func NewClient( } if conf.P2P.ListenAddress == "" { - conf.P2P.ListenAddress = config.DefaultNodeConfig.P2P.ListenAddress + conf.P2P.ListenAddress = config.DefaultConfig.P2P.ListenAddress } gater, err := conngater.NewBasicConnectionGater(ds) @@ -150,7 +149,6 @@ func (c *Client) startWithHost(ctx context.Context, h host.Host) error { // Close gently stops Client. func (c *Client) Close() error { - return errors.Join( c.dht.Close(), c.host.Close(), From 8e29393fac8e4773fdb3e076a658c5409febdc85 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 16:58:08 +0200 Subject: [PATCH 02/13] tests --- pkg/config/config.go | 2 +- pkg/config/config_test.go | 10 ++++++---- pkg/config/yaml_test.go | 12 +++--------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 610fc4f66f..57044133a4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -232,7 +232,7 @@ func AddGlobalFlags(cmd *cobra.Command, defaultHome string) { cmd.PersistentFlags().String(FlagLogLevel, DefaultConfig.Log.Level, "Set the log level (debug, info, warn, error)") cmd.PersistentFlags().String(FlagLogFormat, DefaultConfig.Log.Format, "Set the log format (text, json)") cmd.PersistentFlags().Bool(FlagLogTrace, DefaultConfig.Log.Trace, "Enable stack traces in error logs") - cmd.PersistentFlags().String(FlagRootDir, DefaultRootDir, "Root directory for application data") + cmd.PersistentFlags().String(FlagRootDir, DefaultRootDirWithName(defaultHome), "Root directory for application data") } // AddFlags adds Rollkit specific configuration options to cobra Command. diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index ec3d268cdd..10be7f6861 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -101,7 +101,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagSignerPath, DefaultConfig.Signer.SignerPath) // Count the number of flags we're explicitly checking - expectedFlagCount := 41 // Update this number if you add more flag checks above + expectedFlagCount := 39 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 @@ -122,11 +122,10 @@ func TestAddFlags(t *testing.T) { } func TestLoad(t *testing.T) { - // Create a temporary directory for the test tempDir := t.TempDir() // Create a YAML file in the temporary directory - yamlPath := filepath.Join(tempDir, ConfigName) + yamlPath := filepath.Join(tempDir, AppConfigDir, ConfigName) yamlContent := ` node: aggregator: true @@ -141,7 +140,9 @@ signer: signer_type: "file" signer_path: "something/config" ` - err := os.WriteFile(yamlPath, []byte(yamlContent), 0o600) + err := os.MkdirAll(filepath.Dir(yamlPath), 0o700) + require.NoError(t, err) + err = os.WriteFile(yamlPath, []byte(yamlContent), 0o600) require.NoError(t, err) // Change to the temporary directory so the config file can be found @@ -179,6 +180,7 @@ signer: // Load the configuration config, err := Load(cmd) require.NoError(t, err) + require.NoError(t, config.Validate()) // Verify the order of precedence: // 1. Default values should be overridden by YAML diff --git a/pkg/config/yaml_test.go b/pkg/config/yaml_test.go index 678c0d4ea4..197466645c 100644 --- a/pkg/config/yaml_test.go +++ b/pkg/config/yaml_test.go @@ -1,8 +1,6 @@ package config import ( - "os" - "path/filepath" "testing" "github.com/spf13/cobra" @@ -54,22 +52,18 @@ func TestYamlConfigOperations(t *testing.T) { // Create a temporary directory for each test case tempDir := t.TempDir() - path := filepath.Join(tempDir, "config") - // Setup the test case and write the initial config tc.setup(t, tempDir) - // Verify the config file exists - configPath := filepath.Join(path, ConfigName) - _, err := os.Stat(configPath) - require.NoError(t, err, "Config file should exist") - // Read the config cmd := &cobra.Command{Use: "test"} AddFlags(cmd) AddGlobalFlags(cmd, "test") + cmd.Flags().Set(FlagRootDir, tempDir) + cfg, err := Load(cmd) require.NoError(t, err) + require.NoError(t, cfg.Validate()) // Validate the config tc.validate(t, &cfg) From 59ea2a3f0ade1f408ac15bdbde4d892f127e363f Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 17:14:06 +0200 Subject: [PATCH 03/13] updates --- pkg/config/config.go | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 57044133a4..b32b584f5f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -326,13 +326,11 @@ func Load(cmd *cobra.Command) (Config, error) { return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed decoding file: %w", err)) } - // Unmarshal directly into Config - if err := v.Unmarshal(&cfg, func(c *mapstructure.DecoderConfig) { - c.TagName = "mapstructure" - c.DecodeHook = mapstructure.ComposeDecodeHookFunc( + decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), - func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + func(f reflect.Type, t reflect.Type, data any) (any, error) { if t == reflect.TypeOf(DurationWrapper{}) && f.Kind() == reflect.String { if str, ok := data.(string); ok { duration, err := time.ParseDuration(str) @@ -344,9 +342,16 @@ func Load(cmd *cobra.Command) (Config, error) { } return data, nil }, - ) - }); err != nil { - return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed unmarshaling config: %w", err)) + ), + Result: &cfg, + WeaklyTypedInput: true, + }) + if err != nil { + return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed creating decoder: %w", err)) + } + + if err := decoder.Decode(v.AllSettings()); err != nil { + return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed decoding file: %w", err)) } return cfg, nil @@ -360,23 +365,25 @@ func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) }() cmd.Flags().VisitAll(func(f *pflag.Flag) { + flagName := strings.TrimPrefix(f.Name, "rollkit.") // trimm the prefix from the flag name + // Environment variables can't have dashes in them, so bind them to their equivalent // keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR - err = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_")))) + err = v.BindEnv(flagName, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(flagName, "-", "_")))) if err != nil { panic(err) } - err = v.BindPFlag(f.Name, f) + err = v.BindPFlag(flagName, f) if err != nil { panic(err) } // Apply the viper config value to the flag when the flag is not set and // viper has a value. - if !f.Changed && v.IsSet(f.Name) { - val := v.Get(f.Name) - err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val)) + if !f.Changed && v.IsSet(flagName) { + val := v.Get(flagName) + err = cmd.Flags().Set(flagName, fmt.Sprintf("%v", val)) if err != nil { panic(err) } From 7542bd83dd85df3d5f560ce1bce42cd0a315e4bf Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 17:57:01 +0200 Subject: [PATCH 04/13] ok --- pkg/cmd/init.go | 17 +++++------ pkg/cmd/init_test.go | 7 ++--- pkg/cmd/run_node_test.go | 64 +++++++++++++++++++++++++++------------- pkg/config/config.go | 32 ++++++++++++-------- pkg/config/defaults.go | 12 +++++--- pkg/config/yaml.go | 2 +- pkg/config/yaml_test.go | 7 +++-- 7 files changed, 85 insertions(+), 56 deletions(-) diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index ace9b7caaf..6253e069e6 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -129,8 +129,8 @@ var InitCmd = &cobra.Command{ return fmt.Errorf("error reading aggregator flag: %w", err) } - config := InitializeConfig(homePath, aggregator) - if err := config.Validate(); err != nil { + cfg := InitializeConfig(homePath, aggregator) + if err := cfg.Validate(); err != nil { return fmt.Errorf("error validating config: %w", err) } @@ -139,12 +139,12 @@ var InitCmd = &cobra.Command{ return fmt.Errorf("error reading passphrase flag: %w", err) } - proposerAddress, err := InitializeSigner(&config, homePath, passphrase) + proposerAddress, err := InitializeSigner(&cfg, homePath, passphrase) if err != nil { return err } - if err := config.SaveAsYaml(); err != nil { + if err := cfg.SaveAsYaml(); err != nil { return fmt.Errorf("error writing rollkit.yaml file: %w", err) } @@ -152,11 +152,8 @@ var InitCmd = &cobra.Command{ return err } - // Get chain ID or use default - chainID, err := cmd.Flags().GetString(rollconf.FlagChainID) - if err != nil { - return fmt.Errorf("error reading chain ID flag: %w", err) - } + // get chain ID or use default + chainID, _ := cmd.Flags().GetString(rollconf.FlagChainID) if chainID == "" { chainID = "rollkit-test" } @@ -166,7 +163,7 @@ var InitCmd = &cobra.Command{ return fmt.Errorf("error initializing genesis file: %w", err) } - fmt.Printf("Initialized %s file in %s\n", rollconf.ConfigName, homePath) + cmd.Printf("Successfully initialized config file at %s\n", cfg.ConfigPath()) return nil }, } diff --git a/pkg/cmd/init_test.go b/pkg/cmd/init_test.go index ca4f34f443..e99c12bb49 100644 --- a/pkg/cmd/init_test.go +++ b/pkg/cmd/init_test.go @@ -50,13 +50,10 @@ func TestInitCommand(t *testing.T) { err = cmd.Execute() require.NoError(t, err) - // Verify the file was created - _, err = os.Stat(configPath) - require.NoError(t, err) - // Verify the config can be read - _, err = rollconf.Load(cmd) + cfg, err := rollconf.Load(cmd) require.NoError(t, err) + require.NoError(t, cfg.Validate()) // Read the file content directly to verify the YAML structure //nolint:gosec // This is a test file and we control the input diff --git a/pkg/cmd/run_node_test.go b/pkg/cmd/run_node_test.go index 19de8eed78..2e889c951e 100644 --- a/pkg/cmd/run_node_test.go +++ b/pkg/cmd/run_node_test.go @@ -24,7 +24,7 @@ import ( filesigner "github.com/rollkit/rollkit/pkg/signer/file" ) -func createTestComponents(ctx context.Context, t *testing.T) (coreexecutor.Executor, coresequencer.Sequencer, coreda.Client, signer.Signer, *p2p.Client, datastore.Batching) { +func createTestComponents(_ context.Context, t *testing.T) (coreexecutor.Executor, coresequencer.Sequencer, coreda.Client, signer.Signer, *p2p.Client, datastore.Batching) { executor := coreexecutor.NewDummyExecutor() sequencer := coresequencer.NewDummySequencer() dummyDA := coreda.NewDummyDA(100_000, 0, 0) @@ -196,31 +196,53 @@ func TestAggregatorFlagInvariants(t *testing.T) { // TestDefaultAggregatorValue verifies that the default value of Aggregator is true // when no flag is specified func TestDefaultAggregatorValue(t *testing.T) { - // Create a new command without specifying any flags - args := []string{"start"} - executor, sequencer, dac, keyProvider, p2pClient, ds := createTestComponents(context.Background(), t) - - nodeKey, err := key.GenerateNodeKey() - if err != nil { - t.Fatalf("Error: %v", err) + testCases := []struct { + name string + expected bool + }{ + {"DefaultAggregatorFalse", false}, + {"DefaultAggregatorTrue", true}, } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + executor, sequencer, dac, keyProvider, p2pClient, ds := createTestComponents(context.Background(), t) - nodeConfig := rollconf.DefaultConfig + nodeKey, err := key.GenerateNodeKey() + if err != nil { + t.Fatalf("Error: %v", err) + } - newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) - newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") + nodeConfig := rollconf.DefaultConfig - if err := newRunNodeCmd.ParseFlags(args); err != nil { - t.Errorf("Error parsing flags: %v", err) - } + newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) + newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") - nodeConfig, err = ParseConfig(newRunNodeCmd) - if err != nil { - t.Errorf("Error parsing config: %v", err) - } + // Create a new command without specifying any flags + var args []string + if tc.expected { + args = []string{"start"} + } else { + args = []string{"start", "--rollkit.node.aggregator=false"} + } - // Verify that Aggregator is true by default - assert.False(t, nodeConfig.Node.Aggregator, "Expected Aggregator to be false by default") + if err := newRunNodeCmd.ParseFlags(args); err != nil { + t.Errorf("Error parsing flags: %v", err) + } + + nodeConfig, err = ParseConfig(newRunNodeCmd) + if err != nil { + t.Errorf("Error parsing config: %v", err) + } + + if tc.expected { + // Verify that Aggregator is true by default + assert.True(t, nodeConfig.Node.Aggregator, "Expected Aggregator to be true by default") + } else { + // Verify that Aggregator is false when explicitly set + assert.False(t, nodeConfig.Node.Aggregator) + } + }) + } } // TestCentralizedAddresses verifies that when centralized service flags are provided, @@ -301,7 +323,7 @@ func newRunNodeCmd( // Add Rollkit flags rollconf.AddFlags(cmd) - rollconf.AddGlobalFlags(cmd, "testapp") + rollconf.AddGlobalFlags(cmd, "") return cmd } diff --git a/pkg/config/config.go b/pkg/config/config.go index b32b584f5f..48ef8e46c0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -218,13 +218,19 @@ func (c *Config) Validate() error { return fmt.Errorf("root directory cannot be empty") } - if err := os.MkdirAll(c.RootDir, 0o750); err != nil { - return fmt.Errorf("could not create directory %q: %w", c.RootDir, err) + fullDir := filepath.Dir(c.ConfigPath()) + if err := os.MkdirAll(fullDir, 0o750); err != nil { + return fmt.Errorf("could not create directory %q: %w", fullDir, err) } return nil } +// ConfigPath returns the path to the configuration file. +func (c *Config) ConfigPath() string { + return filepath.Join(c.RootDir, AppConfigDir, ConfigName) +} + // AddGlobalFlags registers the basic configuration flags that are common across applications. // This includes logging configuration and root directory settings. // It should be used in apps that do not already define their logger and home flag. @@ -244,7 +250,7 @@ func AddFlags(cmd *cobra.Command) { cmd.Flags().String(FlagChainID, def.ChainID, "chain ID") // Node configuration flags - cmd.Flags().BoolVar(&def.Node.Aggregator, FlagAggregator, def.Node.Aggregator, "run node in aggregator mode") + cmd.Flags().Bool(FlagAggregator, def.Node.Aggregator, "run node in aggregator mode") cmd.Flags().Bool(FlagLight, def.Node.Light, "run light client") cmd.Flags().Duration(FlagBlockTime, def.Node.BlockTime.Duration, "block time (for aggregator mode)") cmd.Flags().String(FlagTrustedHash, def.Node.TrustedHash, "initial trusted hash to start the header exchange service") @@ -300,17 +306,19 @@ func Load(cmd *cobra.Command) (Config, error) { home = DefaultRootDir } + cfg := DefaultConfig + cfg.RootDir = home + v := viper.New() - v.SetConfigName(ConfigDirName) + v.SetConfigName(ConfigFileName) v.SetConfigType(ConfigExtension) v.AddConfigPath(filepath.Join(home, AppConfigDir)) + v.AddConfigPath(filepath.Join(home, AppConfigDir, ConfigName)) + v.SetConfigFile(filepath.Join(home, AppConfigDir, ConfigName)) v.BindPFlags(cmd.Flags()) v.BindPFlags(cmd.PersistentFlags()) v.AutomaticEnv() - cfg := DefaultConfig - cfg.RootDir = filepath.Dir(v.ConfigFileUsed()) - // get the executable name executableName, err := os.Executable() if err != nil { @@ -321,10 +329,10 @@ func Load(cmd *cobra.Command) (Config, error) { return cfg, err } - // Read the configuration file - if err := v.ReadInConfig(); err != nil { - return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed decoding file: %w", err)) - } + // read the configuration file + // if the configuration file does not exist, we ignore the error + // it will use the defaults + _ = v.ReadInConfig() decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( @@ -351,7 +359,7 @@ func Load(cmd *cobra.Command) (Config, error) { } if err := decoder.Decode(v.AllSettings()); err != nil { - return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed decoding file: %w", err)) + return cfg, errors.Join(ErrReadYaml, fmt.Errorf("failed decoding viper: %w", err)) } return cfg, nil diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index e86e0149af..c0e8a13d7d 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -7,22 +7,26 @@ import ( ) const ( - // ConfigDirName is the base name of the rollkit configuration file without extension. - ConfigDirName = "rollkit" + // ConfigFileName is the base name of the rollkit configuration file without extension. + ConfigFileName = "rollkit" // ConfigExtension is the file extension for the configuration file without the leading dot. ConfigExtension = "yaml" // ConfigPath is the filename for the rollkit configuration file. - ConfigName = ConfigDirName + "." + ConfigExtension + ConfigName = ConfigFileName + "." + ConfigExtension // AppConfigDir is the directory name for the app configuration. AppConfigDir = "config" ) // DefaultRootDir returns the default root directory for rollkit -var DefaultRootDir = DefaultRootDirWithName(ConfigDirName) +var DefaultRootDir = DefaultRootDirWithName(ConfigFileName) // DefaultRootDirWithName returns the default root directory for an application, // based on the app name and the user's home directory func DefaultRootDirWithName(appName string) string { + if appName == "" { + appName = ConfigFileName + } + home, err := os.UserHomeDir() if err != nil { return "" diff --git a/pkg/config/yaml.go b/pkg/config/yaml.go index 8ad6556666..d41584325a 100644 --- a/pkg/config/yaml.go +++ b/pkg/config/yaml.go @@ -33,7 +33,7 @@ var ErrReadYaml = fmt.Errorf("reading %s", ConfigName) // SaveAsYaml saves the current configuration to a YAML file. func (c *Config) SaveAsYaml() error { - configPath := filepath.Join(DefaultConfig.RootDir, AppConfigDir, ConfigName) + configPath := c.ConfigPath() if err := os.MkdirAll(filepath.Dir(configPath), 0o750); err != nil { return fmt.Errorf("could not create directory %q: %w", filepath.Dir(configPath), err) } diff --git a/pkg/config/yaml_test.go b/pkg/config/yaml_test.go index 197466645c..a19b84071c 100644 --- a/pkg/config/yaml_test.go +++ b/pkg/config/yaml_test.go @@ -49,7 +49,6 @@ func TestYamlConfigOperations(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - // Create a temporary directory for each test case tempDir := t.TempDir() // Setup the test case and write the initial config @@ -58,8 +57,10 @@ func TestYamlConfigOperations(t *testing.T) { // Read the config cmd := &cobra.Command{Use: "test"} AddFlags(cmd) - AddGlobalFlags(cmd, "test") - cmd.Flags().Set(FlagRootDir, tempDir) + AddGlobalFlags(cmd, "") + args := []string{"--home=" + tempDir} + cmd.SetArgs(args) + cmd.ParseFlags(args) cfg, err := Load(cmd) require.NoError(t, err) From 896529523a18d65018e2e253f3b1833fd9240049 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 18:06:19 +0200 Subject: [PATCH 05/13] update app --- go.mod | 2 +- pkg/cmd/run_node.go | 3 +-- rollups/testapp/README.md | 2 +- rollups/testapp/cmd/run.go | 13 +++---------- scripts/go-mod-tidy-all.sh | 9 +++++++++ 5 files changed, 15 insertions(+), 14 deletions(-) create mode 100755 scripts/go-mod-tidy-all.sh diff --git a/go.mod b/go.mod index 1aa0d3d4d7..ed9ab3f361 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( github.com/cosmos/gogoproto v1.7.0 github.com/go-kit/kit v0.13.0 github.com/goccy/go-yaml v1.16.0 - github.com/hashicorp/go-multierror v1.1.1 github.com/ipfs/go-datastore v0.7.0 github.com/ipfs/go-ds-badger4 v0.1.5 github.com/libp2p/go-libp2p v0.40.0 @@ -78,6 +77,7 @@ require ( github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/pkg/cmd/run_node.go b/pkg/cmd/run_node.go index 8c32c2bce3..cba4a362c3 100644 --- a/pkg/cmd/run_node.go +++ b/pkg/cmd/run_node.go @@ -19,7 +19,6 @@ import ( coreexecutor "github.com/rollkit/rollkit/core/execution" coresequencer "github.com/rollkit/rollkit/core/sequencer" "github.com/rollkit/rollkit/node" - "github.com/rollkit/rollkit/pkg/config" rollconf "github.com/rollkit/rollkit/pkg/config" genesispkg "github.com/rollkit/rollkit/pkg/genesis" "github.com/rollkit/rollkit/pkg/p2p" @@ -121,7 +120,7 @@ func StartNode( metrics := node.DefaultMetricsProvider(rollconf.DefaultInstrumentationConfig()) - genesisPath := filepath.Join(config.AppConfigDir, "genesis.json") + genesisPath := filepath.Join(filepath.Dir(nodeConfig.ConfigPath()), "genesis.json") genesis, err := genesispkg.LoadGenesis(genesisPath) if err != nil { return fmt.Errorf("failed to load genesis: %w", err) diff --git a/rollups/testapp/README.md b/rollups/testapp/README.md index 349fd9baa1..0415d76676 100644 --- a/rollups/testapp/README.md +++ b/rollups/testapp/README.md @@ -11,7 +11,7 @@ When implementing your own application, it's your responsibility to provide the To install and test the application, you can use the following command: ```bash -make install +go build . ``` This will build and install all necessary dependencies for running the test application. diff --git a/rollups/testapp/cmd/run.go b/rollups/testapp/cmd/run.go index bcc72edc08..808740dffe 100644 --- a/rollups/testapp/cmd/run.go +++ b/rollups/testapp/cmd/run.go @@ -1,8 +1,8 @@ package cmd import ( - "fmt" "os" + "path/filepath" "cosmossdk.io/log" "github.com/spf13/cobra" @@ -10,7 +10,6 @@ import ( coreda "github.com/rollkit/rollkit/core/da" "github.com/rollkit/rollkit/da" rollcmd "github.com/rollkit/rollkit/pkg/cmd" - "github.com/rollkit/rollkit/pkg/config" "github.com/rollkit/rollkit/pkg/p2p" "github.com/rollkit/rollkit/pkg/p2p/key" "github.com/rollkit/rollkit/pkg/store" @@ -23,19 +22,13 @@ var RunCmd = &cobra.Command{ Aliases: []string{"node", "run"}, Short: "Run the testapp node", RunE: func(cmd *cobra.Command, args []string) error { - logger := log.NewLogger(os.Stdout) // Create test implementations // TODO: we need to start the executor http server executor := kvexecutor.CreateDirectKVExecutor() - homePath, err := cmd.Flags().GetString(config.FlagRootDir) - if err != nil { - return fmt.Errorf("error reading home flag: %w", err) - } - - nodeConfig, err := rollcmd.ParseConfig(cmd, homePath) + nodeConfig, err := rollcmd.ParseConfig(cmd) if err != nil { panic(err) } @@ -44,7 +37,7 @@ var RunCmd = &cobra.Command{ dummyDA := coreda.NewDummyDA(100_000, 0, 0) dac := da.NewDAClient(dummyDA, 0, 1.0, []byte("test"), []byte(""), logger) - nodeKey, err := key.LoadNodeKey(nodeConfig.ConfigDir) + nodeKey, err := key.LoadNodeKey(filepath.Dir(nodeConfig.ConfigPath())) if err != nil { panic(err) } diff --git a/scripts/go-mod-tidy-all.sh b/scripts/go-mod-tidy-all.sh new file mode 100755 index 0000000000..c129f734d4 --- /dev/null +++ b/scripts/go-mod-tidy-all.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +for modfile in $(find . -name go.mod); do + echo "Updating $modfile" + DIR=$(dirname $modfile) + (cd $DIR; go mod tidy) +done From 1dce9e757429bba14a2463351add14b8384de8a8 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 20:42:40 +0200 Subject: [PATCH 06/13] simplify --- pkg/cmd/init.go | 15 +++++---------- pkg/config/yaml.go | 2 +- rollups/testapp/cmd/root.go | 3 +-- rollups/testapp/cmd/run.go | 23 ++++++++++++++++------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index 6253e069e6..67a6303cf1 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -16,14 +16,6 @@ import ( "github.com/rollkit/rollkit/pkg/signer/file" ) -// InitializeConfig creates and initializes the configuration with default values -func InitializeConfig(homePath string, aggregator bool) rollconf.Config { - config := rollconf.DefaultConfig - config.RootDir = homePath - config.Node.Aggregator = aggregator - return config -} - // InitializeSigner sets up the signer configuration and creates necessary files func InitializeSigner(config *rollconf.Config, homePath string, passphrase string) ([]byte, error) { if config.Signer.SignerType == "file" && config.Node.Aggregator { @@ -82,7 +74,7 @@ func InitializeGenesis(homePath string, chainID string, initialHeight uint64, pr // Check if the genesis file already exists if _, err := os.Stat(genesisPath); err == nil { // File exists, return successfully without overwriting - fmt.Printf("Genesis file already exists: %s\n", genesisPath) + fmt.Printf("Genesis file already exists at %s: skipping...\n", genesisPath) return nil } else if !os.IsNotExist(err) { // An error other than "not exist" occurred (e.g., permissions) @@ -129,7 +121,10 @@ var InitCmd = &cobra.Command{ return fmt.Errorf("error reading aggregator flag: %w", err) } - cfg := InitializeConfig(homePath, aggregator) + // ignore error, as we are creating a new config + // we use load in order to parse all the flags + cfg, _ := rollconf.Load(cmd) + cfg.Node.Aggregator = aggregator if err := cfg.Validate(); err != nil { return fmt.Errorf("error validating config: %w", err) } diff --git a/pkg/config/yaml.go b/pkg/config/yaml.go index d41584325a..c36148b797 100644 --- a/pkg/config/yaml.go +++ b/pkg/config/yaml.go @@ -42,7 +42,7 @@ func (c *Config) SaveAsYaml() error { yamlCommentMap := yaml.CommentMap{} addComment := func(path string, comment string) { yamlCommentMap[path] = []*yaml.Comment{ - yaml.HeadComment(comment), + yaml.HeadComment(" " + comment), // add a space for better formatting } } diff --git a/rollups/testapp/cmd/root.go b/rollups/testapp/cmd/root.go index 26a30917bd..464acd801e 100644 --- a/rollups/testapp/cmd/root.go +++ b/rollups/testapp/cmd/root.go @@ -22,6 +22,5 @@ var RootCmd = &cobra.Command{ Short: "The first sovereign rollup framework that allows you to launch a sovereign, customizable blockchain as easily as a smart contract.", Long: ` Rollkit is the first sovereign rollup framework that allows you to launch a sovereign, customizable blockchain as easily as a smart contract. -If the --home flag is not specified, the rollkit command will create a folder "~/.testapp" where it will store node keys, config, and data. -`, +If the --home flag is not specified, the rollkit command will create a folder "~/.testapp" where it will store node keys, config, and data.`, } diff --git a/rollups/testapp/cmd/run.go b/rollups/testapp/cmd/run.go index 808740dffe..c6c30a07a3 100644 --- a/rollups/testapp/cmd/run.go +++ b/rollups/testapp/cmd/run.go @@ -30,7 +30,7 @@ var RunCmd = &cobra.Command{ nodeConfig, err := rollcmd.ParseConfig(cmd) if err != nil { - panic(err) + return err } // Create DA client with dummy DA @@ -39,27 +39,36 @@ var RunCmd = &cobra.Command{ nodeKey, err := key.LoadNodeKey(filepath.Dir(nodeConfig.ConfigPath())) if err != nil { - panic(err) + return err } datastore, err := store.NewDefaultKVStore(nodeConfig.RootDir, nodeConfig.DBPath, "testapp") if err != nil { - panic(err) + return err } singleMetrics, err := single.NopMetrics() if err != nil { - panic(err) + return err } - sequencer, err := single.NewSequencer(logger, datastore, dummyDA, []byte(nodeConfig.DA.Namespace), []byte(nodeConfig.Node.SequencerRollupID), nodeConfig.Node.BlockTime.Duration, singleMetrics, nodeConfig.Node.Aggregator) + sequencer, err := single.NewSequencer( + logger, + datastore, + dummyDA, + []byte(nodeConfig.DA.Namespace), + []byte(nodeConfig.Node.SequencerRollupID), + nodeConfig.Node.BlockTime.Duration, + singleMetrics, + nodeConfig.Node.Aggregator, + ) if err != nil { - panic(err) + return err } p2pClient, err := p2p.NewClient(nodeConfig, "testapp", nodeKey, datastore, logger, p2p.NopMetrics()) if err != nil { - panic(err) + return err } return rollcmd.StartNode(logger, cmd, executor, sequencer, dac, nodeKey, p2pClient, datastore, nodeConfig) From 392b681391d927a84c6d8d1c82e71efa31e92ea3 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 20:52:19 +0200 Subject: [PATCH 07/13] bump sonic to remove annoying log line --- go.mod | 7 +++---- go.sum | 13 ++++++------- rollups/testapp/go.mod | 2 +- rollups/testapp/go.sum | 4 ++-- sequencers/single/go.mod | 7 +++---- sequencers/single/go.sum | 13 ++++++------- 6 files changed, 21 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index ed9ab3f361..cc7140c64c 100644 --- a/go.mod +++ b/go.mod @@ -43,12 +43,11 @@ require ( require ( github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.12.3 // indirect - github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/celestiaorg/go-libp2p-messenger v0.2.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect diff --git a/go.sum b/go.sum index 514c3e36e5..e0efe5b824 100644 --- a/go.sum +++ b/go.sum @@ -98,11 +98,11 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= -github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= -github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/celestiaorg/go-header v0.6.4 h1:3kXi7N3qBc4SCmT+tNVWhLi0Ilw5e/7qq2cxVGbs0ss= github.com/celestiaorg/go-header v0.6.4/go.mod h1:Az4S4NxMOJ1eAzOaF8u5AZt5UzsSzg92uqpdXS3yOZE= @@ -125,9 +125,8 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= diff --git a/rollups/testapp/go.mod b/rollups/testapp/go.mod index f3330054a9..be7b4e4950 100644 --- a/rollups/testapp/go.mod +++ b/rollups/testapp/go.mod @@ -23,7 +23,7 @@ require ( connectrpc.com/grpcreflect v1.3.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.13.1 // indirect + github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/celestiaorg/go-header v0.6.4 // indirect github.com/celestiaorg/go-libp2p-messenger v0.2.0 // indirect diff --git a/rollups/testapp/go.sum b/rollups/testapp/go.sum index 7e1adf0915..b23b37ec11 100644 --- a/rollups/testapp/go.sum +++ b/rollups/testapp/go.sum @@ -98,8 +98,8 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= -github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= diff --git a/sequencers/single/go.mod b/sequencers/single/go.mod index 523b3ff14f..7a5bc0cc0b 100644 --- a/sequencers/single/go.mod +++ b/sequencers/single/go.mod @@ -21,11 +21,10 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.12.3 // indirect - github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/google/uuid v1.6.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect diff --git a/sequencers/single/go.sum b/sequencers/single/go.sum index f552051390..7744c4d6da 100644 --- a/sequencers/single/go.sum +++ b/sequencers/single/go.sum @@ -4,16 +4,15 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= -github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= -github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 8c544b47dd1384def8dc12a61f21a2b75ecd0971 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 21:15:40 +0200 Subject: [PATCH 08/13] fix chainid issue + add log level handling in testapp --- rollups/testapp/cmd/run.go | 16 ++++++++++++++-- sequencers/single/sequencer.go | 1 - test/e2e/base_test.go | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/rollups/testapp/cmd/run.go b/rollups/testapp/cmd/run.go index c6c30a07a3..bc65a07991 100644 --- a/rollups/testapp/cmd/run.go +++ b/rollups/testapp/cmd/run.go @@ -5,11 +5,13 @@ import ( "path/filepath" "cosmossdk.io/log" + "github.com/rs/zerolog" "github.com/spf13/cobra" coreda "github.com/rollkit/rollkit/core/da" "github.com/rollkit/rollkit/da" rollcmd "github.com/rollkit/rollkit/pkg/cmd" + "github.com/rollkit/rollkit/pkg/config" "github.com/rollkit/rollkit/pkg/p2p" "github.com/rollkit/rollkit/pkg/p2p/key" "github.com/rollkit/rollkit/pkg/store" @@ -22,7 +24,17 @@ var RunCmd = &cobra.Command{ Aliases: []string{"node", "run"}, Short: "Run the testapp node", RunE: func(cmd *cobra.Command, args []string) error { - logger := log.NewLogger(os.Stdout) + opts := []log.Option{} + logLevel, _ := cmd.Flags().GetString(config.FlagLogLevel) + if logLevel != "" { + zl, err := zerolog.ParseLevel(logLevel) + if err != nil { + return err + } + opts = append(opts, log.LevelOption(zl)) + } + + logger := log.NewLogger(os.Stdout, opts...) // Create test implementations // TODO: we need to start the executor http server @@ -57,7 +69,7 @@ var RunCmd = &cobra.Command{ datastore, dummyDA, []byte(nodeConfig.DA.Namespace), - []byte(nodeConfig.Node.SequencerRollupID), + []byte(nodeConfig.ChainID), nodeConfig.Node.BlockTime.Duration, singleMetrics, nodeConfig.Node.Aggregator, diff --git a/sequencers/single/sequencer.go b/sequencers/single/sequencer.go index 1a9bd47d71..05b9970dab 100644 --- a/sequencers/single/sequencer.go +++ b/sequencers/single/sequencer.go @@ -62,7 +62,6 @@ func NewSequencer( metrics *Metrics, proposer bool, ) (*Sequencer, error) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() diff --git a/test/e2e/base_test.go b/test/e2e/base_test.go index 8ca2f93302..2bf9ff5502 100644 --- a/test/e2e/base_test.go +++ b/test/e2e/base_test.go @@ -39,7 +39,7 @@ func TestBasic(t *testing.T) { output, err := sut.RunCmd(binaryPath, "init", "--home="+node1Home, - "--rollkit.node.sequencer_rollup_id=testing", + "--chain_id=testing", "--rollkit.node.aggregator", "--rollkit.node.block_time=5ms", "--rollkit.da.block_time=15ms", @@ -52,7 +52,7 @@ func TestBasic(t *testing.T) { sut.StartNode(binaryPath, "start", "--home="+node1Home, - "--rollkit.node.sequencer_rollup_id=testing", + "--chain_id=testing", "--rollkit.node.aggregator", "--rollkit.signer.passphrase="+aggregatorPass, "--rollkit.node.block_time=5ms", From 9ab24df9a70f00971114be44a3f54929758f84fd Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Thu, 10 Apr 2025 21:52:00 +0200 Subject: [PATCH 09/13] bump minimum go due to bug in go 1.24.0 --- core/go.mod | 2 +- da/go.mod | 2 +- go.mod | 2 +- rollups/abci/based/go.mod | 2 +- rollups/abci/centralized/go.mod | 2 +- rollups/evm/based/go.mod | 2 +- rollups/evm/centralized/go.mod | 2 +- sequencers/based/go.mod | 2 +- sequencers/single/go.mod | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/go.mod b/core/go.mod index 623dace7a7..995aa1b10e 100644 --- a/core/go.mod +++ b/core/go.mod @@ -1,5 +1,5 @@ module github.com/rollkit/rollkit/core -go 1.24.0 +go 1.24.1 // Please keep this pkg a 0 dependency pkg. diff --git a/da/go.mod b/da/go.mod index 34878c9870..4cd0b72c2b 100644 --- a/da/go.mod +++ b/da/go.mod @@ -1,6 +1,6 @@ module github.com/rollkit/rollkit/da -go 1.24.0 +go 1.24.1 replace github.com/rollkit/rollkit/core => ../core diff --git a/go.mod b/go.mod index cc7140c64c..dd56614d99 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/rollkit/rollkit -go 1.24.0 +go 1.24.1 retract v0.12.0 // Published by accident diff --git a/rollups/abci/based/go.mod b/rollups/abci/based/go.mod index 5decd20628..fd97cea112 100644 --- a/rollups/abci/based/go.mod +++ b/rollups/abci/based/go.mod @@ -1,3 +1,3 @@ module github.com/rollkit/rollkit/rollups/abci/based -go 1.24.0 +go 1.24.1 diff --git a/rollups/abci/centralized/go.mod b/rollups/abci/centralized/go.mod index 3faf078bab..08ba73c198 100644 --- a/rollups/abci/centralized/go.mod +++ b/rollups/abci/centralized/go.mod @@ -1,3 +1,3 @@ module github.com/rollkit/rollkit/rollups/abci/centralized -go 1.24.0 +go 1.24.1 diff --git a/rollups/evm/based/go.mod b/rollups/evm/based/go.mod index 1b6e4aa2f8..4b6268ce56 100644 --- a/rollups/evm/based/go.mod +++ b/rollups/evm/based/go.mod @@ -1,3 +1,3 @@ module github.com/rollkit/rollkit/rollups/evm/based -go 1.24.0 +go 1.24.1 diff --git a/rollups/evm/centralized/go.mod b/rollups/evm/centralized/go.mod index 1b6e4aa2f8..4b6268ce56 100644 --- a/rollups/evm/centralized/go.mod +++ b/rollups/evm/centralized/go.mod @@ -1,3 +1,3 @@ module github.com/rollkit/rollkit/rollups/evm/based -go 1.24.0 +go 1.24.1 diff --git a/sequencers/based/go.mod b/sequencers/based/go.mod index 87a4398eac..7548efefb2 100644 --- a/sequencers/based/go.mod +++ b/sequencers/based/go.mod @@ -1,3 +1,3 @@ module github.com/rollkit/rollkit/sequencers/based -go 1.24.0 +go 1.24.1 diff --git a/sequencers/single/go.mod b/sequencers/single/go.mod index 7a5bc0cc0b..5c0d409eb2 100644 --- a/sequencers/single/go.mod +++ b/sequencers/single/go.mod @@ -1,6 +1,6 @@ module github.com/rollkit/rollkit/sequencers/single -go 1.24.0 +go 1.24.1 replace ( github.com/rollkit/rollkit => ../../ From c0b45b4db11d592649bf91cd9744faaad6b8defd Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 11 Apr 2025 11:06:37 +0200 Subject: [PATCH 10/13] remove bash script --- scripts/go-mod-tidy-all.sh | 9 --------- 1 file changed, 9 deletions(-) delete mode 100755 scripts/go-mod-tidy-all.sh diff --git a/scripts/go-mod-tidy-all.sh b/scripts/go-mod-tidy-all.sh deleted file mode 100755 index c129f734d4..0000000000 --- a/scripts/go-mod-tidy-all.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -for modfile in $(find . -name go.mod); do - echo "Updating $modfile" - DIR=$(dirname $modfile) - (cd $DIR; go mod tidy) -done From 3944be928dc21a9324bf39dbd08bc8abca8ae29c Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 11 Apr 2025 11:08:01 +0200 Subject: [PATCH 11/13] updates --- pkg/config/config_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 771f215fb2..7557ee9227 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -99,7 +99,7 @@ func TestAddFlags(t *testing.T) { assertFlagValue(t, flags, FlagRPCAddress, DefaultConfig.RPC.Address) // Count the number of flags we're explicitly checking - expectedFlagCount := 37 // Update this number if you add more flag checks above + expectedFlagCount := 35 // Update this number if you add more flag checks above // Get the actual number of flags (both regular and persistent) actualFlagCount := 0 From fb0a27752c91a89098cbd212635e895d628e6d01 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 11 Apr 2025 11:16:54 +0200 Subject: [PATCH 12/13] linting --- pkg/cmd/run_node_test.go | 9 ++++----- pkg/config/config.go | 4 ++-- pkg/config/yaml_test.go | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/pkg/cmd/run_node_test.go b/pkg/cmd/run_node_test.go index 26ca5ce2cc..6ca0feb130 100644 --- a/pkg/cmd/run_node_test.go +++ b/pkg/cmd/run_node_test.go @@ -89,7 +89,7 @@ func TestParseFlags(t *testing.T) { nodeConfig.RootDir = t.TempDir() newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) - newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") + _ = newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := newRunNodeCmd.ParseFlags(args); err != nil { t.Errorf("Error: %v", err) } @@ -170,7 +170,7 @@ func TestAggregatorFlagInvariants(t *testing.T) { nodeConfig.RootDir = t.TempDir() newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) - newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") + _ = newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := newRunNodeCmd.ParseFlags(args); err != nil { t.Errorf("Error: %v", err) @@ -194,8 +194,8 @@ func TestDefaultAggregatorValue(t *testing.T) { name string expected bool }{ - {"DefaultAggregatorFalse", false}, {"DefaultAggregatorTrue", true}, + {"DefaultAggregatorFalse", false}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -209,7 +209,7 @@ func TestDefaultAggregatorValue(t *testing.T) { nodeConfig := rollconf.DefaultConfig newRunNodeCmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) - newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") + _ = newRunNodeCmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") // Create a new command without specifying any flags var args []string @@ -271,7 +271,6 @@ func TestCentralizedAddresses(t *testing.T) { if nodeConfig.DA.Address != "http://central-da:26657" { t.Errorf("Expected nodeConfig.Rollkit.DAAddress to be 'http://central-da:26657', got '%s'", nodeConfig.DA.Address) } - } // newRunNodeCmd returns the command that allows the CLI to start a node. diff --git a/pkg/config/config.go b/pkg/config/config.go index 6ad45507a9..c61fa94dec 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -297,8 +297,8 @@ func Load(cmd *cobra.Command) (Config, error) { v.AddConfigPath(filepath.Join(home, AppConfigDir)) v.AddConfigPath(filepath.Join(home, AppConfigDir, ConfigName)) v.SetConfigFile(filepath.Join(home, AppConfigDir, ConfigName)) - v.BindPFlags(cmd.Flags()) - v.BindPFlags(cmd.PersistentFlags()) + _ = v.BindPFlags(cmd.Flags()) + _ = v.BindPFlags(cmd.PersistentFlags()) v.AutomaticEnv() // get the executable name diff --git a/pkg/config/yaml_test.go b/pkg/config/yaml_test.go index b1876cd0f8..59acf82018 100644 --- a/pkg/config/yaml_test.go +++ b/pkg/config/yaml_test.go @@ -60,7 +60,7 @@ func TestYamlConfigOperations(t *testing.T) { AddGlobalFlags(cmd, "") args := []string{"--home=" + tempDir} cmd.SetArgs(args) - cmd.ParseFlags(args) + require.NoError(t, cmd.ParseFlags(args)) cfg, err := Load(cmd) require.NoError(t, err) From c07aea455aa35fefeb1f7147e722d1a92b2c0569 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 11 Apr 2025 11:23:44 +0200 Subject: [PATCH 13/13] updates --- pkg/cmd/init.go | 2 +- pkg/cmd/run_node_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index 67a6303cf1..f802f5aa27 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -108,7 +108,7 @@ func InitializeGenesis(homePath string, chainID string, initialHeight uint64, pr // InitCmd initializes a new rollkit.yaml file in the current directory var InitCmd = &cobra.Command{ Use: "init", - Short: fmt.Sprintf("Initialize rollkit config"), + Short: "Initialize rollkit config", Long: fmt.Sprintf("This command initializes a new %s file in the specified directory (or current directory if not specified).", rollconf.ConfigName), RunE: func(cmd *cobra.Command, args []string) error { homePath, err := cmd.Flags().GetString(rollconf.FlagRootDir) diff --git a/pkg/cmd/run_node_test.go b/pkg/cmd/run_node_test.go index 6ca0feb130..dc4c4383ec 100644 --- a/pkg/cmd/run_node_test.go +++ b/pkg/cmd/run_node_test.go @@ -214,7 +214,7 @@ func TestDefaultAggregatorValue(t *testing.T) { // Create a new command without specifying any flags var args []string if tc.expected { - args = []string{"start"} + args = []string{"start", "--rollkit.node.aggregator"} } else { args = []string{"start", "--rollkit.node.aggregator=false"} } @@ -258,7 +258,7 @@ func TestCentralizedAddresses(t *testing.T) { } cmd := newRunNodeCmd(t.Context(), executor, sequencer, dac, keyProvider, nodeKey, p2pClient, ds, nodeConfig) - cmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") + _ = cmd.Flags().Set(rollconf.FlagRootDir, "custom/root/dir") if err := cmd.ParseFlags(args); err != nil { t.Fatalf("ParseFlags error: %v", err) }