feat(client): allow overwritting client.toml (#17513)
This commit is contained in:
parent
6ed81a737c
commit
6601713eb6
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
127
simapp/simd/cmd/config.go
Normal file
127
simapp/simd/cmd/config.go
Normal file
@ -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
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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).
|
||||
|
||||
Loading…
Reference in New Issue
Block a user