From 7fa80abc44eb79ee6db0eb5a6770ef0db4560dd1 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 17 Feb 2021 16:24:11 -0800 Subject: [PATCH] waddrmgr: include master key fingerprint in derivation path Following the previous commit, some external hardware signers require a master key fingerprint to be present within the PSBT input derivation paths so that the signer can recognize which inputs are relevant and must be signed. --- waddrmgr/manager.go | 2 +- waddrmgr/scoped_manager.go | 45 +++++++++++++++++++++++--------------- wallet/utxos.go | 2 +- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/waddrmgr/manager.go b/waddrmgr/manager.go index cf7b9aa..f8acb34 100644 --- a/waddrmgr/manager.go +++ b/waddrmgr/manager.go @@ -1218,7 +1218,7 @@ func (m *Manager) Unlock(ns walletdb.ReadBucket, passphrase []byte) error { // We'll also derive any private keys that are pending due to // them being created while the address manager was locked. for _, info := range manager.deriveOnUnlock { - addressKey, _, err := manager.deriveKeyFromPath( + addressKey, _, _, err := manager.deriveKeyFromPath( ns, info.managedAddr.InternalAccount(), info.branch, info.index, true, ) diff --git a/waddrmgr/scoped_manager.go b/waddrmgr/scoped_manager.go index 70fe48b..962ab8e 100644 --- a/waddrmgr/scoped_manager.go +++ b/waddrmgr/scoped_manager.go @@ -72,6 +72,12 @@ type DerivationPath struct { // Index is the final child in the derivation path. This denotes the // key index within as a child of the account and branch. Index uint32 + + // MasterKeyFingerprint represents the fingerprint of the root key (also + // known as the key with derivation path m/) corresponding to the + // account public key. This may be required by some hardware wallets for + // proper identification and signing. + MasterKeyFingerprint uint32 } // KeyScope represents a restricted key scope from the primary root key within @@ -444,10 +450,11 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket, index-- } lastExtAddrPath := DerivationPath{ - InternalAccount: account, - Account: acctInfo.acctKeyPub.ChildIndex(), - Branch: branch, - Index: index, + InternalAccount: account, + Account: acctInfo.acctKeyPub.ChildIndex(), + Branch: branch, + Index: index, + MasterKeyFingerprint: acctInfo.masterKeyFingerprint, } lastExtKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey) if err != nil { @@ -465,10 +472,11 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket, index-- } lastIntAddrPath := DerivationPath{ - InternalAccount: account, - Account: acctInfo.acctKeyPub.ChildIndex(), - Branch: branch, - Index: index, + InternalAccount: account, + Account: acctInfo.acctKeyPub.ChildIndex(), + Branch: branch, + Index: index, + MasterKeyFingerprint: acctInfo.masterKeyFingerprint, } lastIntKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey) if err != nil { @@ -580,7 +588,7 @@ func (s *ScopedKeyManager) DeriveFromKeyPath(ns walletdb.ReadBucket, watchOnly := s.rootManager.WatchOnly() private := !s.rootManager.IsLocked() && !watchOnly - addrKey, _, err := s.deriveKeyFromPath( + addrKey, _, _, err := s.deriveKeyFromPath( ns, kp.InternalAccount, kp.Branch, kp.Index, private, ) if err != nil { @@ -601,18 +609,18 @@ func (s *ScopedKeyManager) DeriveFromKeyPath(ns walletdb.ReadBucket, // This function MUST be called with the manager lock held for writes. func (s *ScopedKeyManager) deriveKeyFromPath(ns walletdb.ReadBucket, internalAccount, branch, index uint32, private bool) ( - *hdkeychain.ExtendedKey, *hdkeychain.ExtendedKey, error) { + *hdkeychain.ExtendedKey, *hdkeychain.ExtendedKey, uint32, error) { // Look up the account key information. acctInfo, err := s.loadAccountInfo(ns, internalAccount) if err != nil { - return nil, nil, err + return nil, nil, 0, err } private = private && acctInfo.acctKeyPriv != nil addrKey, err := s.deriveKey(acctInfo, branch, index, private) if err != nil { - return nil, nil, err + return nil, nil, 0, err } acctKey := acctInfo.acctKeyPub @@ -620,7 +628,7 @@ func (s *ScopedKeyManager) deriveKeyFromPath(ns walletdb.ReadBucket, acctKey = acctInfo.acctKeyPriv } - return addrKey, acctKey, nil + return addrKey, acctKey, acctInfo.masterKeyFingerprint, nil } // chainAddressRowToManaged returns a new managed address based on chained @@ -634,7 +642,7 @@ func (s *ScopedKeyManager) chainAddressRowToManaged(ns walletdb.ReadBucket, // function, we use the internal isLocked to avoid a deadlock. private := !s.rootManager.isLocked() && !s.rootManager.watchOnly() - addressKey, acctKey, err := s.deriveKeyFromPath( + addressKey, acctKey, masterKeyFingerprint, err := s.deriveKeyFromPath( ns, row.account, row.branch, row.index, private, ) if err != nil { @@ -647,10 +655,11 @@ func (s *ScopedKeyManager) chainAddressRowToManaged(ns walletdb.ReadBucket, } return s.keyToManaged( addressKey, DerivationPath{ - InternalAccount: row.account, - Account: acctKey.ChildIndex(), - Branch: row.branch, - Index: row.index, + InternalAccount: row.account, + Account: acctKey.ChildIndex(), + Branch: row.branch, + Index: row.index, + MasterKeyFingerprint: masterKeyFingerprint, }, acctInfo, ) } diff --git a/wallet/utxos.go b/wallet/utxos.go index d22cc90..dc1f209 100644 --- a/wallet/utxos.go +++ b/wallet/utxos.go @@ -155,7 +155,7 @@ func (w *Wallet) FetchInputInfo(prevOut *wire.OutPoint) (*wire.MsgTx, PkScript: pkScript, }, &psbt.Bip32Derivation{ PubKey: pubKeyAddr.PubKey().SerializeCompressed(), - MasterKeyFingerprint: 0, // TODO + MasterKeyFingerprint: derivationPath.MasterKeyFingerprint, Bip32Path: []uint32{ keyScope.Purpose + hdkeychain.HardenedKeyStart, keyScope.Coin + hdkeychain.HardenedKeyStart,