Skip to content

Commit 25b8ae6

Browse files
committed
Prompt for account xpub imports at wallet creation
This simplifies the provisioning process as these xpubs will now be included in the address discovery and initial rescan, without manually requiring a RPC call to add them and rescan again for them.
1 parent 4fbd29a commit 25b8ae6

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

internal/prompt/prompt.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"decred.org/dcrwallet/v4/errors"
1818
"decred.org/dcrwallet/v4/walletseed"
19+
"github.com/decred/dcrd/chaincfg/v3"
1920
"github.com/decred/dcrd/hdkeychain/v3"
2021
"golang.org/x/term"
2122
)
@@ -366,6 +367,59 @@ func Seed(reader *bufio.Reader) (seed []byte, imported bool, err error) {
366367
}
367368
}
368369

370+
// ImportedAccounts prompts for any additional account names and xpubs to
371+
// import at wallet creation.
372+
func ImportedAccounts(reader *bufio.Reader, params *chaincfg.Params) (names []string, xpubs []*hdkeychain.ExtendedKey, err error) {
373+
accounts := make(map[string]struct{})
374+
accounts["default"] = struct{}{}
375+
376+
for {
377+
fmt.Printf("Do you have an additional account to import from an " +
378+
"extended public key? (enter account name, or 'no') [no]: ")
379+
reply, err := reader.ReadString('\n')
380+
if err != nil {
381+
return nil, nil, err
382+
}
383+
reply = strings.TrimSpace(reply)
384+
switch strings.ToLower(reply) {
385+
case "", "n", "no":
386+
return names, xpubs, nil
387+
case "y", "yes":
388+
continue
389+
default:
390+
}
391+
392+
account := reply
393+
if _, ok := accounts[account]; ok {
394+
fmt.Printf("Account %q is already defined\n", account)
395+
continue
396+
}
397+
fmt.Printf("Enter extended public key for account %q: ", account)
398+
reply, err = reader.ReadString('\n')
399+
if err != nil {
400+
if errors.Is(err, io.EOF) {
401+
err = io.ErrUnexpectedEOF
402+
}
403+
return nil, nil, err
404+
}
405+
reply = strings.TrimSpace(reply)
406+
xpub, err := hdkeychain.NewKeyFromString(reply, params)
407+
if err != nil {
408+
fmt.Printf("Failed to decode extended key: %v\n", err)
409+
continue
410+
}
411+
if xpub.IsPrivate() {
412+
fmt.Printf("Extended key is a private key (not neutered)\n")
413+
continue
414+
}
415+
416+
fmt.Printf("Importing account %q from extended public key\n", account)
417+
names = append(names, account)
418+
xpubs = append(xpubs, xpub)
419+
accounts[account] = struct{}{}
420+
}
421+
}
422+
369423
// collapseSpace takes a string and replaces any repeated areas of whitespace
370424
// with a single space character.
371425
func collapseSpace(in string) string {

walletsetup.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"bufio"
1010
"context"
1111
"fmt"
12+
"io"
1213
"os"
1314
"path/filepath"
1415
"strings"
@@ -121,6 +122,8 @@ func createWallet(ctx context.Context, cfg *config) error {
121122

122123
var privPass, pubPass, seed []byte
123124
var imported bool
125+
var importedAccountNames []string
126+
var importedAccountXpubs []*hdkeychain.ExtendedKey
124127
var err error
125128
c := make(chan struct{}, 1)
126129
go func() {
@@ -151,6 +154,23 @@ func createWallet(ctx context.Context, cfg *config) error {
151154
// value the user has entered which has already been validated.
152155
// There is no config flag to set the seed.
153156
seed, imported, err = prompt.Seed(r)
157+
if err != nil {
158+
return
159+
}
160+
161+
// Additional optional prompts are added after the seed. If
162+
// any of them error reading input with EOF, assume the
163+
// default answers. This allows scripts e.g. the tmux simnet
164+
// script which provide scripted input to create wallets to
165+
// continue working.
166+
167+
// Prompt for any additional xpubs to import as watching-only accounts.
168+
importedAccountNames, importedAccountXpubs, err = prompt.ImportedAccounts(
169+
r, activeNet.Params)
170+
if errors.Is(err, io.EOF) {
171+
err = nil
172+
return
173+
}
154174
}()
155175
select {
156176
case <-ctx.Done():
@@ -176,6 +196,15 @@ func createWallet(ctx context.Context, cfg *config) error {
176196
}
177197
}
178198

199+
// Import any provided account xpubs
200+
for i, name := range importedAccountNames {
201+
xpub := importedAccountXpubs[i]
202+
err := w.ImportXpubAccount(ctx, name, xpub)
203+
if err != nil {
204+
return err
205+
}
206+
}
207+
179208
// Display a mining address when creating a simnet wallet.
180209
if cfg.SimNet {
181210
err := displaySimnetMiningAddrs(seed, imported)

0 commit comments

Comments
 (0)