From a4e2f05d7ab651bb58a04adec4dbe4b56ce0e02e Mon Sep 17 00:00:00 2001 From: Sam Kleinman Date: Mon, 31 Jan 2022 09:52:30 -0500 Subject: [PATCH] cmd: avoid package state in cli constructors (#7719) --- abci/cmd/abci-cli/abci-cli.go | 203 ++++----- cmd/priv_val_server/main.go | 10 +- cmd/tendermint/commands/gen_validator.go | 46 +- cmd/tendermint/commands/init.go | 65 ++- cmd/tendermint/commands/inspect.go | 61 ++- cmd/tendermint/commands/key_migrate.go | 7 +- cmd/tendermint/commands/light.go | 335 +++++++------- cmd/tendermint/commands/reindex_event.go | 144 +++--- cmd/tendermint/commands/reindex_event_test.go | 32 +- cmd/tendermint/commands/replay.go | 35 +- .../commands/reset_priv_validator.go | 61 +-- cmd/tendermint/commands/rollback.go | 33 +- cmd/tendermint/commands/root.go | 77 ++-- cmd/tendermint/commands/root_test.go | 117 ++--- cmd/tendermint/commands/run_node.go | 57 +-- cmd/tendermint/commands/show_node_id.go | 27 +- cmd/tendermint/commands/show_validator.go | 109 ++--- cmd/tendermint/commands/testnet.go | 418 ++++++++++-------- cmd/tendermint/main.go | 62 +-- docs/tutorials/go.md | 6 +- libs/cli/setup.go | 10 +- libs/log/default_test.go | 3 - 22 files changed, 1008 insertions(+), 910 deletions(-) diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index 805dc887e..cffbadfb7 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -28,7 +28,6 @@ import ( // client is a global variable so it can be reused by the console var ( client abciclient.Client - logger log.Logger ) // flags @@ -48,34 +47,32 @@ var ( flagPersist string ) -var RootCmd = &cobra.Command{ - Use: "abci-cli", - Short: "the ABCI CLI tool wraps an ABCI client", - Long: "the ABCI CLI tool wraps an ABCI client and is used for testing ABCI servers", - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { +func RootCmmand(logger log.Logger) *cobra.Command { + return &cobra.Command{ + Use: "abci-cli", + Short: "the ABCI CLI tool wraps an ABCI client", + Long: "the ABCI CLI tool wraps an ABCI client and is used for testing ABCI servers", + PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { - switch cmd.Use { - case "kvstore", "version": - return nil - } - - if logger == nil { - logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo) - } - - if client == nil { - var err error - client, err = abciclient.NewClient(logger.With("module", "abci-client"), flagAddress, flagAbci, false) - if err != nil { - return err + switch cmd.Use { + case "kvstore", "version": + return nil } - if err := client.Start(cmd.Context()); err != nil { - return err + if client == nil { + var err error + client, err = abciclient.NewClient(logger.With("module", "abci-client"), flagAddress, flagAbci, false) + if err != nil { + return err + } + + if err := client.Start(cmd.Context()); err != nil { + return err + } } - } - return nil - }, + return nil + }, + } } // Structure for data passed to print response. @@ -97,56 +94,46 @@ type queryResponse struct { } func Execute() error { - addGlobalFlags() - addCommands() - return RootCmd.Execute() + logger, err := log.NewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo) + if err != nil { + return err + } + + cmd := RootCmmand(logger) + addGlobalFlags(cmd) + addCommands(cmd, logger) + return cmd.Execute() } -func addGlobalFlags() { - RootCmd.PersistentFlags().StringVarP(&flagAddress, +func addGlobalFlags(cmd *cobra.Command) { + cmd.PersistentFlags().StringVarP(&flagAddress, "address", "", "tcp://0.0.0.0:26658", "address of application socket") - RootCmd.PersistentFlags().StringVarP(&flagAbci, "abci", "", "socket", "either socket or grpc") - RootCmd.PersistentFlags().BoolVarP(&flagVerbose, + cmd.PersistentFlags().StringVarP(&flagAbci, "abci", "", "socket", "either socket or grpc") + cmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "print the command and results as if it were a console session") - RootCmd.PersistentFlags().StringVarP(&flagLogLevel, "log_level", "", "debug", "set the logger level") + cmd.PersistentFlags().StringVarP(&flagLogLevel, "log_level", "", "debug", "set the logger level") } -func addQueryFlags() { - queryCmd.PersistentFlags().StringVarP(&flagPath, "path", "", "/store", "path to prefix query with") - queryCmd.PersistentFlags().IntVarP(&flagHeight, "height", "", 0, "height to query the blockchain at") - queryCmd.PersistentFlags().BoolVarP(&flagProve, - "prove", - "", - false, - "whether or not to return a merkle proof of the query result") -} - -func addKVStoreFlags() { - kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") -} - -func addCommands() { - RootCmd.AddCommand(batchCmd) - RootCmd.AddCommand(consoleCmd) - RootCmd.AddCommand(echoCmd) - RootCmd.AddCommand(infoCmd) - RootCmd.AddCommand(deliverTxCmd) - RootCmd.AddCommand(checkTxCmd) - RootCmd.AddCommand(commitCmd) - RootCmd.AddCommand(versionCmd) - RootCmd.AddCommand(testCmd) - addQueryFlags() - RootCmd.AddCommand(queryCmd) +func addCommands(cmd *cobra.Command, logger log.Logger) { + cmd.AddCommand(batchCmd) + cmd.AddCommand(consoleCmd) + cmd.AddCommand(echoCmd) + cmd.AddCommand(infoCmd) + cmd.AddCommand(deliverTxCmd) + cmd.AddCommand(checkTxCmd) + cmd.AddCommand(commitCmd) + cmd.AddCommand(versionCmd) + cmd.AddCommand(testCmd) + cmd.AddCommand(getQueryCmd()) // examples - addKVStoreFlags() - RootCmd.AddCommand(kvstoreCmd) + cmd.AddCommand(getKVStoreCmd(logger)) } var batchCmd = &cobra.Command{ @@ -236,20 +223,38 @@ var versionCmd = &cobra.Command{ }, } -var queryCmd = &cobra.Command{ - Use: "query", - Short: "query the application state", - Long: "query the application state", - Args: cobra.ExactArgs(1), - RunE: cmdQuery, +func getQueryCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "query", + Short: "query the application state", + Long: "query the application state", + Args: cobra.ExactArgs(1), + RunE: cmdQuery, + } + + cmd.PersistentFlags().StringVarP(&flagPath, "path", "", "/store", "path to prefix query with") + cmd.PersistentFlags().IntVarP(&flagHeight, "height", "", 0, "height to query the blockchain at") + cmd.PersistentFlags().BoolVarP(&flagProve, + "prove", + "", + false, + "whether or not to return a merkle proof of the query result") + + return cmd } -var kvstoreCmd = &cobra.Command{ - Use: "kvstore", - Short: "ABCI demo example", - Long: "ABCI demo example", - Args: cobra.ExactArgs(0), - RunE: cmdKVStore, +func getKVStoreCmd(logger log.Logger) *cobra.Command { + cmd := &cobra.Command{ + Use: "kvstore", + Short: "ABCI demo example", + Long: "ABCI demo example", + Args: cobra.ExactArgs(0), + RunE: makeKVStoreCmd(logger), + } + + cmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") + return cmd + } var testCmd = &cobra.Command{ @@ -425,12 +430,9 @@ func cmdUnimplemented(cmd *cobra.Command, args []string) error { }) fmt.Println("Available commands:") - fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short) - fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short) - fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short) - fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short) - fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short) - fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short) + for _, cmd := range cmd.Commands() { + fmt.Printf("%s: %s\n", cmd.Use, cmd.Short) + } fmt.Println("Use \"[command] --help\" for more information about a command.") return nil @@ -574,33 +576,34 @@ func cmdQuery(cmd *cobra.Command, args []string) error { return nil } -func cmdKVStore(cmd *cobra.Command, args []string) error { - logger := log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo) +func makeKVStoreCmd(logger log.Logger) func(*cobra.Command, []string) error { + return func(cmd *cobra.Command, args []string) error { + // Create the application - in memory or persisted to disk + var app types.Application + if flagPersist == "" { + app = kvstore.NewApplication() + } else { + app = kvstore.NewPersistentKVStoreApplication(logger, flagPersist) + } - // Create the application - in memory or persisted to disk - var app types.Application - if flagPersist == "" { - app = kvstore.NewApplication() - } else { - app = kvstore.NewPersistentKVStoreApplication(logger, flagPersist) - } + // Start the listener + srv, err := server.NewServer(logger.With("module", "abci-server"), flagAddress, flagAbci, app) + if err != nil { + return err + } - // Start the listener - srv, err := server.NewServer(logger.With("module", "abci-server"), flagAddress, flagAbci, app) - if err != nil { - return err - } + ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM) + defer cancel() - ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM) - defer cancel() + if err := srv.Start(ctx); err != nil { + return err + } - if err := srv.Start(ctx); err != nil { - return err + // Run forever. + <-ctx.Done() + return nil } - // Run forever. - <-ctx.Done() - return nil } //-------------------------------------------------------------------------------- diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index bdd5091d9..e34236acc 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -45,12 +45,16 @@ func main() { keyFile = flag.String("keyfile", "", "absolute path to server key") rootCA = flag.String("rootcafile", "", "absolute path to root CA") prometheusAddr = flag.String("prometheus-addr", "", "address for prometheus endpoint (host:port)") - - logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo). - With("module", "priv_val") ) flag.Parse() + logger, err := log.NewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to construct logger: %v", err) + os.Exit(1) + } + logger = logger.With("module", "priv_val") + ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/cmd/tendermint/commands/gen_validator.go b/cmd/tendermint/commands/gen_validator.go index 450efad85..ac37790cc 100644 --- a/cmd/tendermint/commands/gen_validator.go +++ b/cmd/tendermint/commands/gen_validator.go @@ -12,30 +12,30 @@ import ( // GenValidatorCmd allows the generation of a keypair for a // validator. -var GenValidatorCmd = &cobra.Command{ - Use: "gen-validator", - Short: "Generate new validator keypair", - RunE: genValidator, -} - -func init() { - GenValidatorCmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, - "Key type to generate privval file with. Options: ed25519, secp256k1") -} - -func genValidator(cmd *cobra.Command, args []string) error { - pv, err := privval.GenFilePV("", "", keyType) - if err != nil { - return err - } - - jsbz, err := json.Marshal(pv) - if err != nil { - return fmt.Errorf("validator -> json: %w", err) +func MakeGenValidatorCommand() *cobra.Command { + var keyType string + cmd := &cobra.Command{ + Use: "gen-validator", + Short: "Generate new validator keypair", + RunE: func(cmd *cobra.Command, args []string) error { + pv, err := privval.GenFilePV("", "", keyType) + if err != nil { + return err + } + + jsbz, err := json.Marshal(pv) + if err != nil { + return fmt.Errorf("validator -> json: %w", err) + } + + fmt.Printf("%v\n", string(jsbz)) + + return nil + }, } - fmt.Printf(`%v -`, string(jsbz)) + cmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, + "Key type to generate privval file with. Options: ed25519, secp256k1") - return nil + return cmd } diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index 6c7de8e6d..33d511097 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -7,7 +7,8 @@ import ( "github.com/spf13/cobra" - cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" tmrand "github.com/tendermint/tendermint/libs/rand" tmtime "github.com/tendermint/tendermint/libs/time" @@ -15,43 +16,41 @@ import ( "github.com/tendermint/tendermint/types" ) -// InitFilesCmd initializes a fresh Tendermint Core instance. -var InitFilesCmd = &cobra.Command{ - Use: "init [full|validator|seed]", - Short: "Initializes a Tendermint node", - ValidArgs: []string{"full", "validator", "seed"}, - // We allow for zero args so we can throw a more informative error - Args: cobra.MaximumNArgs(1), - RunE: initFiles, -} - -var ( - keyType string -) +// MakeInitFilesCommand returns the command to initialize a fresh Tendermint Core instance. +func MakeInitFilesCommand(conf *config.Config, logger log.Logger) *cobra.Command { + var keyType string + cmd := &cobra.Command{ + Use: "init [full|validator|seed]", + Short: "Initializes a Tendermint node", + ValidArgs: []string{"full", "validator", "seed"}, + // We allow for zero args so we can throw a more informative error + Args: cobra.MaximumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("must specify a node type: tendermint init [validator|full|seed]") + } + conf.Mode = args[0] + return initFilesWithConfig(cmd.Context(), conf, logger) + }, + } -func init() { - InitFilesCmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, + cmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, "Key type to generate privval file with. Options: ed25519, secp256k1") -} -func initFiles(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errors.New("must specify a node type: tendermint init [validator|full|seed]") - } - config.Mode = args[0] - return initFilesWithConfig(cmd.Context(), config) + return cmd } -func initFilesWithConfig(ctx context.Context, config *cfg.Config) error { +func initFilesWithConfig(ctx context.Context, conf *config.Config, logger log.Logger) error { var ( - pv *privval.FilePV - err error + pv *privval.FilePV + err error + keyType string ) - if config.Mode == cfg.ModeValidator { + if conf.Mode == config.ModeValidator { // private validator - privValKeyFile := config.PrivValidator.KeyFile() - privValStateFile := config.PrivValidator.StateFile() + privValKeyFile := conf.PrivValidator.KeyFile() + privValStateFile := conf.PrivValidator.StateFile() if tmos.FileExists(privValKeyFile) { pv, err = privval.LoadFilePV(privValKeyFile, privValStateFile) if err != nil { @@ -73,7 +72,7 @@ func initFilesWithConfig(ctx context.Context, config *cfg.Config) error { } } - nodeKeyFile := config.NodeKeyFile() + nodeKeyFile := conf.NodeKeyFile() if tmos.FileExists(nodeKeyFile) { logger.Info("Found node key", "path", nodeKeyFile) } else { @@ -84,7 +83,7 @@ func initFilesWithConfig(ctx context.Context, config *cfg.Config) error { } // genesis file - genFile := config.GenesisFile() + genFile := conf.GenesisFile() if tmos.FileExists(genFile) { logger.Info("Found genesis file", "path", genFile) } else { @@ -123,10 +122,10 @@ func initFilesWithConfig(ctx context.Context, config *cfg.Config) error { } // write config file - if err := cfg.WriteConfigFile(config.RootDir, config); err != nil { + if err := config.WriteConfigFile(conf.RootDir, conf); err != nil { return err } - logger.Info("Generated config", "mode", config.Mode) + logger.Info("Generated config", "mode", conf.Mode) return nil } diff --git a/cmd/tendermint/commands/inspect.go b/cmd/tendermint/commands/inspect.go index bb6c5c2f1..11afa419e 100644 --- a/cmd/tendermint/commands/inspect.go +++ b/cmd/tendermint/commands/inspect.go @@ -6,14 +6,17 @@ import ( "github.com/spf13/cobra" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/internal/inspect" + "github.com/tendermint/tendermint/libs/log" ) -// InspectCmd is the command for starting an inspect server. -var InspectCmd = &cobra.Command{ - Use: "inspect", - Short: "Run an inspect server for investigating Tendermint state", - Long: ` +// InspectCmd constructs the command to start an inspect server. +func MakeInspectCommand(conf *config.Config, logger log.Logger) *cobra.Command { + cmd := &cobra.Command{ + Use: "inspect", + Short: "Run an inspect server for investigating Tendermint state", + Long: ` inspect runs a subset of Tendermint's RPC endpoints that are useful for debugging issues with Tendermint. @@ -22,33 +25,27 @@ var InspectCmd = &cobra.Command{ The inspect command can be used to query the block and state store using Tendermint RPC calls to debug issues of inconsistent state. `, - - RunE: runInspect, -} - -func init() { - InspectCmd.Flags(). - String("rpc.laddr", - config.RPC.ListenAddress, "RPC listenener address. Port required") - InspectCmd.Flags(). - String("db-backend", - config.DBBackend, "database backend: goleveldb | cleveldb | boltdb | rocksdb | badgerdb") - InspectCmd.Flags(). - String("db-dir", config.DBPath, "database directory") -} - -func runInspect(cmd *cobra.Command, args []string) error { - ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM, syscall.SIGINT) - defer cancel() - - ins, err := inspect.NewFromConfig(logger, config) - if err != nil { - return err + RunE: func(cmd *cobra.Command, args []string) error { + ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM, syscall.SIGINT) + defer cancel() + + ins, err := inspect.NewFromConfig(logger, conf) + if err != nil { + return err + } + + logger.Info("starting inspect server") + if err := ins.Run(ctx); err != nil { + return err + } + return nil + }, } + cmd.Flags().String("rpc.laddr", + conf.RPC.ListenAddress, "RPC listenener address. Port required") + cmd.Flags().String("db-backend", + conf.DBBackend, "database backend: goleveldb | cleveldb | boltdb | rocksdb | badgerdb") + cmd.Flags().String("db-dir", conf.DBPath, "database directory") - logger.Info("starting inspect server") - if err := ins.Run(ctx); err != nil { - return err - } - return nil + return cmd } diff --git a/cmd/tendermint/commands/key_migrate.go b/cmd/tendermint/commands/key_migrate.go index 739af4a7d..1706f8b6a 100644 --- a/cmd/tendermint/commands/key_migrate.go +++ b/cmd/tendermint/commands/key_migrate.go @@ -6,10 +6,11 @@ import ( "github.com/spf13/cobra" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/scripts/keymigrate" ) -func MakeKeyMigrateCommand() *cobra.Command { +func MakeKeyMigrateCommand(conf *cfg.Config, logger log.Logger) *cobra.Command { cmd := &cobra.Command{ Use: "key-migrate", Short: "Run Database key migration", @@ -38,7 +39,7 @@ func MakeKeyMigrateCommand() *cobra.Command { db, err := cfg.DefaultDBProvider(&cfg.DBContext{ ID: dbctx, - Config: config, + Config: conf, }) if err != nil { @@ -58,7 +59,7 @@ func MakeKeyMigrateCommand() *cobra.Command { } // allow database info to be overridden via cli - addDBFlags(cmd) + addDBFlags(cmd, conf) return cmd } diff --git a/cmd/tendermint/commands/light.go b/cmd/tendermint/commands/light.go index 9cad3397f..0ee774835 100644 --- a/cmd/tendermint/commands/light.go +++ b/cmd/tendermint/commands/light.go @@ -15,6 +15,7 @@ import ( "github.com/spf13/cobra" dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" "github.com/tendermint/tendermint/light" @@ -24,11 +25,58 @@ import ( rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" ) -// LightCmd represents the base command when called without any subcommands -var LightCmd = &cobra.Command{ - Use: "light [chainID]", - Short: "Run a light client proxy server, verifying Tendermint rpc", - Long: `Run a light client proxy server, verifying Tendermint rpc. +// LightCmd constructs the base command called when invoked without any subcommands. +func MakeLightCommand(conf *config.Config, logger log.Logger) *cobra.Command { + var ( + listenAddr string + primaryAddr string + witnessAddrsJoined string + chainID string + dir string + maxOpenConnections int + + sequential bool + trustingPeriod time.Duration + trustedHeight int64 + trustedHash []byte + trustLevelStr string + + logLevel string + logFormat string + + primaryKey = []byte("primary") + witnessesKey = []byte("witnesses") + ) + + checkForExistingProviders := func(db dbm.DB) (string, []string, error) { + primaryBytes, err := db.Get(primaryKey) + if err != nil { + return "", []string{""}, err + } + witnessesBytes, err := db.Get(witnessesKey) + if err != nil { + return "", []string{""}, err + } + witnessesAddrs := strings.Split(string(witnessesBytes), ",") + return string(primaryBytes), witnessesAddrs, nil + } + + saveProviders := func(db dbm.DB, primaryAddr, witnessesAddrs string) error { + err := db.Set(primaryKey, []byte(primaryAddr)) + if err != nil { + return fmt.Errorf("failed to save primary provider: %w", err) + } + err = db.Set(witnessesKey, []byte(witnessesAddrs)) + if err != nil { + return fmt.Errorf("failed to save witness providers: %w", err) + } + return nil + } + + cmd := &cobra.Command{ + Use: "light [chainID]", + Short: "Run a light client proxy server, verifying Tendermint rpc", + Long: `Run a light client proxy server, verifying Tendermint rpc. All calls that can be tracked back to a block header by a proof will be verified before passing them back to the caller. Other than @@ -46,185 +94,134 @@ When /abci_query is called, the Merkle key path format is: Please verify with your application that this Merkle key format is used (true for applications built w/ Cosmos SDK). `, - RunE: runProxy, - Args: cobra.ExactArgs(1), - Example: `light cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657 + RunE: func(cmd *cobra.Command, args []string) error { + chainID = args[0] + logger.Info("Creating client...", "chainID", chainID) + + var witnessesAddrs []string + if witnessAddrsJoined != "" { + witnessesAddrs = strings.Split(witnessAddrsJoined, ",") + } + + lightDB, err := dbm.NewGoLevelDB("light-client-db", dir) + if err != nil { + return fmt.Errorf("can't create a db: %w", err) + } + // create a prefixed db on the chainID + db := dbm.NewPrefixDB(lightDB, []byte(chainID)) + + if primaryAddr == "" { // check to see if we can start from an existing state + var err error + primaryAddr, witnessesAddrs, err = checkForExistingProviders(db) + if err != nil { + return fmt.Errorf("failed to retrieve primary or witness from db: %w", err) + } + if primaryAddr == "" { + return errors.New("no primary address was provided nor found. Please provide a primary (using -p)." + + " Run the command: tendermint light --help for more information") + } + } else { + err := saveProviders(db, primaryAddr, witnessAddrsJoined) + if err != nil { + logger.Error("Unable to save primary and or witness addresses", "err", err) + } + } + + trustLevel, err := tmmath.ParseFraction(trustLevelStr) + if err != nil { + return fmt.Errorf("can't parse trust level: %w", err) + } + + options := []light.Option{light.Logger(logger)} + + vo := light.SkippingVerification(trustLevel) + if sequential { + vo = light.SequentialVerification() + } + options = append(options, vo) + + // Initiate the light client. If the trusted store already has blocks in it, this + // will be used else we use the trusted options. + c, err := light.NewHTTPClient( + context.Background(), + chainID, + light.TrustOptions{ + Period: trustingPeriod, + Height: trustedHeight, + Hash: trustedHash, + }, + primaryAddr, + witnessesAddrs, + dbs.New(db), + options..., + ) + if err != nil { + return err + } + + cfg := rpcserver.DefaultConfig() + cfg.MaxBodyBytes = conf.RPC.MaxBodyBytes + cfg.MaxHeaderBytes = conf.RPC.MaxHeaderBytes + cfg.MaxOpenConnections = maxOpenConnections + // If necessary adjust global WriteTimeout to ensure it's greater than + // TimeoutBroadcastTxCommit. + // See https://github.com/tendermint/tendermint/issues/3435 + if cfg.WriteTimeout <= conf.RPC.TimeoutBroadcastTxCommit { + cfg.WriteTimeout = conf.RPC.TimeoutBroadcastTxCommit + 1*time.Second + } + + p, err := lproxy.NewProxy(c, listenAddr, primaryAddr, cfg, logger, lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn())) + if err != nil { + return err + } + + ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM) + defer cancel() + + go func() { + <-ctx.Done() + p.Listener.Close() + }() + + logger.Info("Starting proxy...", "laddr", listenAddr) + if err := p.ListenAndServe(ctx); err != http.ErrServerClosed { + // Error starting or closing listener: + logger.Error("proxy ListenAndServe", "err", err) + } + + return nil + }, + Args: cobra.ExactArgs(1), + Example: `light cosmoshub-3 -p http://52.57.29.196:26657 -w http://public-seed-node.cosmoshub.certus.one:26657 --height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948020CD`, -} - -var ( - listenAddr string - primaryAddr string - witnessAddrsJoined string - chainID string - dir string - maxOpenConnections int - - sequential bool - trustingPeriod time.Duration - trustedHeight int64 - trustedHash []byte - trustLevelStr string - - logLevel string - logFormat string - - primaryKey = []byte("primary") - witnessesKey = []byte("witnesses") -) + } -func init() { - LightCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", + cmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", "serve the proxy on the given address") - LightCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "", + cmd.Flags().StringVarP(&primaryAddr, "primary", "p", "", "connect to a Tendermint node at this address") - LightCmd.Flags().StringVarP(&witnessAddrsJoined, "witnesses", "w", "", + cmd.Flags().StringVarP(&witnessAddrsJoined, "witnesses", "w", "", "tendermint nodes to cross-check the primary node, comma-separated") - LightCmd.Flags().StringVarP(&dir, "dir", "d", os.ExpandEnv(filepath.Join("$HOME", ".tendermint-light")), + cmd.Flags().StringVarP(&dir, "dir", "d", os.ExpandEnv(filepath.Join("$HOME", ".tendermint-light")), "specify the directory") - LightCmd.Flags().IntVar( + cmd.Flags().IntVar( &maxOpenConnections, "max-open-connections", 900, "maximum number of simultaneous connections (including WebSocket).") - LightCmd.Flags().DurationVar(&trustingPeriod, "trusting-period", 168*time.Hour, + cmd.Flags().DurationVar(&trustingPeriod, "trusting-period", 168*time.Hour, "trusting period that headers can be verified within. Should be significantly less than the unbonding period") - LightCmd.Flags().Int64Var(&trustedHeight, "height", 1, "Trusted header's height") - LightCmd.Flags().BytesHexVar(&trustedHash, "hash", []byte{}, "Trusted header's hash") - LightCmd.Flags().StringVar(&logLevel, "log-level", log.LogLevelInfo, "The logging level (debug|info|warn|error|fatal)") - LightCmd.Flags().StringVar(&logFormat, "log-format", log.LogFormatPlain, "The logging format (text|json)") - LightCmd.Flags().StringVar(&trustLevelStr, "trust-level", "1/3", + cmd.Flags().Int64Var(&trustedHeight, "height", 1, "Trusted header's height") + cmd.Flags().BytesHexVar(&trustedHash, "hash", []byte{}, "Trusted header's hash") + cmd.Flags().StringVar(&logLevel, "log-level", log.LogLevelInfo, "The logging level (debug|info|warn|error|fatal)") + cmd.Flags().StringVar(&logFormat, "log-format", log.LogFormatPlain, "The logging format (text|json)") + cmd.Flags().StringVar(&trustLevelStr, "trust-level", "1/3", "trust level. Must be between 1/3 and 3/3", ) - LightCmd.Flags().BoolVar(&sequential, "sequential", false, + cmd.Flags().BoolVar(&sequential, "sequential", false, "sequential verification. Verify all headers sequentially as opposed to using skipping verification", ) -} - -func runProxy(cmd *cobra.Command, args []string) error { - logger, err := log.NewDefaultLogger(logFormat, logLevel) - if err != nil { - return err - } - - chainID = args[0] - logger.Info("Creating client...", "chainID", chainID) - - witnessesAddrs := []string{} - if witnessAddrsJoined != "" { - witnessesAddrs = strings.Split(witnessAddrsJoined, ",") - } - - lightDB, err := dbm.NewGoLevelDB("light-client-db", dir) - if err != nil { - return fmt.Errorf("can't create a db: %w", err) - } - // create a prefixed db on the chainID - db := dbm.NewPrefixDB(lightDB, []byte(chainID)) - - if primaryAddr == "" { // check to see if we can start from an existing state - var err error - primaryAddr, witnessesAddrs, err = checkForExistingProviders(db) - if err != nil { - return fmt.Errorf("failed to retrieve primary or witness from db: %w", err) - } - if primaryAddr == "" { - return errors.New("no primary address was provided nor found. Please provide a primary (using -p)." + - " Run the command: tendermint light --help for more information") - } - } else { - err := saveProviders(db, primaryAddr, witnessAddrsJoined) - if err != nil { - logger.Error("Unable to save primary and or witness addresses", "err", err) - } - } - - trustLevel, err := tmmath.ParseFraction(trustLevelStr) - if err != nil { - return fmt.Errorf("can't parse trust level: %w", err) - } - - options := []light.Option{light.Logger(logger)} - - if sequential { - options = append(options, light.SequentialVerification()) - } else { - options = append(options, light.SkippingVerification(trustLevel)) - } - - // Initiate the light client. If the trusted store already has blocks in it, this - // will be used else we use the trusted options. - c, err := light.NewHTTPClient( - context.Background(), - chainID, - light.TrustOptions{ - Period: trustingPeriod, - Height: trustedHeight, - Hash: trustedHash, - }, - primaryAddr, - witnessesAddrs, - dbs.New(db), - options..., - ) - if err != nil { - return err - } - cfg := rpcserver.DefaultConfig() - cfg.MaxBodyBytes = config.RPC.MaxBodyBytes - cfg.MaxHeaderBytes = config.RPC.MaxHeaderBytes - cfg.MaxOpenConnections = maxOpenConnections - // If necessary adjust global WriteTimeout to ensure it's greater than - // TimeoutBroadcastTxCommit. - // See https://github.com/tendermint/tendermint/issues/3435 - if cfg.WriteTimeout <= config.RPC.TimeoutBroadcastTxCommit { - cfg.WriteTimeout = config.RPC.TimeoutBroadcastTxCommit + 1*time.Second - } + return cmd - p, err := lproxy.NewProxy(c, listenAddr, primaryAddr, cfg, logger, lrpc.KeyPathFn(lrpc.DefaultMerkleKeyPathFn())) - if err != nil { - return err - } - - ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM) - defer cancel() - - go func() { - <-ctx.Done() - p.Listener.Close() - }() - - logger.Info("Starting proxy...", "laddr", listenAddr) - if err := p.ListenAndServe(ctx); err != http.ErrServerClosed { - // Error starting or closing listener: - logger.Error("proxy ListenAndServe", "err", err) - } - - return nil -} - -func checkForExistingProviders(db dbm.DB) (string, []string, error) { - primaryBytes, err := db.Get(primaryKey) - if err != nil { - return "", []string{""}, err - } - witnessesBytes, err := db.Get(witnessesKey) - if err != nil { - return "", []string{""}, err - } - witnessesAddrs := strings.Split(string(witnessesBytes), ",") - return string(primaryBytes), witnessesAddrs, nil -} - -func saveProviders(db dbm.DB, primaryAddr, witnessesAddrs string) error { - err := db.Set(primaryKey, []byte(primaryAddr)) - if err != nil { - return fmt.Errorf("failed to save primary provider: %w", err) - } - err = db.Set(witnessesKey, []byte(witnessesAddrs)) - if err != nil { - return fmt.Errorf("failed to save witness providers: %w", err) - } - return nil } diff --git a/cmd/tendermint/commands/reindex_event.go b/cmd/tendermint/commands/reindex_event.go index bd9577963..00a1d14b2 100644 --- a/cmd/tendermint/commands/reindex_event.go +++ b/cmd/tendermint/commands/reindex_event.go @@ -17,6 +17,7 @@ import ( "github.com/tendermint/tendermint/internal/state/indexer/sink/kv" "github.com/tendermint/tendermint/internal/state/indexer/sink/psql" "github.com/tendermint/tendermint/internal/store" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/rpc/coretypes" "github.com/tendermint/tendermint/types" @@ -26,59 +27,68 @@ const ( reindexFailed = "event re-index failed: " ) -// ReIndexEventCmd allows re-index the event by given block height interval -var ReIndexEventCmd = &cobra.Command{ - Use: "reindex-event", - Short: "reindex events to the event store backends", - Long: ` +// MakeReindexEventCommand constructs a command to re-index events in a block height interval. +func MakeReindexEventCommand(conf *tmcfg.Config, logger log.Logger) *cobra.Command { + var ( + startHeight int64 + endHeight int64 + ) + + cmd := &cobra.Command{ + Use: "reindex-event", + Short: "reindex events to the event store backends", + Long: ` reindex-event is an offline tooling to re-index block and tx events to the eventsinks, -you can run this command when the event store backend dropped/disconnected or you want to -replace the backend. The default start-height is 0, meaning the tooling will start -reindex from the base block height(inclusive); and the default end-height is 0, meaning +you can run this command when the event store backend dropped/disconnected or you want to +replace the backend. The default start-height is 0, meaning the tooling will start +reindex from the base block height(inclusive); and the default end-height is 0, meaning the tooling will reindex until the latest block height(inclusive). User can omit either or both arguments. `, - Example: ` + Example: ` tendermint reindex-event tendermint reindex-event --start-height 2 tendermint reindex-event --end-height 10 tendermint reindex-event --start-height 2 --end-height 10 `, - Run: func(cmd *cobra.Command, args []string) { - bs, ss, err := loadStateAndBlockStore(config) - if err != nil { - fmt.Println(reindexFailed, err) - return - } - - if err := checkValidHeight(bs); err != nil { - fmt.Println(reindexFailed, err) - return - } + RunE: func(cmd *cobra.Command, args []string) error { + bs, ss, err := loadStateAndBlockStore(conf) + if err != nil { + return fmt.Errorf("%s: %w", reindexFailed, err) + } - es, err := loadEventSinks(config) - if err != nil { - fmt.Println(reindexFailed, err) - return - } + cvhArgs := checkValidHeightArgs{ + startHeight: startHeight, + endHeight: endHeight, + } + if err := checkValidHeight(bs, cvhArgs); err != nil { + return fmt.Errorf("%s: %w", reindexFailed, err) + } - if err = eventReIndex(cmd, es, bs, ss); err != nil { - fmt.Println(reindexFailed, err) - return - } + es, err := loadEventSinks(conf) + if err != nil { + return fmt.Errorf("%s: %w", reindexFailed, err) + } - fmt.Println("event re-index finished") - }, -} + riArgs := eventReIndexArgs{ + startHeight: startHeight, + endHeight: endHeight, + sinks: es, + blockStore: bs, + stateStore: ss, + } + if err := eventReIndex(cmd, riArgs); err != nil { + return fmt.Errorf("%s: %w", reindexFailed, err) + } -var ( - startHeight int64 - endHeight int64 -) + logger.Info("event re-index finished") + return nil + }, + } -func init() { - ReIndexEventCmd.Flags().Int64Var(&startHeight, "start-height", 0, "the block height would like to start for re-index") - ReIndexEventCmd.Flags().Int64Var(&endHeight, "end-height", 0, "the block height would like to finish for re-index") + cmd.Flags().Int64Var(&startHeight, "start-height", 0, "the block height would like to start for re-index") + cmd.Flags().Int64Var(&endHeight, "end-height", 0, "the block height would like to finish for re-index") + return cmd } func loadEventSinks(cfg *tmcfg.Config) ([]indexer.EventSink, error) { @@ -109,7 +119,7 @@ func loadEventSinks(cfg *tmcfg.Config) ([]indexer.EventSink, error) { if conn == "" { return nil, errors.New("the psql connection settings cannot be empty") } - es, err := psql.NewEventSink(conn, chainID) + es, err := psql.NewEventSink(conn, cfg.ChainID()) if err != nil { return nil, err } @@ -159,24 +169,31 @@ func loadStateAndBlockStore(cfg *tmcfg.Config) (*store.BlockStore, state.Store, return blockStore, stateStore, nil } -func eventReIndex(cmd *cobra.Command, es []indexer.EventSink, bs state.BlockStore, ss state.Store) error { +type eventReIndexArgs struct { + startHeight int64 + endHeight int64 + sinks []indexer.EventSink + blockStore state.BlockStore + stateStore state.Store +} +func eventReIndex(cmd *cobra.Command, args eventReIndexArgs) error { var bar progressbar.Bar - bar.NewOption(startHeight-1, endHeight) + bar.NewOption(args.startHeight-1, args.endHeight) fmt.Println("start re-indexing events:") defer bar.Finish() - for i := startHeight; i <= endHeight; i++ { + for i := args.startHeight; i <= args.endHeight; i++ { select { case <-cmd.Context().Done(): return fmt.Errorf("event re-index terminated at height %d: %w", i, cmd.Context().Err()) default: - b := bs.LoadBlock(i) + b := args.blockStore.LoadBlock(i) if b == nil { return fmt.Errorf("not able to load block at height %d from the blockstore", i) } - r, err := ss.LoadABCIResponses(i) + r, err := args.stateStore.LoadABCIResponses(i) if err != nil { return fmt.Errorf("not able to load ABCI Response at height %d from the statestore", i) } @@ -192,11 +209,11 @@ func eventReIndex(cmd *cobra.Command, es []indexer.EventSink, bs state.BlockStor if e.NumTxs > 0 { batch = indexer.NewBatch(e.NumTxs) - for i, tx := range b.Data.Txs { + for i := range b.Data.Txs { tr := abcitypes.TxResult{ Height: b.Height, Index: uint32(i), - Tx: tx, + Tx: b.Data.Txs[i], Result: *(r.DeliverTxs[i]), } @@ -204,7 +221,7 @@ func eventReIndex(cmd *cobra.Command, es []indexer.EventSink, bs state.BlockStor } } - for _, sink := range es { + for _, sink := range args.sinks { if err := sink.IndexBlockEvents(e); err != nil { return fmt.Errorf("block event re-index at height %d failed: %w", i, err) } @@ -223,40 +240,45 @@ func eventReIndex(cmd *cobra.Command, es []indexer.EventSink, bs state.BlockStor return nil } -func checkValidHeight(bs state.BlockStore) error { +type checkValidHeightArgs struct { + startHeight int64 + endHeight int64 +} + +func checkValidHeight(bs state.BlockStore, args checkValidHeightArgs) error { base := bs.Base() - if startHeight == 0 { - startHeight = base + if args.startHeight == 0 { + args.startHeight = base fmt.Printf("set the start block height to the base height of the blockstore %d \n", base) } - if startHeight < base { + if args.startHeight < base { return fmt.Errorf("%s (requested start height: %d, base height: %d)", - coretypes.ErrHeightNotAvailable, startHeight, base) + coretypes.ErrHeightNotAvailable, args.startHeight, base) } height := bs.Height() - if startHeight > height { + if args.startHeight > height { return fmt.Errorf( - "%s (requested start height: %d, store height: %d)", coretypes.ErrHeightNotAvailable, startHeight, height) + "%s (requested start height: %d, store height: %d)", coretypes.ErrHeightNotAvailable, args.startHeight, height) } - if endHeight == 0 || endHeight > height { - endHeight = height + if args.endHeight == 0 || args.endHeight > height { + args.endHeight = height fmt.Printf("set the end block height to the latest height of the blockstore %d \n", height) } - if endHeight < base { + if args.endHeight < base { return fmt.Errorf( - "%s (requested end height: %d, base height: %d)", coretypes.ErrHeightNotAvailable, endHeight, base) + "%s (requested end height: %d, base height: %d)", coretypes.ErrHeightNotAvailable, args.endHeight, base) } - if endHeight < startHeight { + if args.endHeight < args.startHeight { return fmt.Errorf( "%s (requested the end height: %d is less than the start height: %d)", - coretypes.ErrInvalidRequest, startHeight, endHeight) + coretypes.ErrInvalidRequest, args.startHeight, args.endHeight) } return nil diff --git a/cmd/tendermint/commands/reindex_event_test.go b/cmd/tendermint/commands/reindex_event_test.go index 81f11268f..91b1ba42a 100644 --- a/cmd/tendermint/commands/reindex_event_test.go +++ b/cmd/tendermint/commands/reindex_event_test.go @@ -10,9 +10,10 @@ import ( "github.com/stretchr/testify/require" abcitypes "github.com/tendermint/tendermint/abci/types" - tmcfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/internal/state/indexer" "github.com/tendermint/tendermint/internal/state/mocks" + "github.com/tendermint/tendermint/libs/log" prototmstate "github.com/tendermint/tendermint/proto/tendermint/state" "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" @@ -25,9 +26,11 @@ const ( base int64 = 2 ) -func setupReIndexEventCmd(ctx context.Context) *cobra.Command { +func setupReIndexEventCmd(ctx context.Context, conf *config.Config, logger log.Logger) *cobra.Command { + cmd := MakeReindexEventCommand(conf, logger) + reIndexEventCmd := &cobra.Command{ - Use: ReIndexEventCmd.Use, + Use: cmd.Use, Run: func(cmd *cobra.Command, args []string) {}, } @@ -68,10 +71,7 @@ func TestReIndexEventCheckHeight(t *testing.T) { } for _, tc := range testCases { - startHeight = tc.startHeight - endHeight = tc.endHeight - - err := checkValidHeight(mockBlockStore) + err := checkValidHeight(mockBlockStore, checkValidHeightArgs{startHeight: tc.startHeight, endHeight: tc.endHeight}) if tc.validHeight { require.NoError(t, err) } else { @@ -97,7 +97,7 @@ func TestLoadEventSink(t *testing.T) { } for _, tc := range testCases { - cfg := tmcfg.TestConfig() + cfg := config.TestConfig() cfg.TxIndex.Indexer = tc.sinks cfg.TxIndex.PsqlConn = tc.connURL _, err := loadEventSinks(cfg) @@ -110,7 +110,7 @@ func TestLoadEventSink(t *testing.T) { } func TestLoadBlockStore(t *testing.T) { - testCfg, err := tmcfg.ResetTestRoot(t.Name()) + testCfg, err := config.ResetTestRoot(t.Name()) require.NoError(t, err) testCfg.DBBackend = "goleveldb" _, _, err = loadStateAndBlockStore(testCfg) @@ -179,12 +179,20 @@ func TestReIndexEvent(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + logger := log.NewNopLogger() + conf := config.DefaultConfig() for _, tc := range testCases { - startHeight = tc.startHeight - endHeight = tc.endHeight + err := eventReIndex( + setupReIndexEventCmd(ctx, conf, logger), + eventReIndexArgs{ + sinks: []indexer.EventSink{mockEventSink}, + blockStore: mockBlockStore, + stateStore: mockStateStore, + startHeight: tc.startHeight, + endHeight: tc.endHeight, + }) - err := eventReIndex(setupReIndexEventCmd(ctx), []indexer.EventSink{mockEventSink}, mockBlockStore, mockStateStore) if tc.reIndexErr { require.Error(t, err) } else { diff --git a/cmd/tendermint/commands/replay.go b/cmd/tendermint/commands/replay.go index 2cd4c966a..15520c469 100644 --- a/cmd/tendermint/commands/replay.go +++ b/cmd/tendermint/commands/replay.go @@ -2,24 +2,29 @@ package commands import ( "github.com/spf13/cobra" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/internal/consensus" + "github.com/tendermint/tendermint/libs/log" ) -// ReplayCmd allows replaying of messages from the WAL. -var ReplayCmd = &cobra.Command{ - Use: "replay", - Short: "Replay messages from WAL", - RunE: func(cmd *cobra.Command, args []string) error { - return consensus.RunReplayFile(cmd.Context(), logger, config.BaseConfig, config.Consensus, false) - }, +// MakeReplayCommand constructs a command to replay messages from the WAL into consensus. +func MakeReplayCommand(conf *config.Config, logger log.Logger) *cobra.Command { + return &cobra.Command{ + Use: "replay", + Short: "Replay messages from WAL", + RunE: func(cmd *cobra.Command, args []string) error { + return consensus.RunReplayFile(cmd.Context(), logger, conf.BaseConfig, conf.Consensus, false) + }, + } } -// ReplayConsoleCmd allows replaying of messages from the WAL in a -// console. -var ReplayConsoleCmd = &cobra.Command{ - Use: "replay-console", - Short: "Replay messages from WAL in a console", - RunE: func(cmd *cobra.Command, args []string) error { - return consensus.RunReplayFile(cmd.Context(), logger, config.BaseConfig, config.Consensus, true) - }, +// MakeReplayConsoleCommand constructs a command to replay WAL messages to stdout. +func MakeReplayConsoleCommand(conf *config.Config, logger log.Logger) *cobra.Command { + return &cobra.Command{ + Use: "replay-console", + Short: "Replay messages from WAL in a console", + RunE: func(cmd *cobra.Command, args []string) error { + return consensus.RunReplayFile(cmd.Context(), logger, conf.BaseConfig, conf.Consensus, true) + }, + } } diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 725166e9b..ce0798e45 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -5,51 +5,58 @@ import ( "github.com/spf13/cobra" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/types" ) -// ResetAllCmd removes the database of this Tendermint core -// instance. -var ResetAllCmd = &cobra.Command{ - Use: "unsafe-reset-all", - Short: "(unsafe) Remove all the data and WAL, reset this node's validator to genesis state", - RunE: resetAll, +// MakeResetAllCommand constructs a command that removes the database of +// the specified Tendermint core instance. +func MakeResetAllCommand(conf *config.Config, logger log.Logger) *cobra.Command { + var keyType string + + cmd := &cobra.Command{ + Use: "unsafe-reset-all", + Short: "(unsafe) Remove all the data and WAL, reset this node's validator to genesis state", + RunE: func(cmd *cobra.Command, args []string) error { + return resetAll(conf.DBDir(), conf.PrivValidator.KeyFile(), + conf.PrivValidator.StateFile(), logger, keyType) + }, + } + cmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, + "Key type to generate privval file with. Options: ed25519, secp256k1") + + return cmd } -var keepAddrBook bool +func MakeResetPrivateValidatorCommand(conf *config.Config, logger log.Logger) *cobra.Command { + var keyType string -func init() { - ResetAllCmd.Flags().BoolVar(&keepAddrBook, "keep-addr-book", false, "keep the address book intact") - ResetPrivValidatorCmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, + cmd := &cobra.Command{ + Use: "unsafe-reset-priv-validator", + Short: "(unsafe) Reset this node's validator to genesis state", + RunE: func(cmd *cobra.Command, args []string) error { + return resetFilePV(conf.PrivValidator.KeyFile(), conf.PrivValidator.StateFile(), logger, keyType) + }, + } + + cmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, "Key type to generate privval file with. Options: ed25519, secp256k1") -} + return cmd -// ResetPrivValidatorCmd resets the private validator files. -var ResetPrivValidatorCmd = &cobra.Command{ - Use: "unsafe-reset-priv-validator", - Short: "(unsafe) Reset this node's validator to genesis state", - RunE: resetPrivValidator, } // XXX: this is totally unsafe. // it's only suitable for testnets. -func resetAll(cmd *cobra.Command, args []string) error { - return ResetAll(config.DBDir(), config.PrivValidator.KeyFile(), - config.PrivValidator.StateFile(), logger) -} // XXX: this is totally unsafe. // it's only suitable for testnets. -func resetPrivValidator(cmd *cobra.Command, args []string) error { - return resetFilePV(config.PrivValidator.KeyFile(), config.PrivValidator.StateFile(), logger) -} -// ResetAll removes address book files plus all data, and resets the privValdiator data. +// resetAll removes address book files plus all data, and resets the privValdiator data. // Exported so other CLI tools can use it. -func ResetAll(dbDir, privValKeyFile, privValStateFile string, logger log.Logger) error { +func resetAll(dbDir, privValKeyFile, privValStateFile string, logger log.Logger, keyType string) error { if err := os.RemoveAll(dbDir); err == nil { logger.Info("Removed all blockchain history", "dir", dbDir) } else { @@ -59,10 +66,10 @@ func ResetAll(dbDir, privValKeyFile, privValStateFile string, logger log.Logger) if err := tmos.EnsureDir(dbDir, 0700); err != nil { logger.Error("unable to recreate dbDir", "err", err) } - return resetFilePV(privValKeyFile, privValStateFile, logger) + return resetFilePV(privValKeyFile, privValStateFile, logger, keyType) } -func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) error { +func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger, keyType string) error { if _, err := os.Stat(privValKeyFile); err == nil { pv, err := privval.LoadFilePVEmptyState(privValKeyFile, privValStateFile) if err != nil { diff --git a/cmd/tendermint/commands/rollback.go b/cmd/tendermint/commands/rollback.go index 8391ee506..a60434178 100644 --- a/cmd/tendermint/commands/rollback.go +++ b/cmd/tendermint/commands/rollback.go @@ -5,14 +5,15 @@ import ( "github.com/spf13/cobra" - cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/internal/state" ) -var RollbackStateCmd = &cobra.Command{ - Use: "rollback", - Short: "rollback tendermint state by one height", - Long: ` +func MakeRollbackStateCommand(conf *config.Config) *cobra.Command { + return &cobra.Command{ + Use: "rollback", + Short: "rollback tendermint state by one height", + Long: ` A state rollback is performed to recover from an incorrect application state transition, when Tendermint has persisted an incorrect app hash and is thus unable to make progress. Rollback overwrites a state at height n with the state at height n - 1. @@ -20,21 +21,23 @@ The application should also roll back to height n - 1. No blocks are removed, so restarting Tendermint the transactions in block n will be re-executed against the application. `, - RunE: func(cmd *cobra.Command, args []string) error { - height, hash, err := RollbackState(config) - if err != nil { - return fmt.Errorf("failed to rollback state: %w", err) - } - - fmt.Printf("Rolled back state to height %d and hash %X", height, hash) - return nil - }, + RunE: func(cmd *cobra.Command, args []string) error { + height, hash, err := RollbackState(conf) + if err != nil { + return fmt.Errorf("failed to rollback state: %w", err) + } + + fmt.Printf("Rolled back state to height %d and hash %X", height, hash) + return nil + }, + } + } // RollbackState takes the state at the current height n and overwrites it with the state // at height n - 1. Note state here refers to tendermint state not application state. // Returns the latest state height and app hash alongside an error if there was one. -func RollbackState(config *cfg.Config) (int64, []byte, error) { +func RollbackState(config *config.Config) (int64, []byte, error) { // use the parsed config to load the block and state store blockStore, stateStore, err := loadStateAndBlockStore(config) if err != nil { diff --git a/cmd/tendermint/commands/root.go b/cmd/tendermint/commands/root.go index 354b414bf..082bd2903 100644 --- a/cmd/tendermint/commands/root.go +++ b/cmd/tendermint/commands/root.go @@ -2,65 +2,62 @@ package commands import ( "fmt" + "os" + "path/filepath" "time" "github.com/spf13/cobra" "github.com/spf13/viper" - cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" ) -var ( - config = cfg.DefaultConfig() - logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo) - ctxTimeout = 4 * time.Second -) - -func init() { - registerFlagsRootCmd(RootCmd) -} - -func registerFlagsRootCmd(cmd *cobra.Command) { - cmd.PersistentFlags().String("log-level", config.LogLevel, "log level") -} +const ctxTimeout = 4 * time.Second // ParseConfig retrieves the default environment configuration, // sets up the Tendermint root and ensures that the root exists -func ParseConfig() (*cfg.Config, error) { - conf := cfg.DefaultConfig() - err := viper.Unmarshal(conf) - if err != nil { +func ParseConfig(conf *config.Config) (*config.Config, error) { + if err := viper.Unmarshal(conf); err != nil { return nil, err } + conf.SetRoot(conf.RootDir) - cfg.EnsureRoot(conf.RootDir) + if err := conf.ValidateBasic(); err != nil { return nil, fmt.Errorf("error in config file: %w", err) } return conf, nil } -// RootCmd is the root command for Tendermint core. -var RootCmd = &cobra.Command{ - Use: "tendermint", - Short: "BFT state machine replication for applications in any programming languages", - PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) { - if cmd.Name() == VersionCmd.Name() { - return nil - } - - config, err = ParseConfig() - if err != nil { - return err - } +// RootCommand constructs the root command-line entry point for Tendermint core. +func RootCommand(conf *config.Config, logger log.Logger) *cobra.Command { + cmd := &cobra.Command{ + Use: "tendermint", + Short: "BFT state machine replication for applications in any programming languages", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if cmd.Name() == VersionCmd.Name() { + return nil + } + + if err := cli.BindFlagsLoadViper(cmd, args); err != nil { + return err + } + + pconf, err := ParseConfig(conf) + if err != nil { + return err + } + *conf = *pconf + config.EnsureRoot(conf.RootDir) - logger, err = log.NewDefaultLogger(config.LogFormat, config.LogLevel) - if err != nil { - return err - } - - logger = logger.With("module", "main") - return nil - }, + return nil + }, + } + cmd.PersistentFlags().StringP(cli.HomeFlag, "", os.ExpandEnv(filepath.Join("$HOME", config.DefaultTendermintDir)), "directory for config and data") + cmd.PersistentFlags().Bool(cli.TraceFlag, false, "print out full stack trace on errors") + cmd.PersistentFlags().String("log-level", conf.LogLevel, "log level") + cobra.OnInitialize(func() { cli.InitEnv("TM") }) + return cmd } diff --git a/cmd/tendermint/commands/root_test.go b/cmd/tendermint/commands/root_test.go index 86fb75f02..2eade59b6 100644 --- a/cmd/tendermint/commands/root_test.go +++ b/cmd/tendermint/commands/root_test.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "path/filepath" - "strconv" "testing" "github.com/spf13/cobra" @@ -14,42 +13,42 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" ) // clearConfig clears env vars, the given root dir, and resets viper. -func clearConfig(t *testing.T, dir string) { +func clearConfig(t *testing.T, dir string) *cfg.Config { t.Helper() require.NoError(t, os.Unsetenv("TMHOME")) require.NoError(t, os.Unsetenv("TM_HOME")) require.NoError(t, os.RemoveAll(dir)) viper.Reset() - config = cfg.DefaultConfig() + conf := cfg.DefaultConfig() + conf.RootDir = dir + return conf } // prepare new rootCmd -func testRootCmd() *cobra.Command { - rootCmd := &cobra.Command{ - Use: RootCmd.Use, - PersistentPreRunE: RootCmd.PersistentPreRunE, - Run: func(cmd *cobra.Command, args []string) {}, - } - registerFlagsRootCmd(rootCmd) +func testRootCmd(conf *cfg.Config) *cobra.Command { + logger := log.NewNopLogger() + cmd := RootCommand(conf, logger) + cmd.RunE = func(cmd *cobra.Command, args []string) error { return nil } + var l string - rootCmd.PersistentFlags().String("log", l, "Log") - return rootCmd + cmd.PersistentFlags().String("log", l, "Log") + return cmd } -func testSetup(t *testing.T, rootDir string, args []string, env map[string]string) error { +func testSetup(t *testing.T, conf *cfg.Config, args []string, env map[string]string) error { t.Helper() - clearConfig(t, rootDir) - rootCmd := testRootCmd() - cmd := cli.PrepareBaseCmd(rootCmd, "TM", rootDir) + cmd := testRootCmd(conf) + viper.Set(cli.HomeFlag, conf.RootDir) // run with the args and env - args = append([]string{rootCmd.Use}, args...) + args = append([]string{cmd.Use}, args...) return cli.RunWithArgs(cmd, args, env) } @@ -67,22 +66,25 @@ func TestRootHome(t *testing.T) { } for i, tc := range cases { - idxString := strconv.Itoa(i) + t.Run(fmt.Sprint(i), func(t *testing.T) { + conf := clearConfig(t, tc.root) - err := testSetup(t, defaultRoot, tc.args, tc.env) - require.NoError(t, err, idxString) + err := testSetup(t, conf, tc.args, tc.env) + require.NoError(t, err) - assert.Equal(t, tc.root, config.RootDir, idxString) - assert.Equal(t, tc.root, config.P2P.RootDir, idxString) - assert.Equal(t, tc.root, config.Consensus.RootDir, idxString) - assert.Equal(t, tc.root, config.Mempool.RootDir, idxString) + require.Equal(t, tc.root, conf.RootDir) + require.Equal(t, tc.root, conf.P2P.RootDir) + require.Equal(t, tc.root, conf.Consensus.RootDir) + require.Equal(t, tc.root, conf.Mempool.RootDir) + }) } } func TestRootFlagsEnv(t *testing.T) { - // defaults defaults := cfg.DefaultConfig() + defaultDir := t.TempDir() + defaultLogLvl := defaults.LogLevel cases := []struct { @@ -97,19 +99,20 @@ func TestRootFlagsEnv(t *testing.T) { {nil, map[string]string{"TM_LOG_LEVEL": "debug"}, "debug"}, // right env } - defaultRoot := t.TempDir() for i, tc := range cases { - idxString := strconv.Itoa(i) + t.Run(fmt.Sprint(i), func(t *testing.T) { + conf := clearConfig(t, defaultDir) + + err := testSetup(t, conf, tc.args, tc.env) + require.NoError(t, err) - err := testSetup(t, defaultRoot, tc.args, tc.env) - require.NoError(t, err, idxString) + assert.Equal(t, tc.logLevel, conf.LogLevel) + }) - assert.Equal(t, tc.logLevel, config.LogLevel, idxString) } } func TestRootConfig(t *testing.T) { - // write non-default config nonDefaultLogLvl := "debug" cvals := map[string]string{ @@ -117,9 +120,8 @@ func TestRootConfig(t *testing.T) { } cases := []struct { - args []string - env map[string]string - + args []string + env map[string]string logLvl string }{ {nil, nil, nonDefaultLogLvl}, // should load config @@ -128,29 +130,30 @@ func TestRootConfig(t *testing.T) { } for i, tc := range cases { - defaultRoot := t.TempDir() - idxString := strconv.Itoa(i) - clearConfig(t, defaultRoot) - - // XXX: path must match cfg.defaultConfigPath - configFilePath := filepath.Join(defaultRoot, "config") - err := tmos.EnsureDir(configFilePath, 0700) - require.NoError(t, err) - - // write the non-defaults to a different path - // TODO: support writing sub configs so we can test that too - err = WriteConfigVals(configFilePath, cvals) - require.NoError(t, err) - - rootCmd := testRootCmd() - cmd := cli.PrepareBaseCmd(rootCmd, "TM", defaultRoot) - - // run with the args and env - tc.args = append([]string{rootCmd.Use}, tc.args...) - err = cli.RunWithArgs(cmd, tc.args, tc.env) - require.NoError(t, err, idxString) - - assert.Equal(t, tc.logLvl, config.LogLevel, idxString) + t.Run(fmt.Sprint(i), func(t *testing.T) { + defaultRoot := t.TempDir() + conf := clearConfig(t, defaultRoot) + conf.LogLevel = tc.logLvl + + // XXX: path must match cfg.defaultConfigPath + configFilePath := filepath.Join(defaultRoot, "config") + err := tmos.EnsureDir(configFilePath, 0700) + require.NoError(t, err) + + // write the non-defaults to a different path + // TODO: support writing sub configs so we can test that too + err = WriteConfigVals(configFilePath, cvals) + require.NoError(t, err) + + cmd := testRootCmd(conf) + + // run with the args and env + tc.args = append([]string{cmd.Use}, tc.args...) + err = cli.RunWithArgs(cmd, tc.args, tc.env) + require.NoError(t, err) + + require.Equal(t, tc.logLvl, conf.LogLevel) + }) } } diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 9b3a2c7b7..afd3ae8f1 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -12,25 +12,26 @@ import ( "github.com/spf13/cobra" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" ) var ( genesisHash []byte ) -// AddNodeFlags exposes some common configuration options on the command-line -// These are exposed for convenience of commands embedding a tendermint node -func AddNodeFlags(cmd *cobra.Command) { +// AddNodeFlags exposes some common configuration options from conf in the flag +// set for cmd. This is a convenience for commands embedding a Tendermint node. +func AddNodeFlags(cmd *cobra.Command, conf *cfg.Config) { // bind flags - cmd.Flags().String("moniker", config.Moniker, "node name") + cmd.Flags().String("moniker", conf.Moniker, "node name") // mode flags - cmd.Flags().String("mode", config.Mode, "node mode (full | validator | seed)") + cmd.Flags().String("mode", conf.Mode, "node mode (full | validator | seed)") // priv val flags cmd.Flags().String( "priv-validator-laddr", - config.PrivValidator.ListenAddr, + conf.PrivValidator.ListenAddr, "socket address to listen on for connections from external priv-validator process") // node flags @@ -40,74 +41,74 @@ func AddNodeFlags(cmd *cobra.Command) { "genesis-hash", []byte{}, "optional SHA-256 hash of the genesis file") - cmd.Flags().Int64("consensus.double-sign-check-height", config.Consensus.DoubleSignCheckHeight, + cmd.Flags().Int64("consensus.double-sign-check-height", conf.Consensus.DoubleSignCheckHeight, "how many blocks to look back to check existence of the node's "+ "consensus votes before joining consensus") // abci flags cmd.Flags().String( "proxy-app", - config.ProxyApp, + conf.ProxyApp, "proxy app address, or one of: 'kvstore',"+ " 'persistent_kvstore', 'e2e' or 'noop' for local testing.") - cmd.Flags().String("abci", config.ABCI, "specify abci transport (socket | grpc)") + cmd.Flags().String("abci", conf.ABCI, "specify abci transport (socket | grpc)") // rpc flags - cmd.Flags().String("rpc.laddr", config.RPC.ListenAddress, "RPC listen address. Port required") - cmd.Flags().Bool("rpc.unsafe", config.RPC.Unsafe, "enabled unsafe rpc methods") - cmd.Flags().String("rpc.pprof-laddr", config.RPC.PprofListenAddress, "pprof listen address (https://golang.org/pkg/net/http/pprof)") + cmd.Flags().String("rpc.laddr", conf.RPC.ListenAddress, "RPC listen address. Port required") + cmd.Flags().Bool("rpc.unsafe", conf.RPC.Unsafe, "enabled unsafe rpc methods") + cmd.Flags().String("rpc.pprof-laddr", conf.RPC.PprofListenAddress, "pprof listen address (https://golang.org/pkg/net/http/pprof)") // p2p flags cmd.Flags().String( "p2p.laddr", - config.P2P.ListenAddress, + conf.P2P.ListenAddress, "node listen address. (0.0.0.0:0 means any interface, any port)") - cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "comma-delimited ID@host:port seed nodes") //nolint: staticcheck - cmd.Flags().String("p2p.persistent-peers", config.P2P.PersistentPeers, "comma-delimited ID@host:port persistent peers") - cmd.Flags().Bool("p2p.upnp", config.P2P.UPNP, "enable/disable UPNP port forwarding") - cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "enable/disable Peer-Exchange") - cmd.Flags().String("p2p.private-peer-ids", config.P2P.PrivatePeerIDs, "comma-delimited private peer IDs") + cmd.Flags().String("p2p.seeds", conf.P2P.Seeds, "comma-delimited ID@host:port seed nodes") //nolint: staticcheck + cmd.Flags().String("p2p.persistent-peers", conf.P2P.PersistentPeers, "comma-delimited ID@host:port persistent peers") + cmd.Flags().Bool("p2p.upnp", conf.P2P.UPNP, "enable/disable UPNP port forwarding") + cmd.Flags().Bool("p2p.pex", conf.P2P.PexReactor, "enable/disable Peer-Exchange") + cmd.Flags().String("p2p.private-peer-ids", conf.P2P.PrivatePeerIDs, "comma-delimited private peer IDs") // consensus flags cmd.Flags().Bool( "consensus.create-empty-blocks", - config.Consensus.CreateEmptyBlocks, + conf.Consensus.CreateEmptyBlocks, "set this to false to only produce blocks when there are txs or when the AppHash changes") cmd.Flags().String( "consensus.create-empty-blocks-interval", - config.Consensus.CreateEmptyBlocksInterval.String(), + conf.Consensus.CreateEmptyBlocksInterval.String(), "the possible interval between empty blocks") - addDBFlags(cmd) + addDBFlags(cmd, conf) } -func addDBFlags(cmd *cobra.Command) { +func addDBFlags(cmd *cobra.Command, conf *cfg.Config) { cmd.Flags().String( "db-backend", - config.DBBackend, + conf.DBBackend, "database backend: goleveldb | cleveldb | boltdb | rocksdb | badgerdb") cmd.Flags().String( "db-dir", - config.DBPath, + conf.DBPath, "database directory") } // NewRunNodeCmd returns the command that allows the CLI to start a node. // It can be used with a custom PrivValidator and in-process ABCI application. -func NewRunNodeCmd(nodeProvider cfg.ServiceProvider) *cobra.Command { +func NewRunNodeCmd(nodeProvider cfg.ServiceProvider, conf *cfg.Config, logger log.Logger) *cobra.Command { cmd := &cobra.Command{ Use: "start", Aliases: []string{"node", "run"}, Short: "Run the tendermint node", RunE: func(cmd *cobra.Command, args []string) error { - if err := checkGenesisHash(config); err != nil { + if err := checkGenesisHash(conf); err != nil { return err } ctx, cancel := signal.NotifyContext(cmd.Context(), syscall.SIGTERM) defer cancel() - n, err := nodeProvider(ctx, config, logger) + n, err := nodeProvider(ctx, conf, logger) if err != nil { return fmt.Errorf("failed to create node: %w", err) } @@ -123,7 +124,7 @@ func NewRunNodeCmd(nodeProvider cfg.ServiceProvider) *cobra.Command { }, } - AddNodeFlags(cmd) + AddNodeFlags(cmd, conf) return cmd } diff --git a/cmd/tendermint/commands/show_node_id.go b/cmd/tendermint/commands/show_node_id.go index 488f4c322..9183a7c5e 100644 --- a/cmd/tendermint/commands/show_node_id.go +++ b/cmd/tendermint/commands/show_node_id.go @@ -4,21 +4,22 @@ import ( "fmt" "github.com/spf13/cobra" + "github.com/tendermint/tendermint/config" ) -// ShowNodeIDCmd dumps node's ID to the standard output. -var ShowNodeIDCmd = &cobra.Command{ - Use: "show-node-id", - Short: "Show this node's ID", - RunE: showNodeID, -} +// MakeShowNodeIDCommand constructs a command to dump the node ID to stdout. +func MakeShowNodeIDCommand(conf *config.Config) *cobra.Command { + return &cobra.Command{ + Use: "show-node-id", + Short: "Show this node's ID", + RunE: func(cmd *cobra.Command, args []string) error { + nodeKeyID, err := conf.LoadNodeKeyID() + if err != nil { + return err + } -func showNodeID(cmd *cobra.Command, args []string) error { - nodeKeyID, err := config.LoadNodeKeyID() - if err != nil { - return err + fmt.Println(nodeKeyID) + return nil + }, } - - fmt.Println(nodeKeyID) - return nil } diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index d09fce155..a0e8d7566 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -6,75 +6,78 @@ import ( "github.com/spf13/cobra" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/internal/jsontypes" + "github.com/tendermint/tendermint/libs/log" tmnet "github.com/tendermint/tendermint/libs/net" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/privval" tmgrpc "github.com/tendermint/tendermint/privval/grpc" ) -// ShowValidatorCmd adds capabilities for showing the validator info. -var ShowValidatorCmd = &cobra.Command{ - Use: "show-validator", - Short: "Show this node's validator info", - RunE: showValidator, -} +// MakeShowValidatorCommand constructs a command to show the validator info. +func MakeShowValidatorCommand(conf *config.Config, logger log.Logger) *cobra.Command { + return &cobra.Command{ + Use: "show-validator", + Short: "Show this node's validator info", + RunE: func(cmd *cobra.Command, args []string) error { + var ( + pubKey crypto.PubKey + err error + bctx = cmd.Context() + ) + //TODO: remove once gRPC is the only supported protocol + protocol, _ := tmnet.ProtocolAndAddress(conf.PrivValidator.ListenAddr) + switch protocol { + case "grpc": + pvsc, err := tmgrpc.DialRemoteSigner( + bctx, + conf.PrivValidator, + conf.ChainID(), + logger, + conf.Instrumentation.Prometheus, + ) + if err != nil { + return fmt.Errorf("can't connect to remote validator %w", err) + } -func showValidator(cmd *cobra.Command, args []string) error { - var ( - pubKey crypto.PubKey - err error - bctx = cmd.Context() - ) - //TODO: remove once gRPC is the only supported protocol - protocol, _ := tmnet.ProtocolAndAddress(config.PrivValidator.ListenAddr) - switch protocol { - case "grpc": - pvsc, err := tmgrpc.DialRemoteSigner( - bctx, - config.PrivValidator, - config.ChainID(), - logger, - config.Instrumentation.Prometheus, - ) - if err != nil { - return fmt.Errorf("can't connect to remote validator %w", err) - } + ctx, cancel := context.WithTimeout(bctx, ctxTimeout) + defer cancel() - ctx, cancel := context.WithTimeout(bctx, ctxTimeout) - defer cancel() + pubKey, err = pvsc.GetPubKey(ctx) + if err != nil { + return fmt.Errorf("can't get pubkey: %w", err) + } + default: - pubKey, err = pvsc.GetPubKey(ctx) - if err != nil { - return fmt.Errorf("can't get pubkey: %w", err) - } - default: + keyFilePath := conf.PrivValidator.KeyFile() + if !tmos.FileExists(keyFilePath) { + return fmt.Errorf("private validator file %s does not exist", keyFilePath) + } - keyFilePath := config.PrivValidator.KeyFile() - if !tmos.FileExists(keyFilePath) { - return fmt.Errorf("private validator file %s does not exist", keyFilePath) - } + pv, err := privval.LoadFilePV(keyFilePath, conf.PrivValidator.StateFile()) + if err != nil { + return err + } - pv, err := privval.LoadFilePV(keyFilePath, config.PrivValidator.StateFile()) - if err != nil { - return err - } + ctx, cancel := context.WithTimeout(bctx, ctxTimeout) + defer cancel() - ctx, cancel := context.WithTimeout(bctx, ctxTimeout) - defer cancel() + pubKey, err = pv.GetPubKey(ctx) + if err != nil { + return fmt.Errorf("can't get pubkey: %w", err) + } + } - pubKey, err = pv.GetPubKey(ctx) - if err != nil { - return fmt.Errorf("can't get pubkey: %w", err) - } - } + bz, err := jsontypes.Marshal(pubKey) + if err != nil { + return fmt.Errorf("failed to marshal private validator pubkey: %w", err) + } - bz, err := jsontypes.Marshal(pubKey) - if err != nil { - return fmt.Errorf("failed to marshal private validator pubkey: %w", err) + fmt.Println(string(bz)) + return nil + }, } - fmt.Println(string(bz)) - return nil } diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index 75fdb05c5..82954e41b 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -13,285 +13,319 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" tmtime "github.com/tendermint/tendermint/libs/time" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/types" ) -var ( - nValidators int - nNonValidators int - initialHeight int64 - configFile string - outputDir string - nodeDirPrefix string - - populatePersistentPeers bool - hostnamePrefix string - hostnameSuffix string - startingIPAddress string - hostnames []string - p2pPort int - randomMonikers bool -) - const ( nodeDirPerm = 0755 ) -func init() { - TestnetFilesCmd.Flags().IntVar(&nValidators, "v", 4, +// MakeTestnetFilesCommand constructs a command to generate testnet config files. +func MakeTestnetFilesCommand(conf *cfg.Config, logger log.Logger) *cobra.Command { + cmd := &cobra.Command{ + Use: "testnet", + Short: "Initialize files for a Tendermint testnet", + Long: `testnet will create "v" + "n" number of directories and populate each with +necessary files (private validator, genesis, config, etc.). + +Note, strict routability for addresses is turned off in the config file. + +Optionally, it will fill in persistent-peers list in config file using either hostnames or IPs. + +Example: + + tendermint testnet --v 4 --o ./output --populate-persistent-peers --starting-ip-address 192.168.10.2 + `, + } + var ( + nValidators int + nNonValidators int + initialHeight int64 + configFile string + outputDir string + nodeDirPrefix string + + populatePersistentPeers bool + hostnamePrefix string + hostnameSuffix string + startingIPAddress string + hostnames []string + p2pPort int + randomMonikers bool + keyType string + ) + + cmd.Flags().IntVar(&nValidators, "v", 4, "number of validators to initialize the testnet with") - TestnetFilesCmd.Flags().StringVar(&configFile, "config", "", + cmd.Flags().StringVar(&configFile, "config", "", "config file to use (note some options may be overwritten)") - TestnetFilesCmd.Flags().IntVar(&nNonValidators, "n", 0, + cmd.Flags().IntVar(&nNonValidators, "n", 0, "number of non-validators to initialize the testnet with") - TestnetFilesCmd.Flags().StringVar(&outputDir, "o", "./mytestnet", + cmd.Flags().StringVar(&outputDir, "o", "./mytestnet", "directory to store initialization data for the testnet") - TestnetFilesCmd.Flags().StringVar(&nodeDirPrefix, "node-dir-prefix", "node", + cmd.Flags().StringVar(&nodeDirPrefix, "node-dir-prefix", "node", "prefix the directory name for each node with (node results in node0, node1, ...)") - TestnetFilesCmd.Flags().Int64Var(&initialHeight, "initial-height", 0, + cmd.Flags().Int64Var(&initialHeight, "initial-height", 0, "initial height of the first block") - TestnetFilesCmd.Flags().BoolVar(&populatePersistentPeers, "populate-persistent-peers", true, + cmd.Flags().BoolVar(&populatePersistentPeers, "populate-persistent-peers", true, "update config of each node with the list of persistent peers build using either"+ " hostname-prefix or"+ " starting-ip-address") - TestnetFilesCmd.Flags().StringVar(&hostnamePrefix, "hostname-prefix", "node", + cmd.Flags().StringVar(&hostnamePrefix, "hostname-prefix", "node", "hostname prefix (\"node\" results in persistent peers list ID0@node0:26656, ID1@node1:26656, ...)") - TestnetFilesCmd.Flags().StringVar(&hostnameSuffix, "hostname-suffix", "", + cmd.Flags().StringVar(&hostnameSuffix, "hostname-suffix", "", "hostname suffix ("+ "\".xyz.com\""+ " results in persistent peers list ID0@node0.xyz.com:26656, ID1@node1.xyz.com:26656, ...)") - TestnetFilesCmd.Flags().StringVar(&startingIPAddress, "starting-ip-address", "", + cmd.Flags().StringVar(&startingIPAddress, "starting-ip-address", "", "starting IP address ("+ "\"192.168.0.1\""+ " results in persistent peers list ID0@192.168.0.1:26656, ID1@192.168.0.2:26656, ...)") - TestnetFilesCmd.Flags().StringArrayVar(&hostnames, "hostname", []string{}, + cmd.Flags().StringArrayVar(&hostnames, "hostname", []string{}, "manually override all hostnames of validators and non-validators (use --hostname multiple times for multiple hosts)") - TestnetFilesCmd.Flags().IntVar(&p2pPort, "p2p-port", 26656, + cmd.Flags().IntVar(&p2pPort, "p2p-port", 26656, "P2P Port") - TestnetFilesCmd.Flags().BoolVar(&randomMonikers, "random-monikers", false, + cmd.Flags().BoolVar(&randomMonikers, "random-monikers", false, "randomize the moniker for each generated node") - TestnetFilesCmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, + cmd.Flags().StringVar(&keyType, "key", types.ABCIPubKeyTypeEd25519, "Key type to generate privval file with. Options: ed25519, secp256k1") -} -// TestnetFilesCmd allows initialisation of files for a Tendermint testnet. -var TestnetFilesCmd = &cobra.Command{ - Use: "testnet", - Short: "Initialize files for a Tendermint testnet", - Long: `testnet will create "v" + "n" number of directories and populate each with -necessary files (private validator, genesis, config, etc.). - -Note, strict routability for addresses is turned off in the config file. - -Optionally, it will fill in persistent-peers list in config file using either hostnames or IPs. + cmd.RunE = func(cmd *cobra.Command, args []string) error { + if len(hostnames) > 0 && len(hostnames) != (nValidators+nNonValidators) { + return fmt.Errorf( + "testnet needs precisely %d hostnames (number of validators plus non-validators) if --hostname parameter is used", + nValidators+nNonValidators, + ) + } -Example: + // set mode to validator for testnet + config := cfg.DefaultValidatorConfig() - tendermint testnet --v 4 --o ./output --populate-persistent-peers --starting-ip-address 192.168.10.2 - `, - RunE: testnetFiles, -} + // overwrite default config if set and valid + if configFile != "" { + viper.SetConfigFile(configFile) + if err := viper.ReadInConfig(); err != nil { + return err + } + if err := viper.Unmarshal(config); err != nil { + return err + } + if err := config.ValidateBasic(); err != nil { + return err + } + } -func testnetFiles(cmd *cobra.Command, args []string) error { - if len(hostnames) > 0 && len(hostnames) != (nValidators+nNonValidators) { - return fmt.Errorf( - "testnet needs precisely %d hostnames (number of validators plus non-validators) if --hostname parameter is used", - nValidators+nNonValidators, - ) - } + genVals := make([]types.GenesisValidator, nValidators) + ctx := cmd.Context() + for i := 0; i < nValidators; i++ { + nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) + nodeDir := filepath.Join(outputDir, nodeDirName) + config.SetRoot(nodeDir) + + err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } - // set mode to validator for testnet - config := cfg.DefaultValidatorConfig() + if err := initFilesWithConfig(ctx, config, logger); err != nil { + return err + } - // overwrite default config if set and valid - if configFile != "" { - viper.SetConfigFile(configFile) - if err := viper.ReadInConfig(); err != nil { - return err - } - if err := viper.Unmarshal(config); err != nil { - return err - } - if err := config.ValidateBasic(); err != nil { - return err - } - } + pvKeyFile := filepath.Join(nodeDir, config.PrivValidator.Key) + pvStateFile := filepath.Join(nodeDir, config.PrivValidator.State) + pv, err := privval.LoadFilePV(pvKeyFile, pvStateFile) + if err != nil { + return err + } - genVals := make([]types.GenesisValidator, nValidators) - ctx := cmd.Context() - for i := 0; i < nValidators; i++ { - nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i) - nodeDir := filepath.Join(outputDir, nodeDirName) - config.SetRoot(nodeDir) + ctx, cancel := context.WithTimeout(ctx, ctxTimeout) + defer cancel() - err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) - if err != nil { - _ = os.RemoveAll(outputDir) - return err - } - err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) - if err != nil { - _ = os.RemoveAll(outputDir) - return err + pubKey, err := pv.GetPubKey(ctx) + if err != nil { + return fmt.Errorf("can't get pubkey: %w", err) + } + genVals[i] = types.GenesisValidator{ + Address: pubKey.Address(), + PubKey: pubKey, + Power: 1, + Name: nodeDirName, + } } - if err := initFilesWithConfig(ctx, config); err != nil { - return err - } + for i := 0; i < nNonValidators; i++ { + nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i+nValidators)) + config.SetRoot(nodeDir) - pvKeyFile := filepath.Join(nodeDir, config.PrivValidator.Key) - pvStateFile := filepath.Join(nodeDir, config.PrivValidator.State) - pv, err := privval.LoadFilePV(pvKeyFile, pvStateFile) - if err != nil { - return err - } + err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } - ctx, cancel := context.WithTimeout(ctx, ctxTimeout) - defer cancel() + err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } - pubKey, err := pv.GetPubKey(ctx) - if err != nil { - return fmt.Errorf("can't get pubkey: %w", err) - } - genVals[i] = types.GenesisValidator{ - Address: pubKey.Address(), - PubKey: pubKey, - Power: 1, - Name: nodeDirName, + if err := initFilesWithConfig(ctx, conf, logger); err != nil { + return err + } } - } - - for i := 0; i < nNonValidators; i++ { - nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i+nValidators)) - config.SetRoot(nodeDir) - err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm) - if err != nil { - _ = os.RemoveAll(outputDir) - return err + // Generate genesis doc from generated validators + genDoc := &types.GenesisDoc{ + ChainID: "chain-" + tmrand.Str(6), + GenesisTime: tmtime.Now(), + InitialHeight: initialHeight, + Validators: genVals, + ConsensusParams: types.DefaultConsensusParams(), } - - err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) - if err != nil { - _ = os.RemoveAll(outputDir) - return err + if keyType == "secp256k1" { + genDoc.ConsensusParams.Validator = types.ValidatorParams{ + PubKeyTypes: []string{types.ABCIPubKeyTypeSecp256k1}, + } } - if err := initFilesWithConfig(ctx, config); err != nil { - return err + // Write genesis file. + for i := 0; i < nValidators+nNonValidators; i++ { + nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) + if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil { + _ = os.RemoveAll(outputDir) + return err + } } - } - // Generate genesis doc from generated validators - genDoc := &types.GenesisDoc{ - ChainID: "chain-" + tmrand.Str(6), - GenesisTime: tmtime.Now(), - InitialHeight: initialHeight, - Validators: genVals, - ConsensusParams: types.DefaultConsensusParams(), - } - if keyType == "secp256k1" { - genDoc.ConsensusParams.Validator = types.ValidatorParams{ - PubKeyTypes: []string{types.ABCIPubKeyTypeSecp256k1}, + // Gather persistent peer addresses. + var ( + persistentPeers = make([]string, 0) + err error + ) + tpargs := testnetPeerArgs{ + numValidators: nValidators, + numNonValidators: nNonValidators, + peerToPeerPort: p2pPort, + nodeDirPrefix: nodeDirPrefix, + outputDir: outputDir, + hostnames: hostnames, + startingIPAddr: startingIPAddress, + hostnamePrefix: hostnamePrefix, + hostnameSuffix: hostnameSuffix, + randomMonikers: randomMonikers, } - } - // Write genesis file. - for i := 0; i < nValidators+nNonValidators; i++ { - nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) - if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil { - _ = os.RemoveAll(outputDir) - return err - } - } + if populatePersistentPeers { - // Gather persistent peer addresses. - var ( - persistentPeers = make([]string, 0) - err error - ) - if populatePersistentPeers { - persistentPeers, err = persistentPeersArray(config) - if err != nil { - _ = os.RemoveAll(outputDir) - return err + persistentPeers, err = persistentPeersArray(config, tpargs) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } } - } - // Overwrite default config. - for i := 0; i < nValidators+nNonValidators; i++ { - nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) - config.SetRoot(nodeDir) - config.P2P.AllowDuplicateIP = true - if populatePersistentPeers { - persistentPeersWithoutSelf := make([]string, 0) - for j := 0; j < len(persistentPeers); j++ { - if j == i { - continue + // Overwrite default config. + for i := 0; i < nValidators+nNonValidators; i++ { + nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) + config.SetRoot(nodeDir) + config.P2P.AllowDuplicateIP = true + if populatePersistentPeers { + persistentPeersWithoutSelf := make([]string, 0) + for j := 0; j < len(persistentPeers); j++ { + if j == i { + continue + } + persistentPeersWithoutSelf = append(persistentPeersWithoutSelf, persistentPeers[j]) } - persistentPeersWithoutSelf = append(persistentPeersWithoutSelf, persistentPeers[j]) + config.P2P.PersistentPeers = strings.Join(persistentPeersWithoutSelf, ",") } - config.P2P.PersistentPeers = strings.Join(persistentPeersWithoutSelf, ",") - } - config.Moniker = moniker(i) + config.Moniker = tpargs.moniker(i) - if err := cfg.WriteConfigFile(nodeDir, config); err != nil { - return err + if err := cfg.WriteConfigFile(nodeDir, config); err != nil { + return err + } } + + fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators) + return nil } - fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators) - return nil + return cmd +} + +type testnetPeerArgs struct { + numValidators int + numNonValidators int + peerToPeerPort int + nodeDirPrefix string + outputDir string + hostnames []string + startingIPAddr string + hostnamePrefix string + hostnameSuffix string + randomMonikers bool } -func hostnameOrIP(i int) string { - if len(hostnames) > 0 && i < len(hostnames) { - return hostnames[i] +func (args *testnetPeerArgs) hostnameOrIP(i int) (string, error) { + if len(args.hostnames) > 0 && i < len(args.hostnames) { + return args.hostnames[i], nil } - if startingIPAddress == "" { - return fmt.Sprintf("%s%d%s", hostnamePrefix, i, hostnameSuffix) + if args.startingIPAddr == "" { + return fmt.Sprintf("%s%d%s", args.hostnamePrefix, i, args.hostnameSuffix), nil } - ip := net.ParseIP(startingIPAddress) + ip := net.ParseIP(args.startingIPAddr) ip = ip.To4() if ip == nil { - fmt.Printf("%v: non ipv4 address\n", startingIPAddress) - os.Exit(1) + return "", fmt.Errorf("%v is non-ipv4 address", args.startingIPAddr) } for j := 0; j < i; j++ { ip[3]++ } - return ip.String() + return ip.String(), nil + } // get an array of persistent peers -func persistentPeersArray(config *cfg.Config) ([]string, error) { - peers := make([]string, nValidators+nNonValidators) - for i := 0; i < nValidators+nNonValidators; i++ { - nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i)) +func persistentPeersArray(config *cfg.Config, args testnetPeerArgs) ([]string, error) { + peers := make([]string, args.numValidators+args.numNonValidators) + for i := 0; i < len(peers); i++ { + nodeDir := filepath.Join(args.outputDir, fmt.Sprintf("%s%d", args.nodeDirPrefix, i)) config.SetRoot(nodeDir) nodeKey, err := config.LoadNodeKeyID() if err != nil { - return []string{}, err + return nil, err } - peers[i] = nodeKey.AddressString(fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort)) + addr, err := args.hostnameOrIP(i) + if err != nil { + return nil, err + } + + peers[i] = nodeKey.AddressString(fmt.Sprintf("%s:%d", addr, args.peerToPeerPort)) } return peers, nil } -func moniker(i int) string { - if randomMonikers { +func (args *testnetPeerArgs) moniker(i int) string { + if args.randomMonikers { return randomMoniker() } - if len(hostnames) > 0 && i < len(hostnames) { - return hostnames[i] + if len(args.hostnames) > 0 && i < len(args.hostnames) { + return args.hostnames[i] } - if startingIPAddress == "" { - return fmt.Sprintf("%s%d%s", hostnamePrefix, i, hostnameSuffix) + if args.startingIPAddr == "" { + return fmt.Sprintf("%s%d%s", args.hostnamePrefix, i, args.hostnameSuffix) } return randomMoniker() } diff --git a/cmd/tendermint/main.go b/cmd/tendermint/main.go index b461c8e7b..6d2391dde 100644 --- a/cmd/tendermint/main.go +++ b/cmd/tendermint/main.go @@ -1,37 +1,50 @@ package main import ( - "os" - "path/filepath" + "context" - cmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/cmd/tendermint/commands/debug" "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/node" ) func main() { - rootCmd := cmd.RootCmd - rootCmd.AddCommand( - cmd.GenValidatorCmd, - cmd.ReIndexEventCmd, - cmd.InitFilesCmd, - cmd.LightCmd, - cmd.ReplayCmd, - cmd.ReplayConsoleCmd, - cmd.ResetAllCmd, - cmd.ResetPrivValidatorCmd, - cmd.ShowValidatorCmd, - cmd.TestnetFilesCmd, - cmd.ShowNodeIDCmd, - cmd.GenNodeKeyCmd, - cmd.VersionCmd, - cmd.InspectCmd, - cmd.RollbackStateCmd, - cmd.MakeKeyMigrateCommand(), + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + conf, err := commands.ParseConfig(config.DefaultConfig()) + if err != nil { + panic(err) + } + + logger, err := log.NewDefaultLogger(conf.LogFormat, conf.LogLevel) + if err != nil { + panic(err) + } + + rcmd := commands.RootCommand(conf, logger) + rcmd.AddCommand( + commands.MakeGenValidatorCommand(), + commands.MakeReindexEventCommand(conf, logger), + commands.MakeInitFilesCommand(conf, logger), + commands.MakeLightCommand(conf, logger), + commands.MakeReplayCommand(conf, logger), + commands.MakeReplayConsoleCommand(conf, logger), + commands.MakeResetAllCommand(conf, logger), + commands.MakeResetPrivateValidatorCommand(conf, logger), + commands.MakeShowValidatorCommand(conf, logger), + commands.MakeTestnetFilesCommand(conf, logger), + commands.MakeShowNodeIDCommand(conf), + commands.GenNodeKeyCmd, + commands.VersionCmd, + commands.MakeInspectCommand(conf, logger), + commands.MakeRollbackStateCommand(conf), + commands.MakeKeyMigrateCommand(conf, logger), debug.DebugCmd, - cli.NewCompletionCmd(rootCmd, true), + cli.NewCompletionCmd(rcmd, true), ) // NOTE: @@ -45,10 +58,9 @@ func main() { nodeFunc := node.NewDefault // Create & start node - rootCmd.AddCommand(cmd.NewRunNodeCmd(nodeFunc)) + rcmd.AddCommand(commands.NewRunNodeCmd(nodeFunc, conf, logger)) - cmd := cli.PrepareBaseCmd(rootCmd, "TM", os.ExpandEnv(filepath.Join("$HOME", config.DefaultTendermintDir))) - if err := cmd.Execute(); err != nil { + if err := rcmd.ExecuteContext(ctx); err != nil { panic(err) } } diff --git a/docs/tutorials/go.md b/docs/tutorials/go.md index 6614cffe5..5c6988da9 100644 --- a/docs/tutorials/go.md +++ b/docs/tutorials/go.md @@ -367,7 +367,11 @@ func main() { flag.Parse() - logger := log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false) + logger, err := log.NewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to configure logger: %v", err) + os.Exit(1) + } server := abciserver.NewSocketServer(socketAddr, app) server.SetLogger(logger) diff --git a/libs/cli/setup.go b/libs/cli/setup.go index 8e11bac93..be69c30af 100644 --- a/libs/cli/setup.go +++ b/libs/cli/setup.go @@ -28,10 +28,10 @@ type Executable interface { // PrepareBaseCmd is meant for tendermint and other servers func PrepareBaseCmd(cmd *cobra.Command, envPrefix, defaultHome string) Executor { - cobra.OnInitialize(func() { initEnv(envPrefix) }) + cobra.OnInitialize(func() { InitEnv(envPrefix) }) cmd.PersistentFlags().StringP(HomeFlag, "", defaultHome, "directory for config and data") cmd.PersistentFlags().Bool(TraceFlag, false, "print out full stack trace on errors") - cmd.PersistentPreRunE = concatCobraCmdFuncs(bindFlagsLoadViper, cmd.PersistentPreRunE) + cmd.PersistentPreRunE = concatCobraCmdFuncs(BindFlagsLoadViper, cmd.PersistentPreRunE) return Executor{cmd, os.Exit} } @@ -46,8 +46,8 @@ func PrepareMainCmd(cmd *cobra.Command, envPrefix, defaultHome string) Executor return PrepareBaseCmd(cmd, envPrefix, defaultHome) } -// initEnv sets to use ENV variables if set. -func initEnv(prefix string) { +// InitEnv sets to use ENV variables if set. +func InitEnv(prefix string) { copyEnvVars(prefix) // env variables with TM prefix (eg. TM_ROOT) @@ -127,7 +127,7 @@ func concatCobraCmdFuncs(fs ...cobraCmdFunc) cobraCmdFunc { } // Bind all flags and read the config into viper -func bindFlagsLoadViper(cmd *cobra.Command, args []string) error { +func BindFlagsLoadViper(cmd *cobra.Command, args []string) error { // cmd.Flags() includes flags from this command and all persistent flags from the parent if err := viper.BindPFlags(cmd.Flags()); err != nil { return err diff --git a/libs/log/default_test.go b/libs/log/default_test.go index ca854c7ad..5e8e18810 100644 --- a/libs/log/default_test.go +++ b/libs/log/default_test.go @@ -37,9 +37,6 @@ func TestNewDefaultLogger(t *testing.T) { _, err := log.NewDefaultLogger(tc.format, tc.level) if tc.expectErr { require.Error(t, err) - require.Panics(t, func() { - _ = log.MustNewDefaultLogger(tc.format, tc.level) - }) } else { require.NoError(t, err) }