wallet: include addresses from relevant key scopes in rescan

Due to a no longer existing bug within the wallet, it was possible for
change addresses to be created outside of their intended key scope (the
default), so wallets affected by this now need to ensure they scan the
chain for all addresses within the default key scopes (as expected), and
all _internal_ addresses (branch used for change addresses) within any
other registered key scopes to reflect their proper balance.
This commit is contained in:
Wilmer Paulino 2020-03-30 15:32:34 -07:00
parent 43e19da868
commit 1285049923
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F
3 changed files with 70 additions and 4 deletions

View file

@ -738,6 +738,43 @@ func (m *Manager) ForEachActiveAddress(ns walletdb.ReadBucket, fn func(addr btcu
return nil 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 // ForEachAccountAddress calls the given function with each address of
// the given account stored in the manager, breaking early on error. // the given account stored in the manager, breaking early on error.
func (m *Manager) ForEachAccountAddress(ns walletdb.ReadBucket, account uint32, func (m *Manager) ForEachAccountAddress(ns walletdb.ReadBucket, account uint32,

View file

@ -1760,3 +1760,30 @@ func (s *ScopedKeyManager) ForEachActiveAddress(ns walletdb.ReadBucket,
return nil 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
}

View file

@ -311,10 +311,12 @@ func (w *Wallet) activeData(dbtx walletdb.ReadTx) ([]btcutil.Address, []wtxmgr.C
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey) txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
var addrs []btcutil.Address var addrs []btcutil.Address
err := w.Manager.ForEachActiveAddress(addrmgrNs, func(addr btcutil.Address) error { err := w.Manager.ForEachRelevantActiveAddress(
addrs = append(addrs, addr) addrmgrNs, func(addr btcutil.Address) error {
return nil addrs = append(addrs, addr)
}) return nil
},
)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }