github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/cmd/hdwallet/hdwalletderive.go (about) 1 // Copyright (c) 2022 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package hdwallet 7 8 import ( 9 "bytes" 10 "fmt" 11 "os" 12 "path/filepath" 13 14 ecrypt "github.com/ethereum/go-ethereum/crypto" 15 hdwallet "github.com/miguelmota/go-ethereum-hdwallet" 16 "github.com/spf13/cobra" 17 18 "github.com/iotexproject/go-pkgs/crypto" 19 "github.com/iotexproject/iotex-core/ioctl/config" 20 "github.com/iotexproject/iotex-core/ioctl/output" 21 "github.com/iotexproject/iotex-core/ioctl/util" 22 "github.com/iotexproject/iotex-core/pkg/util/fileutil" 23 ) 24 25 // Multi-language support 26 var ( 27 _hdwalletDeriveCmdShorts = map[config.Language]string{ 28 config.English: "derive key from HDWallet", 29 config.Chinese: "查询HDWallet钱包的派生key地址", 30 } 31 ) 32 33 // _hdwalletDeriveCmd represents the hdwallet derive command 34 var _hdwalletDeriveCmd = &cobra.Command{ 35 Use: "derive id1/id2/id3", 36 Short: config.TranslateInLang(_hdwalletDeriveCmdShorts, config.UILanguage), 37 Args: cobra.ExactArgs(1), 38 RunE: func(cmd *cobra.Command, args []string) error { 39 cmd.SilenceUsage = true 40 err := hdwalletDerive(args[0]) 41 return output.PrintError(err) 42 }, 43 } 44 45 func hdwalletDerive(path string) error { 46 signer := "hdw::" + path 47 account, change, index, err := util.ParseHdwPath(signer) 48 if err != nil { 49 return output.NewError(output.InputError, "invalid hdwallet key format", err) 50 } 51 52 output.PrintQuery("Enter password\n") 53 password, err := util.ReadSecretFromStdin() 54 if err != nil { 55 return output.NewError(output.InputError, "failed to get password", err) 56 } 57 58 addr, _, err := DeriveKey(account, change, index, password) 59 if err != nil { 60 return err 61 } 62 output.PrintResult(fmt.Sprintf("address: %s\n", addr)) 63 return nil 64 } 65 66 // DeriveKey derives the key according to path 67 func DeriveKey(account, change, index uint32, password string) (string, crypto.PrivateKey, error) { 68 // derive key as "m/44'/304'/account'/change/index" 69 hdWalletConfigFile := config.ReadConfig.Wallet + "/hdwallet" 70 if !fileutil.FileExists(hdWalletConfigFile) { 71 return "", nil, output.NewError(output.InputError, "Run 'ioctl hdwallet create' to create your HDWallet first.", nil) 72 } 73 74 enctxt, err := os.ReadFile(filepath.Clean(hdWalletConfigFile)) 75 if err != nil { 76 return "", nil, output.NewError(output.InputError, "failed to read config", err) 77 } 78 79 enckey := util.HashSHA256([]byte(password)) 80 dectxt, err := util.Decrypt(enctxt, enckey) 81 if err != nil { 82 return "", nil, output.NewError(output.InputError, "failed to decrypt", err) 83 } 84 85 dectxtLen := len(dectxt) 86 if dectxtLen <= 32 { 87 return "", nil, output.NewError(output.ValidationError, "incorrect data", nil) 88 } 89 90 mnemonic, hash := dectxt[:dectxtLen-32], dectxt[dectxtLen-32:] 91 if !bytes.Equal(hash, util.HashSHA256(mnemonic)) { 92 return "", nil, output.NewError(output.ValidationError, "password error", nil) 93 } 94 95 wallet, err := hdwallet.NewFromMnemonic(string(mnemonic)) 96 if err != nil { 97 return "", nil, err 98 } 99 100 derivationPath := fmt.Sprintf("%s/%d'/%d/%d", DefaultRootDerivationPath, account, change, index) 101 path := hdwallet.MustParseDerivationPath(derivationPath) 102 walletAccount, err := wallet.Derive(path, false) 103 if err != nil { 104 return "", nil, output.NewError(output.InputError, "failed to get account by derive path", err) 105 } 106 107 private, err := wallet.PrivateKey(walletAccount) 108 if err != nil { 109 return "", nil, output.NewError(output.InputError, "failed to get private key", err) 110 } 111 prvKey, err := crypto.BytesToPrivateKey(ecrypt.FromECDSA(private)) 112 if err != nil { 113 return "", nil, output.NewError(output.InputError, "failed to Bytes private key", err) 114 } 115 116 addr := prvKey.PublicKey().Address() 117 if addr == nil { 118 return "", nil, output.NewError(output.ConvertError, "failed to convert public key into address", nil) 119 } 120 return addr.String(), prvKey, nil 121 }