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.
This commit is contained in:
Olaoluwa Osuntokun 2018-08-13 18:44:46 -07:00
parent 05d73f6899
commit bd81968215
No known key found for this signature in database
GPG key ID: 964EA263DD637C21
2 changed files with 82 additions and 20 deletions

View file

@ -108,7 +108,7 @@ type ManagedPubKeyAddress interface {
// that backs the address via traditional methods from the HD root. For // 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 // imported keys, the first value will be set to false to indicate that
// we don't know exactly how the key was derived. // 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 // 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. // the private key associated with the public key.
type managedAddress struct { type managedAddress struct {
manager *ScopedKeyManager manager *ScopedKeyManager
account uint32 derivationPath DerivationPath
address btcutil.Address address btcutil.Address
imported bool imported bool
internal bool internal bool
@ -181,7 +181,7 @@ func (a *managedAddress) lock() {
// //
// This is part of the ManagedAddress interface implementation. // This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Account() uint32 { 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 // 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) 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 // newManagedAddressWithoutPrivKey returns a new managed address based on the
// passed account, public key, and whether or not the public key should be // passed account, public key, and whether or not the public key should be
// compressed. // compressed.
func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey *btcec.PublicKey, func newManagedAddressWithoutPrivKey(m *ScopedKeyManager,
compressed bool, addrType AddressType) (*managedAddress, error) { derivationPath DerivationPath, pubKey *btcec.PublicKey, compressed bool,
addrType AddressType) (*managedAddress, error) {
// Create a pay-to-pubkey-hash address from the public key. // Create a pay-to-pubkey-hash address from the public key.
var pubKeyHash []byte var pubKeyHash []byte
@ -387,7 +409,7 @@ func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey
return &managedAddress{ return &managedAddress{
manager: m, manager: m,
address: address, address: address,
account: account, derivationPath: derivationPath,
imported: false, imported: false,
internal: false, internal: false,
addrType: addrType, addrType: addrType,
@ -401,8 +423,9 @@ func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey
// newManagedAddress returns a new managed address based on the passed account, // newManagedAddress returns a new managed address based on the passed account,
// private key, and whether or not the public key is compressed. The managed // private key, and whether or not the public key is compressed. The managed
// address will have access to the private and public keys. // address will have access to the private and public keys.
func newManagedAddress(s *ScopedKeyManager, account uint32, privKey *btcec.PrivateKey, func newManagedAddress(s *ScopedKeyManager, derivationPath DerivationPath,
compressed bool, addrType AddressType) (*managedAddress, error) { privKey *btcec.PrivateKey, compressed bool,
addrType AddressType) (*managedAddress, error) {
// Encrypt the private key. // 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. // and then add the private key to it.
ecPubKey := (*btcec.PublicKey)(&privKey.PublicKey) ecPubKey := (*btcec.PublicKey)(&privKey.PublicKey)
managedAddr, err := newManagedAddressWithoutPrivKey( managedAddr, err := newManagedAddressWithoutPrivKey(
s, account, ecPubKey, compressed, addrType, s, derivationPath, ecPubKey, compressed, addrType,
) )
if err != nil { if err != nil {
return nil, err 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 // 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 // private and public keys if the provided extended key is private, otherwise it
// will only have access to the public key. // will only have access to the public key.
func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32, func newManagedAddressFromExtKey(s *ScopedKeyManager,
key *hdkeychain.ExtendedKey, addrType AddressType) (*managedAddress, error) { derivationPath DerivationPath, key *hdkeychain.ExtendedKey,
addrType AddressType) (*managedAddress, error) {
// Create a new managed address based on the public or private key // Create a new managed address based on the public or private key
// depending on whether the generated key is private. // depending on whether the generated key is private.
@ -446,9 +470,10 @@ func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32,
return nil, err 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( managedAddr, err = newManagedAddress(
s, account, privKey, true, addrType, s, derivationPath, privKey, true, addrType,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -460,7 +485,8 @@ func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32,
} }
managedAddr, err = newManagedAddressWithoutPrivKey( managedAddr, err = newManagedAddressWithoutPrivKey(
s, account, pubKey, true, addrType, s, derivationPath, pubKey, true,
addrType,
) )
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -221,11 +221,17 @@ func (s *ScopedKeyManager) keyToManaged(derivedKey *hdkeychain.ExtendedKey,
addrType = s.addrSchema.ExternalAddrType addrType = s.addrSchema.ExternalAddrType
} }
derivationPath := DerivationPath{
Account: account,
Branch: branch,
Index: index,
}
// Create a new managed address based on the public or private key // Create a new managed address based on the public or private key
// depending on whether the passed key is private. Also, zero the key // depending on whether the passed key is private. Also, zero the key
// after creating the managed address from it. // after creating the managed address from it.
ma, err := newManagedAddressFromExtKey( ma, err := newManagedAddressFromExtKey(
s, account, derivedKey, addrType, s, derivationPath, derivedKey, addrType,
) )
defer derivedKey.Zero() defer derivedKey.Zero()
if err != nil { if err != nil {
@ -515,9 +521,15 @@ func (s *ScopedKeyManager) importedAddressRowToManaged(row *dbImportedAddressRow
return nil, managerError(ErrCrypto, str, err) 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 compressed := len(pubBytes) == btcec.PubKeyBytesLenCompressed
ma, err := newManagedAddressWithoutPrivKey( ma, err := newManagedAddressWithoutPrivKey(
s, row.account, pubKey, compressed, s, derivationPath, pubKey, compressed,
s.addrSchema.ExternalAddrType, s.addrSchema.ExternalAddrType,
) )
if err != nil { if err != nil {
@ -740,12 +752,21 @@ func (s *ScopedKeyManager) nextAddresses(ns walletdb.ReadWriteBucket,
break 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 // Create a new managed address based on the public or private
// key depending on whether the generated key is private. // key depending on whether the generated key is private.
// Also, zero the next key after creating the managed address // Also, zero the next key after creating the managed address
// from it. // from it.
addr, err := newManagedAddressFromExtKey( addr, err := newManagedAddressFromExtKey(
s, account, nextKey, addrType, s, derivationPath, nextKey, addrType,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -920,12 +941,21 @@ func (s *ScopedKeyManager) extendAddresses(ns walletdb.ReadWriteBucket,
break 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 // Create a new managed address based on the public or private
// key depending on whether the generated key is private. // key depending on whether the generated key is private.
// Also, zero the next key after creating the managed address // Also, zero the next key after creating the managed address
// from it. // from it.
addr, err := newManagedAddressFromExtKey( addr, err := newManagedAddressFromExtKey(
s, account, nextKey, addrType, s, derivationPath, nextKey, addrType,
) )
if err != nil { if err != nil {
return err return err
@ -1452,17 +1482,23 @@ func (s *ScopedKeyManager) ImportPrivateKey(ns walletdb.ReadWriteBucket,
s.rootManager.mtx.Unlock() 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. // Create a new managed address based on the imported address.
var managedAddr *managedAddress var managedAddr *managedAddress
if !s.rootManager.WatchOnly() { if !s.rootManager.WatchOnly() {
managedAddr, err = newManagedAddress( managedAddr, err = newManagedAddress(
s, ImportedAddrAccount, wif.PrivKey, s, importedDerivationPath, wif.PrivKey,
wif.CompressPubKey, s.addrSchema.ExternalAddrType, wif.CompressPubKey, s.addrSchema.ExternalAddrType,
) )
} else { } else {
pubKey := (*btcec.PublicKey)(&wif.PrivKey.PublicKey) pubKey := (*btcec.PublicKey)(&wif.PrivKey.PublicKey)
managedAddr, err = newManagedAddressWithoutPrivKey( managedAddr, err = newManagedAddressWithoutPrivKey(
s, ImportedAddrAccount, pubKey, wif.CompressPubKey, s, importedDerivationPath, pubKey, wif.CompressPubKey,
s.addrSchema.ExternalAddrType, s.addrSchema.ExternalAddrType,
) )
} }