waddrmgr: update all addresses to be aware of new addr scopes
This commit is contained in:
parent
641efbbd31
commit
9a8da416ef
1 changed files with 71 additions and 55 deletions
|
@ -76,6 +76,11 @@ type ManagedAddress interface {
|
||||||
|
|
||||||
// Used returns true if the backing address has been used in a transaction.
|
// Used returns true if the backing address has been used in a transaction.
|
||||||
Used(ns walletdb.ReadBucket) bool
|
Used(ns walletdb.ReadBucket) bool
|
||||||
|
|
||||||
|
// AddrType returns the address type of the managed address. This can
|
||||||
|
// be used to quickly discern the address type without further
|
||||||
|
// processing
|
||||||
|
AddrType() AddressType
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManagedPubKeyAddress extends ManagedAddress and additionally provides the
|
// ManagedPubKeyAddress extends ManagedAddress and additionally provides the
|
||||||
|
@ -98,13 +103,6 @@ type ManagedPubKeyAddress interface {
|
||||||
// ExportPrivKey returns the private key associated with the address
|
// ExportPrivKey returns the private key associated with the address
|
||||||
// serialized as Wallet Import Format (WIF).
|
// serialized as Wallet Import Format (WIF).
|
||||||
ExportPrivKey() (*btcutil.WIF, error)
|
ExportPrivKey() (*btcutil.WIF, error)
|
||||||
|
|
||||||
// IsNestedWitness returns true if the managed address is an instance of pw2wkh
|
|
||||||
// nested within p2sh.
|
|
||||||
IsNestedWitness() bool
|
|
||||||
|
|
||||||
// IsWitness returns true if the managed address is a p2wkh address.
|
|
||||||
IsWitness() bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManagedScriptAddress extends ManagedAddress and represents a pay-to-script-hash
|
// ManagedScriptAddress extends ManagedAddress and represents a pay-to-script-hash
|
||||||
|
@ -120,14 +118,14 @@ type ManagedScriptAddress interface {
|
||||||
// managedAddress represents a public key address. It also may or may not have
|
// managedAddress represents a public key address. It also may or may not have
|
||||||
// the private key associated with the public key.
|
// the private key associated with the public key.
|
||||||
type managedAddress struct {
|
type managedAddress struct {
|
||||||
manager *Manager
|
manager *ScopedKeyManager
|
||||||
account uint32
|
account uint32
|
||||||
address btcutil.Address
|
address btcutil.Address
|
||||||
imported bool
|
imported bool
|
||||||
internal bool
|
internal bool
|
||||||
compressed bool
|
compressed bool
|
||||||
used bool
|
used bool
|
||||||
addrType addressType
|
addrType AddressType
|
||||||
pubKey *btcec.PublicKey
|
pubKey *btcec.PublicKey
|
||||||
privKeyEncrypted []byte
|
privKeyEncrypted []byte
|
||||||
privKeyCT []byte // non-nil if unlocked
|
privKeyCT []byte // non-nil if unlocked
|
||||||
|
@ -180,6 +178,14 @@ func (a *managedAddress) Account() uint32 {
|
||||||
return a.account
|
return a.account
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddrType returns the address type of the managed address. This can be used
|
||||||
|
// to quickly discern the address type without further processing
|
||||||
|
//
|
||||||
|
// This is part of the ManagedAddress interface implementation.
|
||||||
|
func (a *managedAddress) AddrType() AddressType {
|
||||||
|
return a.addrType
|
||||||
|
}
|
||||||
|
|
||||||
// Address returns the btcutil.Address which represents the managed address.
|
// Address returns the btcutil.Address which represents the managed address.
|
||||||
// This will be a pay-to-pubkey-hash address.
|
// This will be a pay-to-pubkey-hash address.
|
||||||
//
|
//
|
||||||
|
@ -266,7 +272,7 @@ func (a *managedAddress) ExportPubKey() string {
|
||||||
// This is part of the ManagedPubKeyAddress interface implementation.
|
// This is part of the ManagedPubKeyAddress interface implementation.
|
||||||
func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) {
|
func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) {
|
||||||
// No private keys are available for a watching-only address manager.
|
// No private keys are available for a watching-only address manager.
|
||||||
if a.manager.watchingOnly {
|
if a.manager.rootManager.WatchOnly() {
|
||||||
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,14 +280,14 @@ func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) {
|
||||||
defer a.manager.mtx.Unlock()
|
defer a.manager.mtx.Unlock()
|
||||||
|
|
||||||
// Account manager must be unlocked to decrypt the private key.
|
// Account manager must be unlocked to decrypt the private key.
|
||||||
if a.manager.locked {
|
if a.manager.rootManager.Locked() {
|
||||||
return nil, managerError(ErrLocked, errLocked, nil)
|
return nil, managerError(ErrLocked, errLocked, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the key as needed. Also, make sure it's a copy since the
|
// Decrypt the key as needed. Also, make sure it's a copy since the
|
||||||
// private key stored in memory can be cleared at any time. Otherwise
|
// private key stored in memory can be cleared at any time. Otherwise
|
||||||
// the returned private key could be invalidated from under the caller.
|
// the returned private key could be invalidated from under the caller.
|
||||||
privKeyCopy, err := a.unlock(a.manager.cryptoKeyPriv)
|
privKeyCopy, err := a.unlock(a.manager.rootManager.cryptoKeyPriv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -301,25 +307,14 @@ func (a *managedAddress) ExportPrivKey() (*btcutil.WIF, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return btcutil.NewWIF(pk, a.manager.chainParams, a.compressed)
|
return btcutil.NewWIF(pk, a.manager.rootManager.chainParams, a.compressed)
|
||||||
}
|
|
||||||
|
|
||||||
// IsNestedWitness returns true if the managed address is an instance of pw2wkh
|
|
||||||
// nested within p2sh.
|
|
||||||
func (a *managedAddress) IsNestedWitness() bool {
|
|
||||||
return a.addrType == adtChainNestedWitness
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWitness returns true if the managed address is a p2wkh address.
|
|
||||||
func (a *managedAddress) IsWitness() bool {
|
|
||||||
return a.addrType == adtChainWitness
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 *Manager, account uint32, pubKey *btcec.PublicKey,
|
func newManagedAddressWithoutPrivKey(m *ScopedKeyManager, account uint32, pubKey *btcec.PublicKey,
|
||||||
compressed bool, addrType addressType) (*managedAddress, error) {
|
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
|
||||||
|
@ -333,15 +328,17 @@ func newManagedAddressWithoutPrivKey(m *Manager, account uint32, pubKey *btcec.P
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch addrType {
|
switch addrType {
|
||||||
// TODO(roasbeef): only use these types in the db?
|
|
||||||
case adtChainNestedWitness:
|
case NestedWitnessPubKey:
|
||||||
// For this address type we'l generate an address which is
|
// For this address type we'l generate an address which is
|
||||||
// backwards compatible to Bitcoin nodes running 0.6.0 onwards, but
|
// backwards compatible to Bitcoin nodes running 0.6.0 onwards, but
|
||||||
// allows us to take advantage of segwit's scripting improvments,
|
// allows us to take advantage of segwit's scripting improvments,
|
||||||
// and malleability fixes.
|
// and malleability fixes.
|
||||||
|
|
||||||
// First, we'll generate a normal p2wkh address from the pubkey hash.
|
// First, we'll generate a normal p2wkh address from the pubkey hash.
|
||||||
witAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, m.chainParams)
|
witAddr, err := btcutil.NewAddressWitnessPubKeyHash(
|
||||||
|
pubKeyHash, m.rootManager.chainParams,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -357,20 +354,25 @@ func newManagedAddressWithoutPrivKey(m *Manager, account uint32, pubKey *btcec.P
|
||||||
// to a p2sh address. In order to spend, we first use the
|
// to a p2sh address. In order to spend, we first use the
|
||||||
// witnessProgram as the sigScript, then present the proper
|
// witnessProgram as the sigScript, then present the proper
|
||||||
// <sig, pubkey> pair as the witness.
|
// <sig, pubkey> pair as the witness.
|
||||||
address, err = btcutil.NewAddressScriptHash(witnessProgram, m.chainParams)
|
address, err = btcutil.NewAddressScriptHash(
|
||||||
|
witnessProgram, m.rootManager.chainParams,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case adtImport:
|
|
||||||
// TODO(roasbeef): truly proper?
|
case PubKeyHash:
|
||||||
fallthrough
|
address, err = btcutil.NewAddressPubKeyHash(
|
||||||
case adtChain:
|
pubKeyHash, m.rootManager.chainParams,
|
||||||
address, err = btcutil.NewAddressPubKeyHash(pubKeyHash, m.chainParams)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case adtChainWitness:
|
|
||||||
address, err = btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, m.chainParams)
|
case WitnessPubKey:
|
||||||
|
address, err = btcutil.NewAddressWitnessPubKeyHash(
|
||||||
|
pubKeyHash, m.rootManager.chainParams,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -393,15 +395,15 @@ func newManagedAddressWithoutPrivKey(m *Manager, account uint32, pubKey *btcec.P
|
||||||
// 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(m *Manager, account uint32, privKey *btcec.PrivateKey,
|
func newManagedAddress(s *ScopedKeyManager, account uint32, privKey *btcec.PrivateKey,
|
||||||
compressed bool, addrType addressType) (*managedAddress, error) {
|
compressed bool, addrType AddressType) (*managedAddress, error) {
|
||||||
|
|
||||||
// Encrypt the private key.
|
// Encrypt the private key.
|
||||||
//
|
//
|
||||||
// NOTE: The privKeyBytes here are set into the managed address which
|
// NOTE: The privKeyBytes here are set into the managed address which
|
||||||
// are cleared when locked, so they aren't cleared here.
|
// are cleared when locked, so they aren't cleared here.
|
||||||
privKeyBytes := privKey.Serialize()
|
privKeyBytes := privKey.Serialize()
|
||||||
privKeyEncrypted, err := m.cryptoKeyPriv.Encrypt(privKeyBytes)
|
privKeyEncrypted, err := s.rootManager.cryptoKeyPriv.Encrypt(privKeyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := "failed to encrypt private key"
|
str := "failed to encrypt private key"
|
||||||
return nil, managerError(ErrCrypto, str, err)
|
return nil, managerError(ErrCrypto, str, err)
|
||||||
|
@ -410,8 +412,9 @@ func newManagedAddress(m *Manager, account uint32, privKey *btcec.PrivateKey,
|
||||||
// Leverage the code to create a managed address without a private key
|
// Leverage the code to create a managed address without a private key
|
||||||
// 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(m, account,
|
managedAddr, err := newManagedAddressWithoutPrivKey(
|
||||||
ecPubKey, compressed, addrType)
|
s, account, ecPubKey, compressed, addrType,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -425,8 +428,8 @@ func newManagedAddress(m *Manager, account uint32, privKey *btcec.PrivateKey,
|
||||||
// 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(m *Manager, account uint32,
|
func newManagedAddressFromExtKey(s *ScopedKeyManager, account uint32,
|
||||||
key *hdkeychain.ExtendedKey, addrType addressType) (*managedAddress, error) {
|
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.
|
||||||
|
@ -438,8 +441,9 @@ func newManagedAddressFromExtKey(m *Manager, account uint32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(m, account, privKey, true,
|
managedAddr, err = newManagedAddress(
|
||||||
addrType)
|
s, account, privKey, true, addrType,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -449,8 +453,9 @@ func newManagedAddressFromExtKey(m *Manager, account uint32,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
managedAddr, err = newManagedAddressWithoutPrivKey(m, account,
|
managedAddr, err = newManagedAddressWithoutPrivKey(
|
||||||
pubKey, true, addrType)
|
s, account, pubKey, true, addrType,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -461,7 +466,7 @@ func newManagedAddressFromExtKey(m *Manager, account uint32,
|
||||||
|
|
||||||
// scriptAddress represents a pay-to-script-hash address.
|
// scriptAddress represents a pay-to-script-hash address.
|
||||||
type scriptAddress struct {
|
type scriptAddress struct {
|
||||||
manager *Manager
|
manager *ScopedKeyManager
|
||||||
account uint32
|
account uint32
|
||||||
address *btcutil.AddressScriptHash
|
address *btcutil.AddressScriptHash
|
||||||
scriptEncrypted []byte
|
scriptEncrypted []byte
|
||||||
|
@ -515,6 +520,14 @@ func (a *scriptAddress) Account() uint32 {
|
||||||
return a.account
|
return a.account
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddrType returns the address type of the managed address. This can be used
|
||||||
|
// to quickly discern the address type without further processing
|
||||||
|
//
|
||||||
|
// This is part of the ManagedAddress interface implementation.
|
||||||
|
func (a *scriptAddress) AddrType() AddressType {
|
||||||
|
return Script
|
||||||
|
}
|
||||||
|
|
||||||
// Address returns the btcutil.Address which represents the managed address.
|
// Address returns the btcutil.Address which represents the managed address.
|
||||||
// This will be a pay-to-script-hash address.
|
// This will be a pay-to-script-hash address.
|
||||||
//
|
//
|
||||||
|
@ -565,7 +578,7 @@ func (a *scriptAddress) Used(ns walletdb.ReadBucket) bool {
|
||||||
// This implements the ScriptAddress interface.
|
// This implements the ScriptAddress interface.
|
||||||
func (a *scriptAddress) Script() ([]byte, error) {
|
func (a *scriptAddress) Script() ([]byte, error) {
|
||||||
// No script is available for a watching-only address manager.
|
// No script is available for a watching-only address manager.
|
||||||
if a.manager.watchingOnly {
|
if a.manager.rootManager.WatchOnly() {
|
||||||
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,20 +586,23 @@ func (a *scriptAddress) Script() ([]byte, error) {
|
||||||
defer a.manager.mtx.Unlock()
|
defer a.manager.mtx.Unlock()
|
||||||
|
|
||||||
// Account manager must be unlocked to decrypt the script.
|
// Account manager must be unlocked to decrypt the script.
|
||||||
if a.manager.locked {
|
if a.manager.rootManager.Locked() {
|
||||||
return nil, managerError(ErrLocked, errLocked, nil)
|
return nil, managerError(ErrLocked, errLocked, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the script as needed. Also, make sure it's a copy since the
|
// Decrypt the script as needed. Also, make sure it's a copy since the
|
||||||
// script stored in memory can be cleared at any time. Otherwise,
|
// script stored in memory can be cleared at any time. Otherwise,
|
||||||
// the returned script could be invalidated from under the caller.
|
// the returned script could be invalidated from under the caller.
|
||||||
return a.unlock(a.manager.cryptoKeyScript)
|
return a.unlock(a.manager.rootManager.cryptoKeyScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newScriptAddress initializes and returns a new pay-to-script-hash address.
|
// newScriptAddress initializes and returns a new pay-to-script-hash address.
|
||||||
func newScriptAddress(m *Manager, account uint32, scriptHash, scriptEncrypted []byte) (*scriptAddress, error) {
|
func newScriptAddress(m *ScopedKeyManager, account uint32, scriptHash,
|
||||||
address, err := btcutil.NewAddressScriptHashFromHash(scriptHash,
|
scriptEncrypted []byte) (*scriptAddress, error) {
|
||||||
m.chainParams)
|
|
||||||
|
address, err := btcutil.NewAddressScriptHashFromHash(
|
||||||
|
scriptHash, m.rootManager.chainParams,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue