From bd81968215ae6b138bae701a738ae152632b1736 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 13 Aug 2018 18:44:46 -0700 Subject: [PATCH] waddrmgr: add new DerivationInfo method to managedAddress, update ScopedKeyManager In this commit, we add the new DerivationInfo method to the current default implementation of the ManagedPubKeyAddress interface. In doing this, we replace the account field with the derivationPath, as we can obtain the account field from the derivationPath itself. --- waddrmgr/address.go | 54 ++++++++++++++++++++++++++++---------- waddrmgr/scoped_manager.go | 48 ++++++++++++++++++++++++++++----- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/waddrmgr/address.go b/waddrmgr/address.go index d5ddc41..b8008ae 100644 --- a/waddrmgr/address.go +++ b/waddrmgr/address.go @@ -108,7 +108,7 @@ type ManagedPubKeyAddress interface { // that backs the address via traditional methods from the HD root. For // imported keys, the first value will be set to false to indicate that // we don't know exactly how the key was derived. - DerivationInfo() (bool, KeyScope, DerivationPath) + DerivationInfo() (KeyScope, DerivationPath, bool) } // ManagedScriptAddress extends ManagedAddress and represents a pay-to-script-hash @@ -125,7 +125,7 @@ type ManagedScriptAddress interface { // the private key associated with the public key. type managedAddress struct { manager *ScopedKeyManager - account uint32 + derivationPath DerivationPath address btcutil.Address imported bool internal bool @@ -181,7 +181,7 @@ func (a *managedAddress) lock() { // // This is part of the ManagedAddress interface implementation. func (a *managedAddress) Account() uint32 { - return a.account + return a.derivationPath.Account } // AddrType returns the address type of the managed address. This can be used @@ -316,11 +316,33 @@ func (a *managedAddress) ExportPrivKey() (*btcutil.WIF, error) { return btcutil.NewWIF(pk, a.manager.rootManager.chainParams, a.compressed) } +// Derivationinfo contains the information required to derive the key that +// backs the address via traditional methods from the HD root. For imported +// keys, the first value will be set to false to indicate that we don't know +// exactly how the key was derived. +// +// This is part of the ManagedPubKeyAddress interface implementation. +func (a *managedAddress) DerivationInfo() (KeyScope, DerivationPath, bool) { + var ( + scope KeyScope + path DerivationPath + ) + + // If this key is imported, then we can't return any information as we + // don't know precisely how the key was derived. + if a.imported { + return scope, path, false + } + + return a.manager.Scope(), a.derivationPath, true +} + // newManagedAddressWithoutPrivKey returns a new managed address based on the // passed account, public key, and whether or not the public key should be // compressed. -func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey *btcec.PublicKey, - compressed bool, addrType AddressType) (*managedAddress, error) { +func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, + derivationPath DerivationPath, pubKey *btcec.PublicKey, compressed bool, + addrType AddressType) (*managedAddress, error) { // Create a pay-to-pubkey-hash address from the public key. var pubKeyHash []byte @@ -387,7 +409,7 @@ func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey return &managedAddress{ manager: m, address: address, - account: account, + derivationPath: derivationPath, imported: false, internal: false, addrType: addrType, @@ -401,8 +423,9 @@ func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey // newManagedAddress returns a new managed address based on the passed account, // private key, and whether or not the public key is compressed. The managed // address will have access to the private and public keys. -func newManagedAddress(s *ScopedKeyManager, account uint32, privKey *btcec.PrivateKey, - compressed bool, addrType AddressType) (*managedAddress, error) { +func newManagedAddress(s *ScopedKeyManager, derivationPath DerivationPath, + privKey *btcec.PrivateKey, compressed bool, + addrType AddressType) (*managedAddress, error) { // Encrypt the private key. // @@ -419,7 +442,7 @@ func newManagedAddress(s *ScopedKeyManager, account uint32, privKey *btcec.Priva // and then add the private key to it. ecPubKey := (*btcec.PublicKey)(&privKey.PublicKey) managedAddr, err := newManagedAddressWithoutPrivKey( - s, account, ecPubKey, compressed, addrType, + s, derivationPath, ecPubKey, compressed, addrType, ) if err != nil { return nil, err @@ -434,8 +457,9 @@ func newManagedAddress(s *ScopedKeyManager, account uint32, privKey *btcec.Priva // account and extended key. The managed address will have access to the // private and public keys if the provided extended key is private, otherwise it // will only have access to the public key. -func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32, - key *hdkeychain.ExtendedKey, addrType AddressType) (*managedAddress, error) { +func newManagedAddressFromExtKey(s *ScopedKeyManager, + derivationPath DerivationPath, key *hdkeychain.ExtendedKey, + addrType AddressType) (*managedAddress, error) { // Create a new managed address based on the public or private key // depending on whether the generated key is private. @@ -446,9 +470,10 @@ func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32, return nil, err } - // Ensure the temp private key big integer is cleared after use. + // Ensure the temp private key big integer is cleared after + // use. managedAddr, err = newManagedAddress( - s, account, privKey, true, addrType, + s, derivationPath, privKey, true, addrType, ) if err != nil { return nil, err @@ -460,7 +485,8 @@ func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32, } managedAddr, err = newManagedAddressWithoutPrivKey( - s, account, pubKey, true, addrType, + s, derivationPath, pubKey, true, + addrType, ) if err != nil { return nil, err diff --git a/waddrmgr/scoped_manager.go b/waddrmgr/scoped_manager.go index ab5c8e0..dfa7a8c 100644 --- a/waddrmgr/scoped_manager.go +++ b/waddrmgr/scoped_manager.go @@ -221,11 +221,17 @@ func (s *ScopedKeyManager) keyToManaged(derivedKey *hdkeychain.ExtendedKey, addrType = s.addrSchema.ExternalAddrType } + derivationPath := DerivationPath{ + Account: account, + Branch: branch, + Index: index, + } + // Create a new managed address based on the public or private key // depending on whether the passed key is private. Also, zero the key // after creating the managed address from it. ma, err := newManagedAddressFromExtKey( - s, account, derivedKey, addrType, + s, derivationPath, derivedKey, addrType, ) defer derivedKey.Zero() if err != nil { @@ -515,9 +521,15 @@ func (s *ScopedKeyManager) importedAddressRowToManaged(row *dbImportedAddressRow return nil, managerError(ErrCrypto, str, err) } + // Since this is an imported address, we won't populate the full + // derivation path, as we don't have enough information to do so. + derivationPath := DerivationPath{ + Account: row.account, + } + compressed := len(pubBytes) == btcec.PubKeyBytesLenCompressed ma, err := newManagedAddressWithoutPrivKey( - s, row.account, pubKey, compressed, + s, derivationPath, pubKey, compressed, s.addrSchema.ExternalAddrType, ) if err != nil { @@ -740,12 +752,21 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket, break } + // Now that we know this key can be used, we'll create the + // proper derivation path so this information can be available + // to callers. + derivationPath := DerivationPath{ + Account: account, + Branch: branchNum, + Index: nextIndex - 1, + } + // Create a new managed address based on the public or private // key depending on whether the generated key is private. // Also, zero the next key after creating the managed address // from it. addr, err := newManagedAddressFromExtKey( - s, account, nextKey, addrType, + s, derivationPath, nextKey, addrType, ) if err != nil { return nil, err @@ -920,12 +941,21 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket, break } + // Now that we know this key can be used, we'll create the + // proper derivation path so this information can be available + // to callers. + derivationPath := DerivationPath{ + Account: account, + Branch: branchNum, + Index: nextIndex - 1, + } + // Create a new managed address based on the public or private // key depending on whether the generated key is private. // Also, zero the next key after creating the managed address // from it. addr, err := newManagedAddressFromExtKey( - s, account, nextKey, addrType, + s, derivationPath, nextKey, addrType, ) if err != nil { return err @@ -1452,17 +1482,23 @@ func (s *ScopedKeyManager) ImportPrivateKey(ns walletdb.ReadWriteBucket, s.rootManager.mtx.Unlock() } + // The full derivation path for an imported key is incomplete as we + // don't know exactly how it was derived. + importedDerivationPath := DerivationPath{ + Account: ImportedAddrAccount, + } + // Create a new managed address based on the imported address. var managedAddr *managedAddress if !s.rootManager.WatchOnly() { managedAddr, err = newManagedAddress( - s, ImportedAddrAccount, wif.PrivKey, + s, importedDerivationPath, wif.PrivKey, wif.CompressPubKey, s.addrSchema.ExternalAddrType, ) } else { pubKey := (*btcec.PublicKey)(&wif.PrivKey.PublicKey) managedAddr, err = newManagedAddressWithoutPrivKey( - s, ImportedAddrAccount, pubKey, wif.CompressPubKey, + s, importedDerivationPath, pubKey, wif.CompressPubKey, s.addrSchema.ExternalAddrType, ) }