github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/client/keys/add.go (about) 1 package keys 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "fmt" 8 "io" 9 "sort" 10 "strings" 11 12 bip39 "github.com/bartekn/go-bip39" 13 14 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/flags" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/input" 16 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/crypto/keys" 17 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 18 19 "github.com/spf13/cobra" 20 "github.com/spf13/viper" 21 22 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto" 23 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto/multisig" 24 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/cli" 25 ) 26 27 const ( 28 flagInteractive = "interactive" 29 flagRecover = "recover" 30 flagNoBackup = "no-backup" 31 flagDryRun = "dry-run" 32 flagAccount = "account" 33 flagIndex = "index" 34 flagCointype = "coin-type" 35 flagMultisig = "multisig" 36 flagNoSort = "nosort" 37 flagHDPath = "hd-path" 38 flagKeyAlgo = "algo" 39 40 // DefaultKeyPass contains the default key password for genesis transactions 41 DefaultKeyPass = "12345678" 42 flagMnemonic = "mnemonic" 43 ) 44 45 // AddKeyCommand defines a keys command to add a generated or recovered private key to keybase. 46 func AddKeyCommand() *cobra.Command { 47 cmd := &cobra.Command{ 48 Use: "add <name>", 49 Short: "Add an encrypted private key (either newly generated or recovered), encrypt it, and save to disk", 50 Long: `Derive a new private key and encrypt to disk. 51 Optionally specify a BIP39 mnemonic, a BIP39 passphrase to further secure the mnemonic, 52 and a bip32 HD path to derive a specific account. The key will be stored under the given name 53 and encrypted with the given password. The only input that is required is the encryption password. 54 55 If run with -i, it will prompt the user for BIP44 path, BIP39 mnemonic, and passphrase. 56 The flag --recover allows one to recover a key from a seed passphrase. 57 If run with --dry-run, a key would be generated (or recovered) but not stored to the 58 local keystore. 59 Use the --pubkey flag to add arbitrary public keys to the keystore for constructing 60 multisig transactions. 61 62 You can add a multisig key by passing the list of key names you want the public 63 key to be composed of to the --multisig flag and the minimum number of signatures 64 required through --multisig-threshold. The keys are sorted by address, unless 65 the flag --nosort is set. 66 `, 67 Args: cobra.ExactArgs(1), 68 RunE: runAddCmd, 69 } 70 cmd.Flags().StringSlice(flagMultisig, nil, "Construct and store a multisig public key (implies --pubkey)") 71 cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures. For use in conjunction with --multisig") 72 cmd.Flags().Bool(flagNoSort, false, "Keys passed to --multisig are taken in the order they're supplied") 73 cmd.Flags().String(FlagPublicKey, "", "Parse a public key in bech32 format and save it to disk") 74 cmd.Flags().BoolP(flagInteractive, "i", false, "Interactively prompt user for BIP39 passphrase and mnemonic") 75 cmd.Flags().Bool(flags.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device") 76 cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating") 77 cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") 78 cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") 79 cmd.Flags().String(flagHDPath, "", "Manual HD Path derivation (overrides BIP44 config)") 80 cmd.Flags().Uint32(flagAccount, 0, "Account number for HD derivation") 81 cmd.Flags().Uint32(flagIndex, 0, "Address index number for HD derivation") 82 cmd.Flags().Uint32(flagCointype, 60, "Coin type for HD derivation") 83 cmd.Flags().Bool(flags.FlagIndentResponse, false, "Add indent to JSON response") 84 cmd.Flags().String(flagKeyAlgo, string(keys.Secp256k1), "Key signing algorithm to generate keys for") 85 86 cmd.Flags().BoolP(flagYes, "y", false, "Overwrite the existing account without confirmation") 87 cmd.Flags().StringP(flagMnemonic, "m", "", "Mnemonic words or private key") 88 return cmd 89 } 90 91 func getKeybase(transient bool, buf io.Reader) (keys.Keybase, error) { 92 if transient { 93 return keys.NewInMemory(), nil 94 } 95 96 return keys.NewKeyring(sdk.KeyringServiceName(), viper.GetString(flags.FlagKeyringBackend), viper.GetString(flags.FlagHome), buf) 97 } 98 99 func runAddCmd(cmd *cobra.Command, args []string) error { 100 inBuf := bufio.NewReader(cmd.InOrStdin()) 101 kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf) 102 if err != nil { 103 return err 104 } 105 106 return RunAddCmd(cmd, args, kb, inBuf) 107 } 108 109 /* 110 input 111 - bip39 mnemonic 112 - bip39 passphrase 113 - bip44 path 114 - local encryption password 115 116 output 117 - armor encrypted private key (saved to file) 118 */ 119 func RunAddCmd(cmd *cobra.Command, args []string, kb keys.Keybase, inBuf *bufio.Reader) error { 120 var err error 121 122 name := args[0] 123 124 interactive := viper.GetBool(flagInteractive) 125 showMnemonic := !viper.GetBool(flagNoBackup) 126 127 algo := keys.SigningAlgo(viper.GetString(flagKeyAlgo)) 128 if algo == keys.SigningAlgo("") { 129 algo = keys.Secp256k1 130 } 131 if !keys.IsSupportedAlgorithm(kb.SupportedAlgos(), algo) { 132 return keys.ErrUnsupportedSigningAlgo 133 } 134 135 if !viper.GetBool(flagDryRun) { 136 ask := !viper.GetBool(flagYes) 137 _, err = kb.Get(name) 138 if err == nil && ask { 139 // account exists, ask for user confirmation 140 response, err2 := input.GetConfirmation(fmt.Sprintf("override the existing name %s", name), inBuf) 141 if err2 != nil { 142 return err2 143 } 144 if !response { 145 return errors.New("aborted") 146 } 147 } 148 149 multisigKeys := viper.GetStringSlice(flagMultisig) 150 if len(multisigKeys) != 0 { 151 var pks []crypto.PubKey 152 153 multisigThreshold := viper.GetInt(flagMultiSigThreshold) 154 if err := validateMultisigThreshold(multisigThreshold, len(multisigKeys)); err != nil { 155 return err 156 } 157 158 for _, keyname := range multisigKeys { 159 k, err := kb.Get(keyname) 160 if err != nil { 161 return err 162 } 163 pks = append(pks, k.GetPubKey()) 164 } 165 166 // Handle --nosort 167 if !viper.GetBool(flagNoSort) { 168 sort.Slice(pks, func(i, j int) bool { 169 return bytes.Compare(pks[i].Address(), pks[j].Address()) < 0 170 }) 171 } 172 173 pk := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) 174 if _, err := kb.CreateMulti(name, pk); err != nil { 175 return err 176 } 177 178 cmd.PrintErrf("Key %q saved to disk.\n", name) 179 return nil 180 } 181 } 182 183 if viper.GetString(FlagPublicKey) != "" { 184 pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, viper.GetString(FlagPublicKey)) 185 if err != nil { 186 return err 187 } 188 _, err = kb.CreateOffline(name, pk, algo) 189 if err != nil { 190 return err 191 } 192 return nil 193 } 194 195 account := uint32(viper.GetInt(flagAccount)) 196 index := uint32(viper.GetInt(flagIndex)) 197 cointype := uint32(viper.GetInt(flagCointype)) 198 199 useBIP44 := !viper.IsSet(flagHDPath) 200 var hdPath string 201 202 if useBIP44 { 203 hdPath = keys.CreateHDPathEx(cointype, account, index).String() 204 } else { 205 hdPath = viper.GetString(flagHDPath) 206 } 207 208 // If we're using ledger, only thing we need is the path and the bech32 prefix. 209 if viper.GetBool(flags.FlagUseLedger) { 210 211 if !useBIP44 { 212 return errors.New("cannot set custom bip32 path with ledger") 213 } 214 215 if !keys.IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) { 216 return keys.ErrUnsupportedSigningAlgo 217 } 218 219 bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix() 220 info, err := kb.CreateLedger(name, keys.Secp256k1, bech32PrefixAccAddr, account, index) 221 if err != nil { 222 return err 223 } 224 225 return printCreate(cmd, info, false, "") 226 } 227 228 // Get bip39 mnemonic 229 var mnemonic, bip39Passphrase string 230 231 recover, _ := cmd.Flags().GetBool(flagRecover) 232 if recover { 233 mnemonic = viper.GetString(flagMnemonic) 234 235 if len(mnemonic) == 0 { 236 return errors.New("mnemonic is needed") 237 } 238 if strings.Contains(mnemonic, " ") && !bip39.IsMnemonicValid(mnemonic) { 239 return errors.New("invalid mnemonic") 240 } 241 242 } else if interactive { 243 mnemonic, err = input.GetString("Enter your bip39 mnemonic, or hit enter to generate one.", inBuf) 244 if err != nil { 245 return err 246 } 247 248 if !bip39.IsMnemonicValid(mnemonic) && mnemonic != "" { 249 return errors.New("invalid mnemonic") 250 } 251 } 252 253 if len(mnemonic) == 0 { 254 // read entropy seed straight from crypto.Rand and convert to mnemonic 255 entropySeed, err := bip39.NewEntropy(mnemonicEntropySize) 256 if err != nil { 257 return err 258 } 259 260 mnemonic, err = bip39.NewMnemonic(entropySeed) 261 if err != nil { 262 return err 263 } 264 } 265 266 // override bip39 passphrase 267 if interactive { 268 bip39Passphrase, err = input.GetString( 269 "Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. "+ 270 "Most users should just hit enter to use the default, \"\"", inBuf) 271 if err != nil { 272 return err 273 } 274 275 // if they use one, make them re-enter it 276 if len(bip39Passphrase) != 0 { 277 p2, err := input.GetString("Repeat the passphrase:", inBuf) 278 if err != nil { 279 return err 280 } 281 282 if bip39Passphrase != p2 { 283 return errors.New("passphrases don't match") 284 } 285 } 286 } 287 288 info, err := kb.CreateAccount(name, mnemonic, bip39Passphrase, DefaultKeyPass, hdPath, algo) 289 if err != nil { 290 return err 291 } 292 293 // Recover key from seed passphrase 294 if viper.GetBool(flagRecover) { 295 // Hide mnemonic from output 296 showMnemonic = false 297 mnemonic = "" 298 } 299 return printCreate(cmd, info, showMnemonic, mnemonic) 300 } 301 302 func printCreate(cmd *cobra.Command, info keys.Info, showMnemonic bool, mnemonic string) error { 303 output := viper.Get(cli.OutputFlag) 304 switch output { 305 case OutputFormatText: 306 cmd.PrintErrln() 307 printKeyInfo(info, keys.Bech32KeyOutput) 308 309 // print mnemonic unless requested not to. 310 if showMnemonic { 311 cmd.PrintErrln("\n**Important** write this mnemonic phrase in a safe place.") 312 cmd.PrintErrln("It is the only way to recover your account if you ever forget your password.") 313 cmd.PrintErrln("") 314 cmd.PrintErrln(mnemonic) 315 } 316 case OutputFormatJSON: 317 out, err := keys.Bech32KeyOutput(info) 318 if err != nil { 319 return err 320 } 321 322 if showMnemonic { 323 out.Mnemonic = mnemonic 324 } 325 326 var jsonString []byte 327 if viper.GetBool(flags.FlagIndentResponse) { 328 jsonString, err = KeysCdc.MarshalJSONIndent(out, "", " ") 329 } else { 330 jsonString, err = KeysCdc.MarshalJSON(out) 331 } 332 333 if err != nil { 334 return err 335 } 336 cmd.PrintErrln(string(jsonString)) 337 default: 338 return fmt.Errorf("invalid output format %s", output) 339 } 340 341 return nil 342 }