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.
This commit is contained in:
Wilmer Paulino 2021-02-15 14:38:23 -08:00
parent 198b0b8dae
commit 89e1671f0c
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F
2 changed files with 59 additions and 18 deletions

View file

@ -1947,7 +1947,8 @@ func testManagerCase(t *testing.T, caseName string,
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
_, err = scopedMgr.NewAccountWatchingOnly( _, err = scopedMgr.NewAccountWatchingOnly(
ns, defaultAccountName, acctKeyPub) ns, defaultAccountName, acctKeyPub, 0, nil,
)
return err return err
}) })
if err != nil { if err != nil {
@ -2648,7 +2649,9 @@ func TestNewRawAccountWatchingOnly(t *testing.T) {
const accountNum = 1000 const accountNum = 1000
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return scopedMgr.NewRawAccountWatchingOnly(ns, accountNum, accountKey) return scopedMgr.NewRawAccountWatchingOnly(
ns, accountNum, accountKey, 0, nil,
)
}) })
if err != nil { if err != nil {
t.Fatalf("unable to create new account: %v", err) t.Fatalf("unable to create new account: %v", err)
@ -2713,7 +2716,9 @@ func TestNewRawAccountHybrid(t *testing.T) {
const accountNum = 1000 const accountNum = 1000
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey) ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
return scopedMgr.NewRawAccountWatchingOnly(ns, accountNum, acctKeyPub) return scopedMgr.NewRawAccountWatchingOnly(
ns, accountNum, acctKeyPub, 0, nil,
)
}) })
if err != nil { if err != nil {
t.Fatalf("unable to create new account: %v", err) t.Fatalf("unable to create new account: %v", err)

View file

@ -1271,14 +1271,24 @@ func (s *ScopedKeyManager) NewRawAccount(ns walletdb.ReadWriteBucket, number uin
return s.newAccount(ns, number, name) return s.newAccount(ns, number, name)
} }
// NewRawAccountWatchingOnly creates a new watching only account for // NewRawAccountWatchingOnly creates a new watching only account for the scoped
// the scoped manager. This method differs from the // manager. This method differs from the NewAccountWatchingOnly method in that
// NewAccountWatchingOnly method in that this method takes the account // this method takes the account number *directly*, rather than taking a string
// number *directly*, rather than taking a string name for the // name for the account, then mapping that to the next highest account number.
// 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( func (s *ScopedKeyManager) NewRawAccountWatchingOnly(
ns walletdb.ReadWriteBucket, number uint32, ns walletdb.ReadWriteBucket, number uint32,
pubKey *hdkeychain.ExtendedKey) error { pubKey *hdkeychain.ExtendedKey, masterKeyFingerprint uint32,
addrSchema *ScopeAddrSchema) error {
s.mtx.Lock() s.mtx.Lock()
defer s.mtx.Unlock() 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 // derivation, we'll create a new name for this account based off of
// the account number. // the account number.
name := fmt.Sprintf("act:%v", 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 // 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. // 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() s.mtx.Lock()
defer s.mtx.Unlock() 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 // With the name validated, we'll create a new account for the new
// contiguous account. // 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 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. // 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. // NOTE: This function MUST be called with the manager lock held for writes.
func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket, account uint32, name string, func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket,
pubKey *hdkeychain.ExtendedKey) error { account uint32, name string, pubKey *hdkeychain.ExtendedKey,
masterKeyFingerprint uint32, addrSchema *ScopeAddrSchema) error {
// Validate the account name. // Validate the account name.
if err := ValidateAccountName(name); err != nil { if err := ValidateAccountName(name); err != nil {
@ -1453,15 +1489,15 @@ func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket, a
[]byte(pubKey.String()), []byte(pubKey.String()),
) )
if err != nil { if err != nil {
str := "failed to encrypt public key for account" str := "failed to encrypt public key for account"
return managerError(ErrCrypto, str, err) return managerError(ErrCrypto, str, err)
} }
// We have the encrypted account extended keys, so save them to the // We have the encrypted account extended keys, so save them to the
// database // database
// TODO: set master key fingerprint and addr schema.
err = putWatchOnlyAccountInfo( 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 { if err != nil {
return err return err