diff --git a/waddrmgr/manager.go b/waddrmgr/manager.go index b94c6ee..8dc279a 100644 --- a/waddrmgr/manager.go +++ b/waddrmgr/manager.go @@ -738,6 +738,43 @@ func (m *Manager) ForEachActiveAddress(ns walletdb.ReadBucket, fn func(addr btcu return nil } +// ForEachRelevantActiveAddress invokes the given closure on each active +// address relevant to the wallet. Ideally, only addresses within the default +// key scopes would be relevant, but due to a bug (now fixed) in which change +// addresses could be created outside of the default key scopes, we now need to +// check for those as well. +func (m *Manager) ForEachRelevantActiveAddress(ns walletdb.ReadBucket, + fn func(addr btcutil.Address) error) error { + + m.mtx.RLock() + defer m.mtx.RUnlock() + + for _, scopedMgr := range m.scopedManagers { + // If the manager is for a default key scope, we'll return all + // addresses, otherwise we'll only return internal addresses, as + // that's the branch used for change addresses. + isDefaultKeyScope := false + for _, defaultKeyScope := range DefaultKeyScopes { + if scopedMgr.Scope() == defaultKeyScope { + isDefaultKeyScope = true + break + } + } + + var err error + if isDefaultKeyScope { + err = scopedMgr.ForEachActiveAddress(ns, fn) + } else { + err = scopedMgr.ForEachInternalActiveAddress(ns, fn) + } + if err != nil { + return err + } + } + + return nil +} + // ForEachAccountAddress calls the given function with each address of // the given account stored in the manager, breaking early on error. func (m *Manager) ForEachAccountAddress(ns walletdb.ReadBucket, account uint32, diff --git a/waddrmgr/scoped_manager.go b/waddrmgr/scoped_manager.go index b9acda3..16bd83a 100644 --- a/waddrmgr/scoped_manager.go +++ b/waddrmgr/scoped_manager.go @@ -1760,3 +1760,30 @@ func (s *ScopedKeyManager) ForEachActiveAddress(ns walletdb.ReadBucket, return nil } + +// ForEachInternalActiveAddress invokes the given closure on each _internal_ +// active address belonging to the scoped key manager, breaking early on error. +func (s *ScopedKeyManager) ForEachInternalActiveAddress(ns walletdb.ReadBucket, + fn func(addr btcutil.Address) error) error { + + s.mtx.Lock() + defer s.mtx.Unlock() + + addrFn := func(rowInterface interface{}) error { + managedAddr, err := s.rowInterfaceToManaged(ns, rowInterface) + if err != nil { + return err + } + // Skip any non-internal branch addresses. + if !managedAddr.Internal() { + return nil + } + return fn(managedAddr.Address()) + } + + if err := forEachActiveAddress(ns, &s.scope, addrFn); err != nil { + return maybeConvertDbError(err) + } + + return nil +} diff --git a/wallet/wallet.go b/wallet/wallet.go index cdb1862..3a8167b 100644 --- a/wallet/wallet.go +++ b/wallet/wallet.go @@ -311,10 +311,12 @@ func (w *Wallet) activeData(dbtx walletdb.ReadTx) ([]btcutil.Address, []wtxmgr.C txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) var addrs []btcutil.Address - err := w.Manager.ForEachActiveAddress(addrmgrNs, func(addr btcutil.Address) error { - addrs = append(addrs, addr) - return nil - }) + err := w.Manager.ForEachRelevantActiveAddress( + addrmgrNs, func(addr btcutil.Address) error { + addrs = append(addrs, addr) + return nil + }, + ) if err != nil { return nil, nil, err }