waddrmgr+wallet: only watch addresses within default key scopes

It was discovered that the wallet can scan the chain for unnecessary
additional addresses that are derived by higher-level applications using
custom key scopes. This isn't much of an issue for full nodes, but it
can cause light clients to scan more than what's required, triggering
more false positive matches which lead to block retrieval.

Now, we'll only scan the chain for addresses that exist within the
default key scopes, as those are the only ones the wallet should be
concerned about.
This commit is contained in:
Wilmer Paulino 2020-01-24 15:18:09 -08:00
parent b19df70ddd
commit 51b362d7c5
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F
2 changed files with 35 additions and 8 deletions

View file

@ -748,6 +748,29 @@ func (m *Manager) ForEachAccountAddress(ns walletdb.ReadBucket, account uint32,
return nil return nil
} }
// ForEachDefaultScopeActiveAddress calls the given function with each active
// address stored in the manager within the default scopes, breaking early on
// error.
func (m *Manager) ForEachDefaultScopeActiveAddress(ns walletdb.ReadBucket,
fn func(addr btcutil.Address) error) error {
m.mtx.RLock()
defer m.mtx.RUnlock()
for _, keyScope := range DefaultKeyScopes {
scopedMgr, ok := m.scopedManagers[keyScope]
if !ok {
return fmt.Errorf("manager for default key scope with "+
"purpose %v not found", keyScope.Purpose)
}
if err := scopedMgr.ForEachActiveAddress(ns, fn); err != nil {
return err
}
}
return nil
}
// ChainParams returns the chain parameters for this address manager. // ChainParams returns the chain parameters for this address manager.
func (m *Manager) ChainParams() *chaincfg.Params { func (m *Manager) ChainParams() *chaincfg.Params {
// NOTE: No need for mutex here since the net field does not change // NOTE: No need for mutex here since the net field does not change

View file

@ -303,18 +303,22 @@ func (w *Wallet) SetChainSynced(synced bool) {
w.chainClientSyncMtx.Unlock() w.chainClientSyncMtx.Unlock()
} }
// activeData returns the currently-active receiving addresses and all unspent // activeData returns the currently-active receiving addresses that exist within
// outputs. This is primarely intended to provide the parameters for a // the wallet's default key scopes and all unspent outputs. This is primarily
// rescan request. // intended to provide the parameters for a rescan request.
func (w *Wallet) activeData(dbtx walletdb.ReadTx) ([]btcutil.Address, []wtxmgr.Credit, error) { func (w *Wallet) activeData(dbtx walletdb.ReadTx) ([]btcutil.Address,
[]wtxmgr.Credit, error) {
addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey) addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
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.ForEachDefaultScopeActiveAddress(
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
} }