diff --git a/CHANGELOG.md b/CHANGELOG.md index 22b18d6f19..52ebb9d2e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (client) [#17513](https://github.com/cosmos/cosmos-sdk/pull/17513) Allow overwritting `client.toml`. Use `client.CreateClientConfig` in place of `client.ReadFromClientConfig` and provide a custom template and a custom config. * (x/bank) [#14224](https://github.com/cosmos/cosmos-sdk/pull/14224) Allow injection of restrictions on transfers using `AppendSendRestriction` or `PrependSendRestriction`. ### Improvements diff --git a/client/config/config.go b/client/config/config.go index f0173ffc33..35f4b10b2d 100644 --- a/client/config/config.go +++ b/client/config/config.go @@ -6,10 +6,12 @@ import ( "path/filepath" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" ) -func DefaultConfig() *ClientConfig { - return &ClientConfig{ +// DefaultConfig returns default config for the client.toml +func DefaultConfig() *Config { + return &Config{ ChainID: "", KeyringBackend: "os", Output: "text", @@ -18,7 +20,11 @@ func DefaultConfig() *ClientConfig { } } -type ClientConfig struct { +// ClientConfig is an alias for Config for backward compatibility +// Deprecated: use Config instead which avoid name stuttering +type ClientConfig Config + +type Config struct { ChainID string `mapstructure:"chain-id" json:"chain-id"` KeyringBackend string `mapstructure:"keyring-backend" json:"keyring-backend"` Output string `mapstructure:"output" json:"output"` @@ -26,31 +32,19 @@ type ClientConfig struct { BroadcastMode string `mapstructure:"broadcast-mode" json:"broadcast-mode"` } -func (c *ClientConfig) SetChainID(chainID string) { - c.ChainID = chainID -} - -func (c *ClientConfig) SetKeyringBackend(keyringBackend string) { - c.KeyringBackend = keyringBackend -} - -func (c *ClientConfig) SetOutput(output string) { - c.Output = output -} - -func (c *ClientConfig) SetNode(node string) { - c.Node = node -} - -func (c *ClientConfig) SetBroadcastMode(broadcastMode string) { - c.BroadcastMode = broadcastMode -} - -// ReadFromClientConfig reads values from client.toml file and updates them in client Context +// ReadFromClientConfig reads values from client.toml file and updates them in client.Context +// It uses CreateClientConfig internally with no custom template and custom config. func ReadFromClientConfig(ctx client.Context) (client.Context, error) { + return CreateClientConfig(ctx, "", nil) +} + +// CreateClientConfig reads the client.toml file and returns a new populated client.Context +// If the client.toml file does not exist, it creates one with default values. +// It takes a customClientTemplate and customConfig as input that can be used to overwrite the default config and enhance the client.toml file. +// The custom template/config must be both provided or be "" and nil. +func CreateClientConfig(ctx client.Context, customClientTemplate string, customConfig interface{}) (client.Context, error) { configPath := filepath.Join(ctx.HomeDir, "config") configFilePath := filepath.Join(configPath, "client.toml") - conf := DefaultConfig() // when config.toml does not exist create and init with default values if _, err := os.Stat(configFilePath); os.IsNotExist(err) { @@ -58,12 +52,38 @@ func ReadFromClientConfig(ctx client.Context) (client.Context, error) { return ctx, fmt.Errorf("couldn't make client config: %w", err) } - if ctx.ChainID != "" { - conf.ChainID = ctx.ChainID // chain-id will be written to the client.toml while initiating the chain. + if (customClientTemplate != "" && customConfig == nil) || (customClientTemplate == "" && customConfig != nil) { + return ctx, fmt.Errorf("customClientTemplate and customConfig should be both nil or not nil") } - if err := writeConfigToFile(configFilePath, conf); err != nil { - return ctx, fmt.Errorf("could not write client config to the file: %w", err) + if customClientTemplate != "" { + if err := setConfigTemplate(customClientTemplate); err != nil { + return ctx, fmt.Errorf("couldn't set client config template: %w", err) + } + + if ctx.ChainID != "" { + // chain-id will be written to the client.toml while initiating the chain. + ctx.Viper.Set(flags.FlagChainID, ctx.ChainID) + } + + if err = ctx.Viper.Unmarshal(&customConfig); err != nil { + return ctx, fmt.Errorf("failed to parse custom client config: %w", err) + } + + if err := writeConfigFile(configFilePath, customConfig); err != nil { + return ctx, fmt.Errorf("could not write client config to the file: %w", err) + } + + } else { + conf := DefaultConfig() + if ctx.ChainID != "" { + // chain-id will be written to the client.toml while initiating the chain. + conf.ChainID = ctx.ChainID + } + + if err := writeConfigFile(configFilePath, conf); err != nil { + return ctx, fmt.Errorf("could not write client config to the file: %w", err) + } } } @@ -71,7 +91,8 @@ func ReadFromClientConfig(ctx client.Context) (client.Context, error) { if err != nil { return ctx, fmt.Errorf("couldn't get client config: %w", err) } - // we need to update KeyringDir field on Client Context first cause it is used in NewKeyringFromBackend + + // we need to update KeyringDir field on client.Context first cause it is used in NewKeyringFromBackend ctx = ctx.WithOutputFormat(conf.Output). WithChainID(conf.ChainID). WithKeyringDir(ctx.HomeDir) @@ -81,17 +102,17 @@ func ReadFromClientConfig(ctx client.Context) (client.Context, error) { return ctx, fmt.Errorf("couldn't get keyring: %w", err) } - ctx = ctx.WithKeyring(keyring) - // https://github.com/cosmos/cosmos-sdk/issues/8986 client, err := client.NewClientFromNode(conf.Node) if err != nil { return ctx, fmt.Errorf("couldn't get client from nodeURI: %w", err) } - ctx = ctx.WithNodeURI(conf.Node). + ctx = ctx. + WithNodeURI(conf.Node). + WithBroadcastMode(conf.BroadcastMode). WithClient(client). - WithBroadcastMode(conf.BroadcastMode) + WithKeyring(keyring) return ctx, nil } diff --git a/client/config/config_test.go b/client/config/config_test.go index 0caf5d9777..d4cd84aaa9 100644 --- a/client/config/config_test.go +++ b/client/config/config_test.go @@ -17,16 +17,27 @@ import ( ) const ( + chainID = "test-chain" nodeEnv = "NODE" testNode1 = "http://localhost:1" testNode2 = "http://localhost:2" ) -// initClientContext initiates client Context for tests +// initClientContext initiates client.Context for tests func initClientContext(t *testing.T, envVar string) (client.Context, func()) { t.Helper() + + clientCtx, cleanup, err := initClientContextWithTemplate(t, envVar, "", nil) + require.NoError(t, err) + require.Equal(t, chainID, clientCtx.ChainID) + + return clientCtx, cleanup +} + +// initClientContextWithTemplate initiates client.Context with custom config and template for tests +func initClientContextWithTemplate(t *testing.T, envVar, customTemplate string, customConfig interface{}) (client.Context, func(), error) { + t.Helper() home := t.TempDir() - chainID := "test-chain" clientCtx := client.Context{}. WithHomeDir(home). WithViper(""). @@ -38,11 +49,89 @@ func initClientContext(t *testing.T, envVar string) (client.Context, func()) { require.NoError(t, os.Setenv(nodeEnv, envVar)) } - clientCtx, err := config.ReadFromClientConfig(clientCtx) - require.NoError(t, err) - require.Equal(t, clientCtx.ChainID, chainID) + clientCtx, err := config.CreateClientConfig(clientCtx, customTemplate, customConfig) + return clientCtx, func() { _ = os.RemoveAll(home) }, err +} - return clientCtx, func() { _ = os.RemoveAll(home) } +func TestCustomTemplateAndConfig(t *testing.T) { + type GasConfig struct { + GasAdjustment float64 `mapstructure:"gas-adjustment"` + } + + type CustomClientConfig struct { + config.Config `mapstructure:",squash"` + + GasConfig GasConfig `mapstructure:"gas"` + + Note string `mapstructure:"note"` + } + + clientCfg := config.DefaultConfig() + // Overwrite the default keyring backend. + clientCfg.KeyringBackend = "test" + + customClientConfig := CustomClientConfig{ + Config: *clientCfg, + GasConfig: GasConfig{ + GasAdjustment: 1.5, + }, + Note: "Sent from the CLI.", + } + + customClientConfigTemplate := config.DefaultClientConfigTemplate + ` +# This is the gas adjustment factor used by the tx commands. +# Sets the default and can be overwriten by the --gas-adjustment flag in tx commands. +gas-adjustment = {{ .GasConfig.GasAdjustment }} +# Memo to include in all transactions. +note = "{{ .Note }}" +` + + t.Run("custom template and config provided", func(t *testing.T) { + clientCtx, cleanup, err := initClientContextWithTemplate(t, "", customClientConfigTemplate, customClientConfig) + defer func() { + cleanup() + _ = os.Unsetenv(nodeEnv) + }() + + require.NoError(t, err) + require.Equal(t, customClientConfig.KeyringBackend, clientCtx.Viper.Get(flags.FlagKeyringBackend)) + require.Equal(t, customClientConfig.GasConfig.GasAdjustment, clientCtx.Viper.GetFloat64(flags.FlagGasAdjustment)) + require.Equal(t, customClientConfig.Note, clientCtx.Viper.GetString(flags.FlagNote)) + }) + + t.Run("no template and custom config provided", func(t *testing.T) { + _, cleanup, err := initClientContextWithTemplate(t, "", "", customClientConfig) + defer func() { + cleanup() + _ = os.Unsetenv(nodeEnv) + }() + + require.Error(t, err) + }) + + t.Run("default template and custom config provided", func(t *testing.T) { + clientCtx, cleanup, err := initClientContextWithTemplate(t, "", config.DefaultClientConfigTemplate, customClientConfig) + defer func() { + cleanup() + _ = os.Unsetenv(nodeEnv) + }() + + require.NoError(t, err) + require.Equal(t, customClientConfig.KeyringBackend, clientCtx.Viper.Get(flags.FlagKeyringBackend)) + require.Nil(t, clientCtx.Viper.Get(flags.FlagGasAdjustment)) // nil because we do not read the flags + }) + + t.Run("no template and no config provided", func(t *testing.T) { + clientCtx, cleanup, err := initClientContextWithTemplate(t, "", "", nil) + defer func() { + cleanup() + _ = os.Unsetenv(nodeEnv) + }() + + require.NoError(t, err) + require.Equal(t, config.DefaultConfig().KeyringBackend, clientCtx.Viper.Get(flags.FlagKeyringBackend)) + require.Nil(t, clientCtx.Viper.Get(flags.FlagGasAdjustment)) // nil because we do not read the flags + }) } func TestConfigCmdEnvFlag(t *testing.T) { @@ -79,6 +168,7 @@ func TestConfigCmdEnvFlag(t *testing.T) { cleanup() _ = os.Unsetenv(nodeEnv) }() + /* env var is set with a flag diff --git a/client/config/toml.go b/client/config/toml.go index b3ef23ee51..1028f449e1 100644 --- a/client/config/toml.go +++ b/client/config/toml.go @@ -8,11 +8,11 @@ import ( "github.com/spf13/viper" ) -const defaultConfigTemplate = `# This is a TOML config file. +const DefaultClientConfigTemplate = `# This is a TOML config file. # For more information, see https://github.com/toml-lang/toml ############################################################################### -### Client Configuration ### +### Client Configuration ### ############################################################################### # The network chain ID @@ -27,17 +27,33 @@ node = "{{ .Node }}" broadcast-mode = "{{ .BroadcastMode }}" ` -// writeConfigToFile parses defaultConfigTemplate, renders config using the template and writes it to -// configFilePath. -func writeConfigToFile(configFilePath string, config *ClientConfig) error { - var buffer bytes.Buffer +var configTemplate *template.Template + +func init() { + var err error tmpl := template.New("clientConfigFileTemplate") - configTemplate, err := tmpl.Parse(defaultConfigTemplate) - if err != nil { + if configTemplate, err = tmpl.Parse(DefaultClientConfigTemplate); err != nil { + panic(err) + } +} + +// setConfigTemplate sets the custom app config template for +// the application +func setConfigTemplate(customTemplate string) error { + tmpl := template.New("clientConfigFileTemplate") + var err error + if configTemplate, err = tmpl.Parse(customTemplate); err != nil { return err } + return nil +} + +// writeConfigFile renders config using the template and writes it to +// configFilePath. +func writeConfigFile(configFilePath string, config interface{}) error { + var buffer bytes.Buffer if err := configTemplate.Execute(&buffer, config); err != nil { return err } @@ -46,7 +62,7 @@ func writeConfigToFile(configFilePath string, config *ClientConfig) error { } // getClientConfig reads values from client.toml file and unmarshalls them into ClientConfig -func getClientConfig(configPath string, v *viper.Viper) (*ClientConfig, error) { +func getClientConfig(configPath string, v *viper.Viper) (*Config, error) { v.AddConfigPath(configPath) v.SetConfigName("client") v.SetConfigType("toml") diff --git a/client/tx/factory.go b/client/tx/factory.go index f30c8216c2..f38160218f 100644 --- a/client/tx/factory.go +++ b/client/tx/factory.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/go-bip39" "github.com/spf13/pflag" + "github.com/spf13/viper" "cosmossdk.io/math" @@ -50,6 +51,14 @@ type Factory struct { // NewFactoryCLI creates a new Factory. func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, error) { + if clientCtx.Viper == nil { + clientCtx.Viper = viper.New() + } + + if err := clientCtx.Viper.BindPFlags(flagSet); err != nil { + return Factory{}, fmt.Errorf("failed to bind flags to viper: %w", err) + } + signModeStr := clientCtx.SignModeStr signMode := signing.SignMode_SIGN_MODE_UNSPECIFIED @@ -69,18 +78,18 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e var accNum, accSeq uint64 if clientCtx.Offline { if flagSet.Changed(flags.FlagAccountNumber) && flagSet.Changed(flags.FlagSequence) { - accNum, _ = flagSet.GetUint64(flags.FlagAccountNumber) - accSeq, _ = flagSet.GetUint64(flags.FlagSequence) + accNum = clientCtx.Viper.GetUint64(flags.FlagAccountNumber) + accSeq = clientCtx.Viper.GetUint64(flags.FlagSequence) } else { return Factory{}, errors.New("account-number and sequence must be set in offline mode") } } - gasAdj, _ := flagSet.GetFloat64(flags.FlagGasAdjustment) - memo, _ := flagSet.GetString(flags.FlagNote) - timeoutHeight, _ := flagSet.GetUint64(flags.FlagTimeoutHeight) + gasAdj := clientCtx.Viper.GetFloat64(flags.FlagGasAdjustment) + memo := clientCtx.Viper.GetString(flags.FlagNote) + timeoutHeight := clientCtx.Viper.GetUint64(flags.FlagTimeoutHeight) - gasStr, _ := flagSet.GetString(flags.FlagGas) + gasStr := clientCtx.Viper.GetString(flags.FlagGas) gasSetting, _ := flags.ParseGasSetting(gasStr) f := Factory{ @@ -102,15 +111,15 @@ func NewFactoryCLI(clientCtx client.Context, flagSet *pflag.FlagSet) (Factory, e feePayer: clientCtx.FeePayer, } - feesStr, _ := flagSet.GetString(flags.FlagFees) + feesStr := clientCtx.Viper.GetString(flags.FlagFees) f = f.WithFees(feesStr) - tipsStr, _ := flagSet.GetString(flags.FlagTip) + tipsStr := clientCtx.Viper.GetString(flags.FlagTip) // Add tips to factory. The tipper is necessarily the Msg signer, i.e. // the from address. f = f.WithTips(tipsStr, clientCtx.FromAddress.String()) - gasPricesStr, _ := flagSet.GetString(flags.FlagGasPrices) + gasPricesStr := clientCtx.Viper.GetString(flags.FlagGasPrices) f = f.WithGasPrices(gasPricesStr) f = f.WithPreprocessTxHook(clientCtx.PreprocessTxHook) diff --git a/docs/docs/core/07-cli.md b/docs/docs/core/07-cli.md index 5d2be5c03c..0dfaeb05a6 100644 --- a/docs/docs/core/07-cli.md +++ b/docs/docs/core/07-cli.md @@ -76,7 +76,6 @@ Read more about [AutoCLI](https://docs.cosmos.network/main/core/autocli) in its ::: `rootCmd` has a function called `initAppConfig()` which is useful for setting the application's custom configs. -By default app uses CometBFT app config template from Cosmos SDK, which can be over-written via `initAppConfig()`. Here's an example code to override default `app.toml` template. ```go reference @@ -89,6 +88,26 @@ The `initAppConfig()` also allows overriding the default Cosmos SDK's [server co https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/simapp/simd/cmd/root_v2.go#L164-L180 ``` +By default the app uses CometBFT app config template from Cosmos SDK, which can also be over-written via `initCometBFTConfig()`. + +```go reference +https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-alpha.0/simapp/simd/cmd/root_v2.go#L132-L142 +``` + +Those custom templates and config must be provided to the `server.InterceptConfigsPreRunHandler` command in the `PersistentPreRunE` function of the root command. See [configuration](#configurations) section for more details. + +Additionally, like the `app.toml` and `config.toml`, the `client.toml` config can be extended or over-written by the user thanks to the `client.CreateClientConfig` function. This is useful for setting default values for the client without having to pass a flag. For example, the Cosmos SDK sets the default `keyring-backend` to `os` but the chain developer might instead want to always set it to `file` by default. + +```go reference +https://github.com/cosmos/cosmos-sdk/blob/bb23e920676096b9fd2d2196daec389ad7f8192e/simapp/simd/cmd/root_v2.go#L78-L82 +``` + +Creating the custom template can be done in a `initClientConfig()` function. + +```go reference +https://github.com/cosmos/cosmos-sdk/blob/bb23e920676096b9fd2d2196daec389ad7f8192e/simapp/simd/cmd/config.go#L24-L64 +``` + The root-level `status` and `keys` subcommands are common across most applications and do not interact with application state. The bulk of an application's functionality - what users can actually *do* with it - is enabled by its `tx` and `query` commands. ### Transaction Commands diff --git a/server/config/config_test.go b/server/config/config_test.go index c15186e73a..a8600a80b3 100644 --- a/server/config/config_test.go +++ b/server/config/config_test.go @@ -49,7 +49,8 @@ func TestStreamingConfig(t *testing.T) { testDir := t.TempDir() cfgFile := filepath.Join(testDir, "app.toml") - WriteConfigFile(cfgFile, &cfg) + err := WriteConfigFile(cfgFile, &cfg) + require.NoError(t, err) cfgFileBz, err := os.ReadFile(cfgFile) require.NoError(t, err, "reading %s", cfgFile) @@ -100,7 +101,8 @@ func TestParseStreaming(t *testing.T) { func TestReadConfig(t *testing.T) { cfg := DefaultConfig() tmpFile := filepath.Join(t.TempDir(), "config") - WriteConfigFile(tmpFile, cfg) + err := WriteConfigFile(tmpFile, cfg) + require.NoError(t, err) v := viper.New() otherCfg, err := GetConfig(v) @@ -117,13 +119,14 @@ func TestIndexEventsWriteRead(t *testing.T) { conf := DefaultConfig() conf.IndexEvents = expected - WriteConfigFile(confFile, conf) + err := WriteConfigFile(confFile, conf) + require.NoError(t, err) // read the file into Viper vpr := viper.New() vpr.SetConfigFile(confFile) - err := vpr.ReadInConfig() + err = vpr.ReadInConfig() require.NoError(t, err, "reading config file into viper") // Check that the raw viper value is correct. @@ -168,7 +171,8 @@ func TestGlobalLabelsWriteRead(t *testing.T) { confFile := filepath.Join(t.TempDir(), "app.toml") conf := DefaultConfig() conf.Telemetry.GlobalLabels = expected - WriteConfigFile(confFile, conf) + err := WriteConfigFile(confFile, conf) + require.NoError(t, err) // Read that file into viper. vpr := viper.New() @@ -197,7 +201,7 @@ func TestSetConfigTemplate(t *testing.T) { // Set the template to the default one. initTmpl := configTemplate require.NotPanics(t, func() { - SetConfigTemplate(DefaultConfigTemplate) + _ = SetConfigTemplate(DefaultConfigTemplate) }, "SetConfigTemplate") setTmpl := configTemplate require.NotSame(t, initTmpl, setTmpl, "configTemplate after set") diff --git a/server/config/toml.go b/server/config/toml.go index 8c0a615cd9..6490f8d537 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -243,14 +243,12 @@ func init() { var err error tmpl := template.New("appConfigFileTemplate") - if configTemplate, err = tmpl.Parse(DefaultConfigTemplate); err != nil { panic(err) } } -// ParseConfig retrieves the default environment configuration for the -// application. +// ParseConfig retrieves the default environment configuration for the application. func ParseConfig(v *viper.Viper) (*Config, error) { conf := DefaultConfig() err := v.Unmarshal(conf) @@ -258,32 +256,30 @@ func ParseConfig(v *viper.Viper) (*Config, error) { return conf, err } -// SetConfigTemplate sets the custom app config template for -// the application -func SetConfigTemplate(customTemplate string) { +// SetConfigTemplate sets the custom app config template for the application. +func SetConfigTemplate(customTemplate string) error { var err error tmpl := template.New("appConfigFileTemplate") if configTemplate, err = tmpl.Parse(customTemplate); err != nil { - panic(err) + return err } + + return nil } -// WriteConfigFile renders config using the template and writes it to -// configFilePath. -func WriteConfigFile(configFilePath string, config interface{}) { +// WriteConfigFile renders config using the template and writes it to configFilePath. +func WriteConfigFile(configFilePath string, config interface{}) error { var buffer bytes.Buffer if err := configTemplate.Execute(&buffer, config); err != nil { - panic(err) + return err } - mustWriteFile(configFilePath, buffer.Bytes(), 0o644) -} - -func mustWriteFile(filePath string, contents []byte, mode os.FileMode) { - if err := os.WriteFile(filePath, contents, mode); err != nil { - panic(fmt.Errorf("failed to write file: %w", err)) + if err := os.WriteFile(configFilePath, buffer.Bytes(), 0o600); err != nil { + return fmt.Errorf("failed to write file: %w", err) } + + return nil } diff --git a/server/util.go b/server/util.go index 521a61d410..dae0a21abb 100644 --- a/server/util.go +++ b/server/util.go @@ -281,21 +281,31 @@ func interceptConfigs(rootViper *viper.Viper, customAppTemplate string, customCo appCfgFilePath := filepath.Join(configPath, "app.toml") if _, err := os.Stat(appCfgFilePath); os.IsNotExist(err) { + if (customAppTemplate != "" && customConfig == nil) || (customAppTemplate == "" && customConfig != nil) { + return nil, fmt.Errorf("customAppTemplate and customConfig should be both nil or not nil") + } + if customAppTemplate != "" { - config.SetConfigTemplate(customAppTemplate) + if err := config.SetConfigTemplate(customAppTemplate); err != nil { + return nil, fmt.Errorf("failed to set config template: %w", err) + } if err = rootViper.Unmarshal(&customConfig); err != nil { return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err) } - config.WriteConfigFile(appCfgFilePath, customConfig) + if err := config.WriteConfigFile(appCfgFilePath, customConfig); err != nil { + return nil, fmt.Errorf("failed to write %s: %w", appCfgFilePath, err) + } } else { appConf, err := config.ParseConfig(rootViper) if err != nil { return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err) } - config.WriteConfigFile(appCfgFilePath, appConf) + if err := config.WriteConfigFile(appCfgFilePath, appConf); err != nil { + return nil, fmt.Errorf("failed to write %s: %w", appCfgFilePath, err) + } } } diff --git a/server/util_test.go b/server/util_test.go index 1ca5c0dab9..7304c146de 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -449,7 +449,8 @@ func TestEmptyMinGasPrices(t *testing.T) { appCfgTempFilePath := filepath.Join(tempDir, "config", "app.toml") appConf := config.DefaultConfig() appConf.BaseConfig.MinGasPrices = "" - config.WriteConfigFile(appCfgTempFilePath, appConf) + err = config.WriteConfigFile(appCfgTempFilePath, appConf) + require.NoError(t, err) // Run StartCmd. cmd = server.StartCmd(nil) diff --git a/simapp/simd/cmd/commands.go b/simapp/simd/cmd/commands.go index 5f0294fd9d..42208c3595 100644 --- a/simapp/simd/cmd/commands.go +++ b/simapp/simd/cmd/commands.go @@ -5,7 +5,6 @@ import ( "io" "os" - cmtcfg "github.com/cometbft/cometbft/config" dbm "github.com/cosmos/cosmos-db" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -24,7 +23,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/server" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" @@ -34,75 +32,6 @@ import ( genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" ) -// initCometBFTConfig helps to override default CometBFT Config values. -// return cmtcfg.DefaultConfig if no custom configuration is required for the application. -func initCometBFTConfig() *cmtcfg.Config { - cfg := cmtcfg.DefaultConfig() - - // these values put a higher strain on node memory - // cfg.P2P.MaxNumInboundPeers = 100 - // cfg.P2P.MaxNumOutboundPeers = 40 - - return cfg -} - -// initAppConfig helps to override default appConfig template and configs. -// return "", nil if no custom configuration is required for the application. -func initAppConfig() (string, interface{}) { - // The following code snippet is just for reference. - - // WASMConfig defines configuration for the wasm module. - type WASMConfig struct { - // This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries - QueryGasLimit uint64 `mapstructure:"query_gas_limit"` - - // Address defines the gRPC-web server to listen on - LruSize uint64 `mapstructure:"lru_size"` - } - - type CustomAppConfig struct { - serverconfig.Config `mapstructure:",squash"` - - WASM WASMConfig `mapstructure:"wasm"` - } - - // Optionally allow the chain developer to overwrite the SDK's default - // server config. - srvCfg := serverconfig.DefaultConfig() - // The SDK's default minimum gas price is set to "" (empty value) inside - // app.toml. If left empty by validators, the node will halt on startup. - // However, the chain developer can set a default app.toml value for their - // validators here. - // - // In summary: - // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their - // own app.toml config, - // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their - // own app.toml to override, or use this default value. - // - // In simapp, we set the min gas prices to 0. - srvCfg.MinGasPrices = "0stake" - // srvCfg.BaseConfig.IAVLDisableFastNode = true // disable fastnode by default - - customAppConfig := CustomAppConfig{ - Config: *srvCfg, - WASM: WASMConfig{ - LruSize: 1, - QueryGasLimit: 300000, - }, - } - - customAppTemplate := serverconfig.DefaultConfigTemplate + ` -[wasm] -# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries -query_gas_limit = 300000 -# This is the number of wasm vm instances we keep cached in memory for speed-up -# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally -lru_size = 0` - - return customAppTemplate, customAppConfig -} - func initRootCmd( rootCmd *cobra.Command, txConfig client.TxConfig, diff --git a/simapp/simd/cmd/config.go b/simapp/simd/cmd/config.go new file mode 100644 index 0000000000..68ce3a498f --- /dev/null +++ b/simapp/simd/cmd/config.go @@ -0,0 +1,127 @@ +package cmd + +import ( + "strings" + + cmtcfg "github.com/cometbft/cometbft/config" + + clientconfig "github.com/cosmos/cosmos-sdk/client/config" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" +) + +// initCometBFTConfig helps to override default CometBFT Config values. +// return cmtcfg.DefaultConfig if no custom configuration is required for the application. +func initCometBFTConfig() *cmtcfg.Config { + cfg := cmtcfg.DefaultConfig() + + // these values put a higher strain on node memory + // cfg.P2P.MaxNumInboundPeers = 100 + // cfg.P2P.MaxNumOutboundPeers = 40 + + return cfg +} + +// initAppConfig helps to override default client config template and configs. +// return "", nil if no custom configuration is required for the application. +func initClientConfig() (string, interface{}) { + type GasConfig struct { + GasAdjustment float64 `mapstructure:"gas-adjustment"` + } + + type CustomClientConfig struct { + clientconfig.Config `mapstructure:",squash"` + + GasConfig GasConfig `mapstructure:"gas"` + } + + // Optionally allow the chain developer to overwrite the SDK's default client config. + clientCfg := clientconfig.DefaultConfig() + + // The SDK's default keyring backend is set to "os". + // This is more secure than "test" and is the recommended value. + // + // In simapp, we set the default keyring backend to test, as SimApp is meant + // to be an example and testing application. + clientCfg.KeyringBackend = keyring.BackendTest + + // Now we set the custom config default values. + customClientConfig := CustomClientConfig{ + Config: *clientCfg, + GasConfig: GasConfig{ + GasAdjustment: 1.5, + }, + } + + // The default SDK app template is defined in serverconfig.DefaultConfigTemplate. + // We append the custom config template to the default one. + // And we set the default config to the custom app template. + customClientConfigTemplate := clientconfig.DefaultClientConfigTemplate + strings.TrimSpace(` +# This is default the gas adjustment factor used in tx commands. +# It can be overwriten by the --gas-adjustment flag in each tx command. +gas-adjustment = {{ .GasConfig.GasAdjustment }} +`) + + return customClientConfigTemplate, customClientConfig +} + +// initAppConfig helps to override default appConfig template and configs. +// return "", nil if no custom configuration is required for the application. +func initAppConfig() (string, interface{}) { + // The following code snippet is just for reference. + + // WASMConfig defines configuration for the wasm module. + type WASMConfig struct { + // This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries + QueryGasLimit uint64 `mapstructure:"query-gas-limit"` + + // Address defines the gRPC-web server to listen on + LruSize uint64 `mapstructure:"lru-size"` + } + + type CustomAppConfig struct { + serverconfig.Config `mapstructure:",squash"` + + WASM WASMConfig `mapstructure:"wasm"` + } + + // Optionally allow the chain developer to overwrite the SDK's default + // server config. + srvCfg := serverconfig.DefaultConfig() + // The SDK's default minimum gas price is set to "" (empty value) inside + // app.toml. If left empty by validators, the node will halt on startup. + // However, the chain developer can set a default app.toml value for their + // validators here. + // + // In summary: + // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their + // own app.toml config, + // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their + // own app.toml to override, or use this default value. + // + // In simapp, we set the min gas prices to 0. + srvCfg.MinGasPrices = "0stake" + // srvCfg.BaseConfig.IAVLDisableFastNode = true // disable fastnode by default + + // Now we set the custom config default values. + customAppConfig := CustomAppConfig{ + Config: *srvCfg, + WASM: WASMConfig{ + LruSize: 1, + QueryGasLimit: 300000, + }, + } + + // The default SDK app template is defined in serverconfig.DefaultConfigTemplate. + // We append the custom config template to the default one. + // And we set the default config to the custom app template. + customAppTemplate := serverconfig.DefaultConfigTemplate + ` +[wasm] +# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries +query-gas-limit = {{ .WASM.QueryGasLimit }} +# This is the number of wasm vm instances we keep cached in memory for speed-up +# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally +lru-size = {{ .WASM.LruSize }}` + + return customAppTemplate, customAppConfig +} diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index d392c896e6..a9c621d549 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -64,12 +64,13 @@ func NewRootCmd() *cobra.Command { return err } - initClientCtx, err = config.ReadFromClientConfig(initClientCtx) + customClientTemplate, customClientConfig := initClientConfig() + initClientCtx, err = config.CreateClientConfig(initClientCtx, customClientTemplate, customClientConfig) if err != nil { return err } - // This needs to go after ReadFromClientConfig, as that function + // This needs to go after CreateClientConfig, as that function // sets the RPC client needed for SIGN_MODE_TEXTUAL. enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL) txConfigOpts := tx.ConfigOptions{ @@ -99,7 +100,12 @@ func NewRootCmd() *cobra.Command { initRootCmd(rootCmd, encodingConfig.TxConfig, encodingConfig.InterfaceRegistry, encodingConfig.Codec, tempApp.BasicModuleManager) // autocli opts - initClientCtx, _ = config.ReadFromClientConfig(initClientCtx) + customClientTemplate, customClientConfig := initClientConfig() + var err error + initClientCtx, err = config.CreateClientConfig(initClientCtx, customClientTemplate, customClientConfig) + if err != nil { + panic(err) + } autoCliOpts := tempApp.AutoCliOpts() autoCliOpts.Keyring = initClientCtx.Keyring diff --git a/simapp/simd/cmd/root_v2.go b/simapp/simd/cmd/root_v2.go index 4cdd6b1c76..aa882aec5a 100644 --- a/simapp/simd/cmd/root_v2.go +++ b/simapp/simd/cmd/root_v2.go @@ -75,12 +75,13 @@ func NewRootCmd() *cobra.Command { return err } - initClientCtx, err = config.ReadFromClientConfig(initClientCtx) + customClientTemplate, customClientConfig := initClientConfig() + initClientCtx, err = config.CreateClientConfig(initClientCtx, customClientTemplate, customClientConfig) if err != nil { return err } - // This needs to go after ReadFromClientConfig, as that function + // This needs to go after CreateClientConfig, as that function // sets the RPC client needed for SIGN_MODE_TEXTUAL. enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL) txConfigOpts := tx.ConfigOptions{ @@ -123,6 +124,8 @@ func ProvideClientContext( validatorAddressCodec runtime.ValidatorAddressCodec, consensusAddressCodec runtime.ConsensusAddressCodec, ) client.Context { + var err error + initClientCtx := client.Context{}. WithCodec(appCodec). WithInterfaceRegistry(interfaceRegistry). @@ -135,8 +138,12 @@ func ProvideClientContext( WithHomeDir(simapp.DefaultNodeHome). WithViper("") // In simapp, we don't use any prefix for env variables. - // Read the config again to overwrite the default values with the values from the config file - initClientCtx, _ = config.ReadFromClientConfig(initClientCtx) + // Read the config to overwrite the default values with the values from the config file + customClientTemplate, customClientConfig := initClientConfig() + initClientCtx, err = config.CreateClientConfig(initClientCtx, customClientTemplate, customClientConfig) + if err != nil { + panic(err) + } return initClientCtx } diff --git a/simapp/simd/cmd/testnet.go b/simapp/simd/cmd/testnet.go index 1586905b1b..707104d63c 100644 --- a/simapp/simd/cmd/testnet.go +++ b/simapp/simd/cmd/testnet.go @@ -345,7 +345,13 @@ func initTestnetFiles( return err } - srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), simappConfig) + if err := srvconfig.SetConfigTemplate(srvconfig.DefaultConfigTemplate); err != nil { + return err + } + + if err := srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), simappConfig); err != nil { + return err + } } if err := initGenFiles(clientCtx, mbm, args.chainID, genAccounts, genBalances, genFiles, args.numValidators); err != nil { diff --git a/testutil/network/network.go b/testutil/network/network.go index 28df0d634b..eb0c8ead0d 100644 --- a/testutil/network/network.go +++ b/testutil/network/network.go @@ -588,7 +588,10 @@ func New(l Logger, baseDir string, cfg Config) (*Network, error) { if err != nil { return nil, err } - srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appCfg) + err = srvconfig.WriteConfigFile(filepath.Join(nodeDir, "config", "app.toml"), appCfg) + if err != nil { + return nil, err + } clientCtx := client.Context{}. WithKeyringDir(clientDir).