wallet: add FastWalletRescanFilter class for speeding up rescans

This only supports wallet descriptors right now.
This commit is contained in:
Sebastian Falbesoner 2022-08-26 01:41:01 +02:00
parent c051026586
commit 70b3513904

View File

@ -5,6 +5,7 @@
#include <wallet/wallet.h> #include <wallet/wallet.h>
#include <blockfilter.h>
#include <chain.h> #include <chain.h>
#include <consensus/amount.h> #include <consensus/amount.h>
#include <consensus/consensus.h> #include <consensus/consensus.h>
@ -261,6 +262,64 @@ std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::s
return nullptr; return nullptr;
} }
} }
class FastWalletRescanFilter
{
public:
FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)
{
// fast rescanning via block filters is only supported by descriptor wallets right now
assert(!m_wallet.IsLegacy());
// create initial filter with scripts from all ScriptPubKeyMans
for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {
auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
assert(desc_spkm != nullptr);
AddScriptPubKeys(desc_spkm);
// save each range descriptor's end for possible future filter updates
if (desc_spkm->IsHDEnabled()) {
m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange());
}
}
}
void UpdateIfNeeded()
{
// repopulate filter with new scripts if top-up has happened since last iteration
for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) {
auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))};
assert(desc_spkm != nullptr);
int32_t current_range_end{desc_spkm->GetEndRange()};
if (current_range_end > last_range_end) {
AddScriptPubKeys(desc_spkm, last_range_end);
m_last_range_ends.at(desc_spkm->GetID()) = current_range_end;
}
}
}
std::optional<bool> MatchesBlock(const uint256& block_hash) const
{
return m_wallet.chain().blockFilterMatchesAny(BlockFilterType::BASIC, block_hash, m_filter_set);
}
private:
const CWallet& m_wallet;
/** Map for keeping track of each range descriptor's last seen end range.
* This information is used to detect whether new addresses were derived
* (that is, if the current end range is larger than the saved end range)
* after processing a block and hence a filter set update is needed to
* take possible keypool top-ups into account.
*/
std::map<uint256, int32_t> m_last_range_ends;
GCSFilter::ElementSet m_filter_set;
void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0)
{
for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) {
m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end());
}
}
};
} // namespace } // namespace
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) 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)