200 lines
5.7 KiB
Go
200 lines
5.7 KiB
Go
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,
|
|
)
|
|
}
|