From 89e1671f0cb8b65491c462565763157bd0fbfe17 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Mon, 15 Feb 2021 14:38:23 -0800 Subject: [PATCH] waddrmgr: extend watch-only account init with new parameters The master fingerprint corresponds to the fingerprint of the root master public key (otherwise known as m/). This is required by some hardware wallets for proper identification and signing. The address schema is an optional field that allows an account to override its corresponding address schema with a custom one. --- waddrmgr/manager_test.go | 11 +++++-- waddrmgr/scoped_manager.go | 66 +++++++++++++++++++++++++++++--------- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/waddrmgr/manager_test.go b/waddrmgr/manager_test.go index dd2139f..d574302 100644 --- a/waddrmgr/manager_test.go +++ b/waddrmgr/manager_test.go @@ -1947,7 +1947,8 @@ func testManagerCase(t *testing.T, caseName string, err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) _, err = scopedMgr.NewAccountWatchingOnly( - ns, defaultAccountName, acctKeyPub) + ns, defaultAccountName, acctKeyPub, 0, nil, + ) return err }) if err != nil { @@ -2648,7 +2649,9 @@ func TestNewRawAccountWatchingOnly(t *testing.T) { const accountNum = 1000 err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) - return scopedMgr.NewRawAccountWatchingOnly(ns, accountNum, accountKey) + return scopedMgr.NewRawAccountWatchingOnly( + ns, accountNum, accountKey, 0, nil, + ) }) if err != nil { t.Fatalf("unable to create new account: %v", err) @@ -2713,7 +2716,9 @@ func TestNewRawAccountHybrid(t *testing.T) { const accountNum = 1000 err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) - return scopedMgr.NewRawAccountWatchingOnly(ns, accountNum, acctKeyPub) + return scopedMgr.NewRawAccountWatchingOnly( + ns, accountNum, acctKeyPub, 0, nil, + ) }) if err != nil { t.Fatalf("unable to create new account: %v", err) diff --git a/waddrmgr/scoped_manager.go b/waddrmgr/scoped_manager.go index 09dddab..298d175 100644 --- a/waddrmgr/scoped_manager.go +++ b/waddrmgr/scoped_manager.go @@ -1271,14 +1271,24 @@ func (s *ScopedKeyManager) NewRawAccount(ns walletdb.ReadWriteBucket, number uin return s.newAccount(ns, number, name) } -// NewRawAccountWatchingOnly creates a new watching only account for -// the scoped manager. This method differs from the -// NewAccountWatchingOnly method in that this method takes the account -// number *directly*, rather than taking a string name for the -// account, then mapping that to the next highest account number. +// NewRawAccountWatchingOnly creates a new watching only account for the scoped +// manager. This method differs from the NewAccountWatchingOnly method in that +// this method takes the account number *directly*, rather than taking a string +// name for the account, then mapping that to the next highest account number. +// +// The master key fingerprint denotes the fingerprint of the root key +// corresponding to the account public key (also known as the key with +// derivation path m/). This may be required by some hardware wallets for proper +// identification and signing. +// +// An optional address schema may also be provided to override the +// ScopedKeyManager's address schema. This will affect all addresses derived +// from the account. func (s *ScopedKeyManager) NewRawAccountWatchingOnly( ns walletdb.ReadWriteBucket, number uint32, - pubKey *hdkeychain.ExtendedKey) error { + pubKey *hdkeychain.ExtendedKey, masterKeyFingerprint uint32, + addrSchema *ScopeAddrSchema) error { + s.mtx.Lock() defer s.mtx.Unlock() @@ -1286,7 +1296,9 @@ func (s *ScopedKeyManager) NewRawAccountWatchingOnly( // derivation, we'll create a new name for this account based off of // the account number. name := fmt.Sprintf("act:%v", number) - return s.newAccountWatchingOnly(ns, number, name, pubKey) + return s.newAccountWatchingOnly( + ns, number, name, pubKey, masterKeyFingerprint, addrSchema, + ) } // NewAccount creates and returns a new account stored in the manager based on @@ -1407,8 +1419,19 @@ func (s *ScopedKeyManager) newAccount(ns walletdb.ReadWriteBucket, } // NewAccountWatchingOnly is similar to NewAccount, but for watch-only wallets. -func (s *ScopedKeyManager) NewAccountWatchingOnly(ns walletdb.ReadWriteBucket, name string, - pubKey *hdkeychain.ExtendedKey) (uint32, error) { +// +// The master key fingerprint denotes the fingerprint of the root key +// corresponding to the account public key (also known as the key with +// derivation path m/). This may be required by some hardware wallets for proper +// identification and signing. +// +// An optional address schema may also be provided to override the +// ScopedKeyManager's address schema. This will affect all addresses derived +// from the account. +func (s *ScopedKeyManager) NewAccountWatchingOnly(ns walletdb.ReadWriteBucket, + name string, pubKey *hdkeychain.ExtendedKey, masterKeyFingerprint uint32, + addrSchema *ScopeAddrSchema) (uint32, error) { + s.mtx.Lock() defer s.mtx.Unlock() @@ -1423,7 +1446,10 @@ func (s *ScopedKeyManager) NewAccountWatchingOnly(ns walletdb.ReadWriteBucket, n // With the name validated, we'll create a new account for the new // contiguous account. - if err := s.newAccountWatchingOnly(ns, account, name, pubKey); err != nil { + err = s.newAccountWatchingOnly( + ns, account, name, pubKey, masterKeyFingerprint, addrSchema, + ) + if err != nil { return 0, err } @@ -1432,9 +1458,19 @@ func (s *ScopedKeyManager) NewAccountWatchingOnly(ns walletdb.ReadWriteBucket, n // newAccountWatchingOnly is similar to newAccount, but for watching-only wallets. // +// The master key fingerprint denotes the fingerprint of the root key +// corresponding to the account public key (also known as the key with +// derivation path m/). This may be required by some hardware wallets for proper +// identification and signing. +// +// An optional address schema may also be provided to override the +// ScopedKeyManager's address schema. This will affect all addresses derived +// from the account. +// // NOTE: This function MUST be called with the manager lock held for writes. -func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket, account uint32, name string, - pubKey *hdkeychain.ExtendedKey) error { +func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket, + account uint32, name string, pubKey *hdkeychain.ExtendedKey, + masterKeyFingerprint uint32, addrSchema *ScopeAddrSchema) error { // Validate the account name. if err := ValidateAccountName(name); err != nil { @@ -1453,15 +1489,15 @@ func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket, a []byte(pubKey.String()), ) if err != nil { - str := "failed to encrypt public key for account" + str := "failed to encrypt public key for account" return managerError(ErrCrypto, str, err) } // We have the encrypted account extended keys, so save them to the // database - // TODO: set master key fingerprint and addr schema. err = putWatchOnlyAccountInfo( - ns, &s.scope, account, acctPubEnc, 0, 0, 0, name, nil, + ns, &s.scope, account, acctPubEnc, masterKeyFingerprint, 0, 0, + name, addrSchema, ) if err != nil { return err