You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
3.9 KiB

  1. // Copyright © 2017 Ethan Frey
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package cmd
  15. import (
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "strings"
  20. "github.com/pkg/errors"
  21. "github.com/spf13/cobra"
  22. "github.com/spf13/viper"
  23. data "github.com/tendermint/go-data"
  24. "github.com/tendermint/go-data/base58"
  25. keys "github.com/tendermint/go-keys"
  26. "github.com/tendermint/go-keys/cryptostore"
  27. "github.com/tendermint/go-keys/storage/filestorage"
  28. )
  29. var (
  30. rootDir string
  31. output string
  32. keyDir string
  33. manager keys.Manager
  34. )
  35. // RootCmd represents the base command when called without any subcommands
  36. var RootCmd = &cobra.Command{
  37. Use: "keys",
  38. Short: "Key manager for tendermint clients",
  39. Long: `Keys allows you to manage your local keystore for tendermint.
  40. These keys may be in any format supported by go-crypto and can be
  41. used by light-clients, full nodes, or any other application that
  42. needs to sign with a private key.`,
  43. PersistentPreRunE: bindFlags,
  44. }
  45. // Execute adds all child commands to the root command sets flags appropriately.
  46. // This is called by main.main(). It only needs to happen once to the rootCmd.
  47. func Execute() {
  48. if err := RootCmd.Execute(); err != nil {
  49. fmt.Println(err)
  50. os.Exit(-1)
  51. }
  52. }
  53. func init() {
  54. cobra.OnInitialize(initEnv)
  55. RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data")
  56. RootCmd.PersistentFlags().String("keydir", "keys", "Directory to store private keys (subdir of root)")
  57. RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)")
  58. RootCmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)")
  59. }
  60. // initEnv sets to use ENV variables if set.
  61. func initEnv() {
  62. // env variables with TM prefix (eg. TM_ROOT)
  63. viper.SetEnvPrefix("TM")
  64. viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  65. viper.AutomaticEnv()
  66. }
  67. func bindFlags(cmd *cobra.Command, args []string) error {
  68. // cmd.Flags() includes flags from this command and all persistent flags from the parent
  69. if err := viper.BindPFlags(cmd.Flags()); err != nil {
  70. return err
  71. }
  72. // rootDir is command line flag, env variable, or default $HOME/.tlc
  73. rootDir = viper.GetString("root")
  74. viper.SetConfigName("keys") // name of config file (without extension)
  75. viper.AddConfigPath(rootDir) // search root directory
  76. // If a config file is found, read it in.
  77. if err := viper.ReadInConfig(); err == nil {
  78. // stderr, so if we redirect output to json file, this doesn't appear
  79. fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
  80. }
  81. return validateFlags(cmd)
  82. }
  83. // validateFlags asserts all RootCmd flags are valid
  84. func validateFlags(cmd *cobra.Command) error {
  85. // validate output format
  86. output = viper.GetString("output")
  87. switch output {
  88. case "text", "json":
  89. default:
  90. return errors.Errorf("Unsupported output format: %s", output)
  91. }
  92. // validate and set encoding
  93. enc := viper.GetString("encoding")
  94. switch enc {
  95. case "hex":
  96. data.Encoder = data.HexEncoder
  97. case "b64":
  98. data.Encoder = data.B64Encoder
  99. case "btc":
  100. data.Encoder = base58.BTCEncoder
  101. default:
  102. return errors.Errorf("Unsupported encoding: %s", enc)
  103. }
  104. // store the keys directory
  105. keyDir = viper.GetString("keydir")
  106. if !filepath.IsAbs(keyDir) {
  107. keyDir = filepath.Join(rootDir, keyDir)
  108. }
  109. // and construct the key manager
  110. manager = cryptostore.New(
  111. cryptostore.SecretBox,
  112. filestorage.New(keyDir),
  113. )
  114. return nil
  115. }