From b97681bd96502d81ec6e5277b7e9f88e47e3393b Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Wed, 25 Mar 2020 16:03:16 -0700 Subject: [PATCH] Added Wallet and Loader watching-only wallet creation. --- wallet/loader.go | 36 +++++++++++++++++++---- wallet/wallet.go | 57 +++++++++++++++++++++++++------------ wallet/watchingonly_test.go | 34 ++++++++++++++++++++++ 3 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 wallet/watchingonly_test.go diff --git a/wallet/loader.go b/wallet/loader.go index 5ae807e3fc..9cb77341b5 100644 --- a/wallet/loader.go +++ b/wallet/loader.go @@ -100,6 +100,25 @@ func (l *Loader) RunAfterLoad(fn func(*Wallet)) { func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte, bday time.Time) (*Wallet, error) { + return l.createNewWalletInternal( + pubPassphrase, privPassphrase, seed, bday, false, + ) +} + +// CreateNewWatchingOnlyWallet creates a new wallet using the provided +// public passphrase. No seed or private passphrase may be provided +// since the wallet is watching-only. +func (l *Loader) CreateNewWatchingOnlyWallet(pubPassphrase []byte, + bday time.Time) (*Wallet, error) { + + return l.createNewWalletInternal( + pubPassphrase, nil, nil, bday, true, + ) +} + +func (l *Loader) createNewWalletInternal(pubPassphrase, privPassphrase, + seed []byte, bday time.Time, isWatchingOnly bool) (*Wallet, error) { + defer l.mu.Unlock() l.mu.Lock() @@ -127,11 +146,18 @@ func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte, } // Initialize the newly created database for the wallet before opening. - err = Create( - db, pubPassphrase, privPassphrase, seed, l.chainParams, bday, - ) - if err != nil { - return nil, err + if isWatchingOnly { + err = CreateWatchingOnly(db, pubPassphrase, l.chainParams, bday) + if err != nil { + return nil, err + } + } else { + err = Create( + db, pubPassphrase, privPassphrase, seed, l.chainParams, bday, + ) + if err != nil { + return nil, err + } } // Open the newly-created wallet. diff --git a/wallet/wallet.go b/wallet/wallet.go index a3e092cb3d..a00588a99f 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -3620,23 +3620,45 @@ func (w *Wallet) Database() walletdb.DB { // Create creates an new wallet, writing it to an empty database. If the passed // seed is non-nil, it is used. Otherwise, a secure random seed of the // recommended length is generated. -func Create(db walletdb.DB, pubPass, privPass, seed []byte, params *chaincfg.Params, - birthday time.Time) error { - - // If a seed was provided, ensure that it is of valid length. Otherwise, - // we generate a random seed for the wallet with the recommended seed - // length. - if seed == nil { - hdSeed, err := hdkeychain.GenerateSeed( - hdkeychain.RecommendedSeedLen) - if err != nil { - return err +func Create(db walletdb.DB, pubPass, privPass, seed []byte, + params *chaincfg.Params, birthday time.Time) error { + + return createInternal( + db, pubPass, privPass, seed, params, birthday, false, + ) +} + +// CreateWatchingOnly creates an new watch-only wallet, writing it to +// an empty database. No seed can be provided as this wallet will be +// watching only. Likewise no private passphrase may be provided +// either. +func CreateWatchingOnly(db walletdb.DB, pubPass []byte, + params *chaincfg.Params, birthday time.Time) error { + + return createInternal( + db, pubPass, nil, nil, params, birthday, true, + ) +} + +func createInternal(db walletdb.DB, pubPass, privPass, seed []byte, + params *chaincfg.Params, birthday time.Time, isWatchingOnly bool) error { + + if !isWatchingOnly { + // If a seed was provided, ensure that it is of valid length. Otherwise, + // we generate a random seed for the wallet with the recommended seed + // length. + if seed == nil { + hdSeed, err := hdkeychain.GenerateSeed( + hdkeychain.RecommendedSeedLen) + if err != nil { + return err + } + seed = hdSeed + } + if len(seed) < hdkeychain.MinSeedBytes || + len(seed) > hdkeychain.MaxSeedBytes { + return hdkeychain.ErrInvalidSeedLen } - seed = hdSeed - } - if len(seed) < hdkeychain.MinSeedBytes || - len(seed) > hdkeychain.MaxSeedBytes { - return hdkeychain.ErrInvalidSeedLen } return walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { @@ -3650,8 +3672,7 @@ func Create(db walletdb.DB, pubPass, privPass, seed []byte, params *chaincfg.Par } err = waddrmgr.Create( - addrmgrNs, seed, pubPass, privPass, params, nil, - birthday, + addrmgrNs, seed, pubPass, privPass, params, nil, birthday, ) if err != nil { return err diff --git a/wallet/watchingonly_test.go b/wallet/watchingonly_test.go new file mode 100644 index 0000000000..f7846b1cc2 --- /dev/null +++ b/wallet/watchingonly_test.go @@ -0,0 +1,34 @@ +// Copyright (c) 2018 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package wallet + +import ( + "io/ioutil" + "os" + "testing" + "time" + + "github.com/btcsuite/btcd/chaincfg" + _ "github.com/btcsuite/btcwallet/walletdb/bdb" +) + +// TestCreateWatchingOnly checks that we can construct a watching-only +// wallet. +func TestCreateWatchingOnly(t *testing.T) { + // Set up a wallet. + dir, err := ioutil.TempDir("", "watchingonly_test") + if err != nil { + t.Fatalf("Failed to create db dir: %v", err) + } + defer os.RemoveAll(dir) + + pubPass := []byte("hello") + + loader := NewLoader(&chaincfg.TestNet3Params, dir, true, 250) + _, err = loader.CreateNewWatchingOnlyWallet(pubPass, time.Now()) + if err != nil { + t.Fatalf("unable to create wallet: %v", err) + } +}