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.
This commit is contained in:
Wilmer Paulino 2021-02-17 16:24:11 -08:00
parent 35b4b237c9
commit 7fa80abc44
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F
3 changed files with 29 additions and 20 deletions

View file

@ -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 // We'll also derive any private keys that are pending due to
// them being created while the address manager was locked. // them being created while the address manager was locked.
for _, info := range manager.deriveOnUnlock { for _, info := range manager.deriveOnUnlock {
addressKey, _, err := manager.deriveKeyFromPath( addressKey, _, _, err := manager.deriveKeyFromPath(
ns, info.managedAddr.InternalAccount(), ns, info.managedAddr.InternalAccount(),
info.branch, info.index, true, info.branch, info.index, true,
) )

View file

@ -72,6 +72,12 @@ type DerivationPath struct {
// Index is the final child in the derivation path. This denotes the // Index is the final child in the derivation path. This denotes the
// key index within as a child of the account and branch. // key index within as a child of the account and branch.
Index uint32 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 // KeyScope represents a restricted key scope from the primary root key within
@ -444,10 +450,11 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket,
index-- index--
} }
lastExtAddrPath := DerivationPath{ lastExtAddrPath := DerivationPath{
InternalAccount: account, InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(), Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: branch, Branch: branch,
Index: index, Index: index,
MasterKeyFingerprint: acctInfo.masterKeyFingerprint,
} }
lastExtKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey) lastExtKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey)
if err != nil { if err != nil {
@ -465,10 +472,11 @@ func (s *ScopedKeyManager) loadAccountInfo(ns walletdb.ReadBucket,
index-- index--
} }
lastIntAddrPath := DerivationPath{ lastIntAddrPath := DerivationPath{
InternalAccount: account, InternalAccount: account,
Account: acctInfo.acctKeyPub.ChildIndex(), Account: acctInfo.acctKeyPub.ChildIndex(),
Branch: branch, Branch: branch,
Index: index, Index: index,
MasterKeyFingerprint: acctInfo.masterKeyFingerprint,
} }
lastIntKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey) lastIntKey, err := s.deriveKey(acctInfo, branch, index, hasPrivateKey)
if err != nil { if err != nil {
@ -580,7 +588,7 @@ func (s *ScopedKeyManager) DeriveFromKeyPath(ns walletdb.ReadBucket,
watchOnly := s.rootManager.WatchOnly() watchOnly := s.rootManager.WatchOnly()
private := !s.rootManager.IsLocked() && !watchOnly private := !s.rootManager.IsLocked() && !watchOnly
addrKey, _, err := s.deriveKeyFromPath( addrKey, _, _, err := s.deriveKeyFromPath(
ns, kp.InternalAccount, kp.Branch, kp.Index, private, ns, kp.InternalAccount, kp.Branch, kp.Index, private,
) )
if err != nil { 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. // This function MUST be called with the manager lock held for writes.
func (s *ScopedKeyManager) deriveKeyFromPath(ns walletdb.ReadBucket, func (s *ScopedKeyManager) deriveKeyFromPath(ns walletdb.ReadBucket,
internalAccount, branch, index uint32, private bool) ( internalAccount, branch, index uint32, private bool) (
*hdkeychain.ExtendedKey, *hdkeychain.ExtendedKey, error) { *hdkeychain.ExtendedKey, *hdkeychain.ExtendedKey, uint32, error) {
// Look up the account key information. // Look up the account key information.
acctInfo, err := s.loadAccountInfo(ns, internalAccount) acctInfo, err := s.loadAccountInfo(ns, internalAccount)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, 0, err
} }
private = private && acctInfo.acctKeyPriv != nil private = private && acctInfo.acctKeyPriv != nil
addrKey, err := s.deriveKey(acctInfo, branch, index, private) addrKey, err := s.deriveKey(acctInfo, branch, index, private)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, 0, err
} }
acctKey := acctInfo.acctKeyPub acctKey := acctInfo.acctKeyPub
@ -620,7 +628,7 @@ func (s *ScopedKeyManager) deriveKeyFromPath(ns walletdb.ReadBucket,
acctKey = acctInfo.acctKeyPriv acctKey = acctInfo.acctKeyPriv
} }
return addrKey, acctKey, nil return addrKey, acctKey, acctInfo.masterKeyFingerprint, nil
} }
// chainAddressRowToManaged returns a new managed address based on chained // 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. // function, we use the internal isLocked to avoid a deadlock.
private := !s.rootManager.isLocked() && !s.rootManager.watchOnly() 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, ns, row.account, row.branch, row.index, private,
) )
if err != nil { if err != nil {
@ -647,10 +655,11 @@ func (s *ScopedKeyManager) chainAddressRowToManaged(ns walletdb.ReadBucket,
} }
return s.keyToManaged( return s.keyToManaged(
addressKey, DerivationPath{ addressKey, DerivationPath{
InternalAccount: row.account, InternalAccount: row.account,
Account: acctKey.ChildIndex(), Account: acctKey.ChildIndex(),
Branch: row.branch, Branch: row.branch,
Index: row.index, Index: row.index,
MasterKeyFingerprint: masterKeyFingerprint,
}, acctInfo, }, acctInfo,
) )
} }

View file

@ -155,7 +155,7 @@ func (w *Wallet) FetchInputInfo(prevOut *wire.OutPoint) (*wire.MsgTx,
PkScript: pkScript, PkScript: pkScript,
}, &psbt.Bip32Derivation{ }, &psbt.Bip32Derivation{
PubKey: pubKeyAddr.PubKey().SerializeCompressed(), PubKey: pubKeyAddr.PubKey().SerializeCompressed(),
MasterKeyFingerprint: 0, // TODO MasterKeyFingerprint: derivationPath.MasterKeyFingerprint,
Bip32Path: []uint32{ Bip32Path: []uint32{
keyScope.Purpose + hdkeychain.HardenedKeyStart, keyScope.Purpose + hdkeychain.HardenedKeyStart,
keyScope.Coin + hdkeychain.HardenedKeyStart, keyScope.Coin + hdkeychain.HardenedKeyStart,