@@ -143,87 +143,200 @@ func execAdd(cfg *AddCfg, args []string, io commands.IO) error {
143143 return fmt .Errorf ("unable to read keybase, %w" , err )
144144 }
145145
146- // Check if the key exists
147- exists , err := kb .HasByName (name )
148- if err != nil {
149- return fmt .Errorf ("unable to fetch key, %w" , err )
146+ getMnemonic := func () (string , error ) {
147+ switch {
148+ case cfg .Recover :
149+ bip39Message := "Enter your bip39 mnemonic"
150+ var mnemonic string
151+ var err error
152+ if cfg .Masked {
153+ mnemonic , err = io .GetPassword (bip39Message , false )
154+ } else {
155+ mnemonic , err = io .GetString (bip39Message )
156+ }
157+ if err != nil {
158+ return "" , fmt .Errorf ("unable to parse mnemonic, %w" , err )
159+ }
160+
161+ // Make sure it's valid
162+ if ! bip39 .IsMnemonicValid (mnemonic ) {
163+ return "" , errInvalidMnemonic
164+ }
165+
166+ return mnemonic , nil
167+ case cfg .Entropy :
168+ // Generate mnemonic using custom entropy
169+ mnemonic , err := GenerateMnemonicWithCustomEntropy (io , cfg .Masked )
170+ if err != nil {
171+ return "" , fmt .Errorf ("unable to generate mnemonic with custom entropy, %w" , err )
172+ }
173+
174+ return mnemonic , nil
175+ default :
176+ // Generate mnemonic using computer PRNG
177+ mnemonic , err := GenerateMnemonic (mnemonicEntropySize )
178+ if err != nil {
179+ return "" , fmt .Errorf ("unable to generate mnemonic, %w" , err )
180+ }
181+
182+ return mnemonic , nil
183+ }
150184 }
151185
152- // Get overwrite confirmation, if any
153- if exists {
154- overwrite , err := io .GetConfirmation (fmt .Sprintf ("Override the existing name %s" , name ))
186+ var (
187+ infos []keys.Info
188+ mnemonic string
189+ )
190+
191+ if len (cfg .DerivationPath ) == 0 {
192+ // Check if the key exists
193+ exists , err := kb .HasByName (name )
155194 if err != nil {
156- return fmt .Errorf ("unable to get confirmation , %w" , err )
195+ return fmt .Errorf ("unable to fetch key , %w" , err )
157196 }
158197
159- if ! overwrite {
160- return errOverwriteAborted
161- }
162- }
198+ // Get overwrite confirmation, if any
199+ if exists {
200+ overwrite , err := io .GetConfirmation (fmt .Sprintf ("Override the existing name %s" , name ))
201+ if err != nil {
202+ return fmt .Errorf ("unable to get confirmation, %w" , err )
203+ }
163204
164- // Ask for a password when generating a local key
165- pw , err := promptPassphrase (io , cfg .RootCfg .InsecurePasswordStdin )
166- if err != nil {
167- return err
168- }
205+ if ! overwrite {
206+ return errOverwriteAborted
207+ }
208+ }
169209
170- var mnemonic string
210+ // Ask for a password when generating a local key
211+ pw , err := promptPassphrase (io , cfg .RootCfg .InsecurePasswordStdin )
212+ if err != nil {
213+ return err
214+ }
171215
172- switch {
173- case cfg .Recover :
174- bip39Message := "Enter your bip39 mnemonic"
175- if cfg .Masked {
176- mnemonic , err = io .GetPassword (bip39Message , false )
177- } else {
178- mnemonic , err = io .GetString (bip39Message )
216+ mnemonic , err = getMnemonic ()
217+ if err != nil {
218+ return err
179219 }
220+
221+ // Save the account
222+ info , err := kb .CreateAccount (
223+ name ,
224+ mnemonic ,
225+ "" ,
226+ pw ,
227+ uint32 (cfg .Account ),
228+ uint32 (cfg .Index ),
229+ )
180230 if err != nil {
181- return fmt .Errorf ("unable to parse mnemonic , %w" , err )
231+ return fmt .Errorf ("unable to save account to keybase , %w" , err )
182232 }
183233
184- // Make sure it's valid
185- if ! bip39 .IsMnemonicValid (mnemonic ) {
186- return errInvalidMnemonic
234+ infos = []keys.Info {info }
235+ } else {
236+ type deriveEntry struct {
237+ name string
238+ params * hd.BIP44Params
187239 }
188- case cfg .Entropy :
189- // Generate mnemonic using custom entropy
190- mnemonic , err = GenerateMnemonicWithCustomEntropy (io , cfg .Masked )
191- if err != nil {
192- return fmt .Errorf ("unable to generate mnemonic with custom entropy, %w" , err )
240+
241+ entries := make ([]deriveEntry , 0 , len (cfg .DerivationPath ))
242+
243+ for _ , path := range cfg .DerivationPath {
244+ params , err := hd .NewParamsFromPath (path )
245+ if err != nil {
246+ return fmt .Errorf ("unable to parse derivation path, %w" , err )
247+ }
248+
249+ derivedName := deriveKeyName (name , params , len (cfg .DerivationPath ))
250+
251+ exists , err := kb .HasByName (derivedName )
252+ if err != nil {
253+ return fmt .Errorf ("unable to fetch key, %w" , err )
254+ }
255+
256+ if exists {
257+ overwrite , err := io .GetConfirmation (fmt .Sprintf ("Override the existing name %s" , derivedName ))
258+ if err != nil {
259+ return fmt .Errorf ("unable to get confirmation, %w" , err )
260+ }
261+
262+ if ! overwrite {
263+ return errOverwriteAborted
264+ }
265+ }
266+
267+ entries = append (entries , deriveEntry {
268+ name : derivedName ,
269+ params : params ,
270+ })
193271 }
194- default :
195- // Generate mnemonic using computer PRNG
196- mnemonic , err = GenerateMnemonic (mnemonicEntropySize )
272+
273+ mnemonic , err = getMnemonic ()
197274 if err != nil {
198- return fmt . Errorf ( "unable to generate mnemonic, %w" , err )
275+ return err
199276 }
200- }
201277
202- // Save the account
203- info , err := kb .CreateAccount (
204- name ,
205- mnemonic ,
206- "" ,
207- pw ,
208- uint32 (cfg .Account ),
209- uint32 (cfg .Index ),
210- )
211- if err != nil {
212- return fmt .Errorf ("unable to save account to keybase, %w" , err )
278+ infos = make ([]keys.Info , 0 , len (entries ))
279+
280+ if len (entries ) == 1 {
281+ // Ask for a password when generating a local key
282+ pw , err := promptPassphrase (io , cfg .RootCfg .InsecurePasswordStdin )
283+ if err != nil {
284+ return err
285+ }
286+
287+ entry := entries [0 ]
288+ info , err := kb .CreateAccountBip44 (
289+ entry .name ,
290+ mnemonic ,
291+ "" ,
292+ pw ,
293+ * entry .params ,
294+ )
295+ if err != nil {
296+ return fmt .Errorf ("unable to save account to keybase, %w" , err )
297+ }
298+
299+ infos = append (infos , info )
300+ } else {
301+ for _ , entry := range entries {
302+ // Ask for a password when generating a local key
303+ pw , err := promptPassphrase (io , cfg .RootCfg .InsecurePasswordStdin )
304+ if err != nil {
305+ return err
306+ }
307+
308+ info , err := kb .CreateAccountBip44 (
309+ entry .name ,
310+ mnemonic ,
311+ "" ,
312+ pw ,
313+ * entry .params ,
314+ )
315+ if err != nil {
316+ return fmt .Errorf ("unable to save account to keybase, %w" , err )
317+ }
318+
319+ infos = append (infos , info )
320+ }
321+ }
213322 }
214323
215324 // Print the derived address info
216325 printDerive (mnemonic , cfg .DerivationPath , io )
217326
218327 // Recover key from seed passphrase
219328 if cfg .Recover {
220- printCreate (info , false , "" , io )
329+ for _ , info := range infos {
330+ printCreate (info , false , "" , io )
331+ }
221332
222333 return nil
223334 }
224335
225- // Print the key create info
226- printCreate (info , ! cfg .NoBackup , mnemonic , io )
336+ // Print the key create info (mnemonic only once)
337+ for i , info := range infos {
338+ printCreate (info , ! cfg .NoBackup && i == 0 , mnemonic , io )
339+ }
227340
228341 return nil
229342}
@@ -310,6 +423,14 @@ func printDerive(
310423 }
311424}
312425
426+ func deriveKeyName (base string , params * hd.BIP44Params , totalPaths int ) string {
427+ if totalPaths == 1 {
428+ return base
429+ }
430+
431+ return fmt .Sprintf ("%s-%d-%d" , base , params .Account , params .AddressIndex )
432+ }
433+
313434// generateAccounts the accounts using the provided mnemonics
314435func generateAccounts (mnemonic string , paths []string ) []crypto.Address {
315436 addresses := make ([]crypto.Address , len (paths ))
0 commit comments