Skip to content

Commit 70b3513

Browse files
committed
wallet: add FastWalletRescanFilter class for speeding up rescans
This only supports wallet descriptors right now.
1 parent c051026 commit 70b3513

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

src/wallet/wallet.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <wallet/wallet.h>
77

8+
#include <blockfilter.h>
89
#include <chain.h>
910
#include <consensus/amount.h>
1011
#include <consensus/consensus.h>
@@ -261,6 +262,64 @@ std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::s
261262
return nullptr;
262263
}
263264
}
265+
266+
class FastWalletRescanFilter
267+
{
268+
public:
269+
FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)
270+
{
271+
// fast rescanning via block filters is only supported by descriptor wallets right now
272+
assert(!m_wallet.IsLegacy());
273+
274+
// create initial filter with scripts from all ScriptPubKeyMans
275+
for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {
276+
auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
277+
assert(desc_spkm != nullptr);
278+
AddScriptPubKeys(desc_spkm);
279+
// save each range descriptor's end for possible future filter updates
280+
if (desc_spkm->IsHDEnabled()) {
281+
m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange());
282+
}
283+
}
284+
}
285+
286+
void UpdateIfNeeded()
287+
{
288+
// repopulate filter with new scripts if top-up has happened since last iteration
289+
for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) {
290+
auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))};
291+
assert(desc_spkm != nullptr);
292+
int32_t current_range_end{desc_spkm->GetEndRange()};
293+
if (current_range_end > last_range_end) {
294+
AddScriptPubKeys(desc_spkm, last_range_end);
295+
m_last_range_ends.at(desc_spkm->GetID()) = current_range_end;
296+
}
297+
}
298+
}
299+
300+
std::optional<bool> MatchesBlock(const uint256& block_hash) const
301+
{
302+
return m_wallet.chain().blockFilterMatchesAny(BlockFilterType::BASIC, block_hash, m_filter_set);
303+
}
304+
305+
private:
306+
const CWallet& m_wallet;
307+
/** Map for keeping track of each range descriptor's last seen end range.
308+
* This information is used to detect whether new addresses were derived
309+
* (that is, if the current end range is larger than the saved end range)
310+
* after processing a block and hence a filter set update is needed to
311+
* take possible keypool top-ups into account.
312+
*/
313+
std::map<uint256, int32_t> m_last_range_ends;
314+
GCSFilter::ElementSet m_filter_set;
315+
316+
void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0)
317+
{
318+
for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) {
319+
m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end());
320+
}
321+
}
322+
};
264323
} // namespace
265324

266325
std::shared_ptr<CWallet> LoadWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)

0 commit comments

Comments
 (0)