package server import ( "fmt" "io" "os" "path" "path/filepath" "strings" "time" "github.com/rs/zerolog" "github.com/spf13/cobra" "github.com/spf13/viper" tmcfg "github.com/tendermint/tendermint/config" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/client/flags" sdkserver "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/ethermint/server/config" ) // InterceptConfigsPreRunHandler performs a pre-run function for the root daemon // application command. It will create a Viper literal and a default server // Context. The server Tendermint configuration will either be read and parsed // or created and saved to disk, where the server Context is updated to reflect // the Tendermint configuration. The Viper literal is used to read and parse // the application configuration. Command handlers can fetch the server Context // to get the Tendermint configuration or to get access to Viper. func InterceptConfigsPreRunHandler(cmd *cobra.Command) error { serverCtx := sdkserver.NewDefaultContext() // Get the executable name and configure the viper instance so that environmental // variables are checked based off that name. The underscore character is used // as a separator executableName, err := os.Executable() if err != nil { return err } basename := path.Base(executableName) // Configure the viper instance if err := serverCtx.Viper.BindPFlags(cmd.Flags()); err != nil { return err } if err := serverCtx.Viper.BindPFlags(cmd.PersistentFlags()); err != nil { return err } serverCtx.Viper.SetEnvPrefix(basename) serverCtx.Viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) serverCtx.Viper.AutomaticEnv() // intercept configuration files, using both Viper instances separately config, err := interceptConfigs(serverCtx.Viper) if err != nil { return err } // return value is a tendermint configuration object serverCtx.Config = config var logWriter io.Writer if strings.ToLower(serverCtx.Viper.GetString(flags.FlagLogFormat)) == tmcfg.LogFormatPlain { logWriter = zerolog.ConsoleWriter{Out: os.Stderr} } else { logWriter = os.Stderr } logLvlStr := serverCtx.Viper.GetString(flags.FlagLogLevel) logLvl, err := zerolog.ParseLevel(logLvlStr) if err != nil { return fmt.Errorf("failed to parse log level (%s): %w", logLvlStr, err) } serverCtx.Logger = sdkserver.ZeroLogWrapper{ Logger: zerolog.New(logWriter).Level(logLvl).With().Timestamp().Logger(), } return sdkserver.SetCmdServerContext(cmd, serverCtx) } // interceptConfigs parses and updates a Tendermint configuration file or // creates a new one and saves it. It also parses and saves the application // configuration file. The Tendermint configuration file is parsed given a root // Viper object, whereas the application is parsed with the private package-aware // viperCfg object. func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) { rootDir := rootViper.GetString(flags.FlagHome) configPath := filepath.Join(rootDir, "config") configFile := filepath.Join(configPath, "config.toml") conf := tmcfg.DefaultConfig() switch _, err := os.Stat(configFile); { case os.IsNotExist(err): tmcfg.EnsureRoot(rootDir) if err = conf.ValidateBasic(); err != nil { return nil, fmt.Errorf("error in config file: %v", err) } conf.RPC.PprofListenAddress = "localhost:6060" conf.P2P.RecvRate = 5120000 conf.P2P.SendRate = 5120000 conf.Consensus.TimeoutCommit = 5 * time.Second tmcfg.WriteConfigFile(configFile, conf) case err != nil: return nil, err default: rootViper.SetConfigType("toml") rootViper.SetConfigName("config") rootViper.AddConfigPath(configPath) if err := rootViper.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read in app.toml: %w", err) } } // Read into the configuration whatever data the viper instance has for it // This may come from the configuration file above but also any of the other sources // viper uses if err := rootViper.Unmarshal(conf); err != nil { return nil, err } conf.SetRoot(rootDir) appConfigFilePath := filepath.Join(configPath, "app.toml") if _, err := os.Stat(appConfigFilePath); os.IsNotExist(err) { appConf, err := config.ParseConfig(rootViper) if err != nil { return nil, fmt.Errorf("failed to parse app.toml: %w", err) } config.WriteConfigFile(appConfigFilePath, appConf) } rootViper.SetConfigType("toml") rootViper.SetConfigName("app") rootViper.AddConfigPath(configPath) if err := rootViper.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read in app.toml: %w", err) } return conf, nil } // AddCommands adds the server commands func AddCommands( rootCmd *cobra.Command, defaultNodeHome string, appCreator servertypes.AppCreator, appExport servertypes.AppExporter, addStartFlags servertypes.ModuleInitFlags, ) { tendermintCmd := &cobra.Command{ Use: "tendermint", Short: "Tendermint subcommands", } tendermintCmd.AddCommand( sdkserver.ShowNodeIDCmd(), sdkserver.ShowValidatorCmd(), sdkserver.ShowAddressCmd(), sdkserver.VersionCmd(), ) startCmd := StartCmd(appCreator, defaultNodeHome) addStartFlags(startCmd) rootCmd.AddCommand( startCmd, sdkserver.UnsafeResetAllCmd(), flags.LineBreak, tendermintCmd, sdkserver.ExportCmd(appExport, defaultNodeHome), flags.LineBreak, version.NewVersionCommand(), ) } func openDB(rootDir string) (dbm.DB, error) { dataDir := filepath.Join(rootDir, "data") return sdk.NewLevelDB("application", dataDir) } func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { if traceWriterFile == "" { return } return os.OpenFile( traceWriterFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666, ) }