mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-21 07:39:08 +01:00
55b931934aremoved duplicate calling of GetDescriptorScriptPubKeyMan (Saikiran) Pull request description: Removed duplicate call to GetDescriptorScriptPubKeyMan and Instead of checking linearly I have used find method so time complexity reduced significantly for GetDescriptorScriptPubKeyMan after this fix improved performance of importdescriptor part refs https://github.com/bitcoin/bitcoin/issues/32013. **Steps to reproduce in testnet environment** **Input size:** 2 million address in the wallet **Step1:** call importaddresdescriptor rpc method observe the time it has taken. **With the provided fix:** Do the same steps again observe the time it has taken. There is a huge improvement in the performance. (previously it may take 5 to 6 seconds now it will take 1 seconds or less) main changes i've made during this pr: 1. remove duplicate call to GetDescriptorScriptPubKeyMan method 2. And inside GetDescriptorScriptPubKeyMan method previously we checking **each address linearly** so each time it is calling HasWallet method which has aquired lock. 3. Now i've modified this logic call **find method on the map (O(logn)**) time it is taking, so only once we calling HasWallet method. **Note:** Smaller inputs in the wallet you may not see the issue but huge wallet size it will definitely impact the performance. ACKs for top commit: achow101: ACK55b931934aw0xlt: ACK55b931934aTree-SHA512: 4a7fdbcbb4e55bd034e9cf28ab4e7ee3fb1745fc8847adb388c98a19c952a1fb66d7b54f0f28b4c2a75a42473923742b4a99fb26771577183a98e0bcbf87a8ca
217 lines
6.6 KiB
C++
217 lines
6.6 KiB
C++
// Copyright (c) 2021-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <wallet/test/util.h>
|
|
|
|
#include <chain.h>
|
|
#include <key.h>
|
|
#include <key_io.h>
|
|
#include <streams.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <validationinterface.h>
|
|
#include <wallet/context.h>
|
|
#include <wallet/wallet.h>
|
|
#include <wallet/walletdb.h>
|
|
|
|
#include <memory>
|
|
|
|
namespace wallet {
|
|
std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, const CKey& key)
|
|
{
|
|
auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockableWalletDatabase());
|
|
{
|
|
LOCK2(wallet->cs_wallet, ::cs_main);
|
|
wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
|
|
}
|
|
{
|
|
LOCK(wallet->cs_wallet);
|
|
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
|
|
wallet->SetupDescriptorScriptPubKeyMans();
|
|
|
|
FlatSigningProvider provider;
|
|
std::string error;
|
|
auto descs = Parse("combo(" + EncodeSecret(key) + ")", provider, error, /* require_checksum=*/ false);
|
|
assert(descs.size() == 1);
|
|
auto& desc = descs.at(0);
|
|
WalletDescriptor w_desc(std::move(desc), 0, 0, 1, 1);
|
|
auto spk_manager = *Assert(wallet->AddWalletDescriptor(w_desc, provider, "", false));
|
|
assert(spk_manager);
|
|
}
|
|
WalletRescanReserver reserver(*wallet);
|
|
reserver.reserve();
|
|
CWallet::ScanResult result = wallet->ScanForWalletTransactions(cchain.Genesis()->GetBlockHash(), /*start_height=*/0, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/false);
|
|
assert(result.status == CWallet::ScanResult::SUCCESS);
|
|
assert(result.last_scanned_block == cchain.Tip()->GetBlockHash());
|
|
assert(*result.last_scanned_height == cchain.Height());
|
|
assert(result.last_failed_block.IsNull());
|
|
return wallet;
|
|
}
|
|
|
|
std::shared_ptr<CWallet> TestLoadWallet(std::unique_ptr<WalletDatabase> database, WalletContext& context, uint64_t create_flags)
|
|
{
|
|
bilingual_str error;
|
|
std::vector<bilingual_str> warnings;
|
|
auto wallet = CWallet::Create(context, "", std::move(database), create_flags, error, warnings);
|
|
NotifyWalletLoaded(context, wallet);
|
|
if (context.chain) {
|
|
wallet->postInitProcess();
|
|
}
|
|
return wallet;
|
|
}
|
|
|
|
std::shared_ptr<CWallet> TestLoadWallet(WalletContext& context)
|
|
{
|
|
DatabaseOptions options;
|
|
options.create_flags = WALLET_FLAG_DESCRIPTORS;
|
|
DatabaseStatus status;
|
|
bilingual_str error;
|
|
std::vector<bilingual_str> warnings;
|
|
auto database = MakeWalletDatabase("", options, status, error);
|
|
return TestLoadWallet(std::move(database), context, options.create_flags);
|
|
}
|
|
|
|
void TestUnloadWallet(std::shared_ptr<CWallet>&& wallet)
|
|
{
|
|
// Calls SyncWithValidationInterfaceQueue
|
|
wallet->chain().waitForNotificationsIfTipChanged({});
|
|
wallet->m_chain_notifications_handler.reset();
|
|
WaitForDeleteWallet(std::move(wallet));
|
|
}
|
|
|
|
std::unique_ptr<WalletDatabase> DuplicateMockDatabase(WalletDatabase& database)
|
|
{
|
|
return std::make_unique<MockableDatabase>(dynamic_cast<MockableDatabase&>(database).m_records);
|
|
}
|
|
|
|
std::string getnewaddress(CWallet& w)
|
|
{
|
|
constexpr auto output_type = OutputType::BECH32;
|
|
return EncodeDestination(getNewDestination(w, output_type));
|
|
}
|
|
|
|
CTxDestination getNewDestination(CWallet& w, OutputType output_type)
|
|
{
|
|
return *Assert(w.GetNewDestination(output_type, ""));
|
|
}
|
|
|
|
MockableCursor::MockableCursor(const MockableData& records, bool pass, std::span<const std::byte> prefix)
|
|
{
|
|
m_pass = pass;
|
|
std::tie(m_cursor, m_cursor_end) = records.equal_range(BytePrefix{prefix});
|
|
}
|
|
|
|
DatabaseCursor::Status MockableCursor::Next(DataStream& key, DataStream& value)
|
|
{
|
|
if (!m_pass) {
|
|
return Status::FAIL;
|
|
}
|
|
if (m_cursor == m_cursor_end) {
|
|
return Status::DONE;
|
|
}
|
|
key.clear();
|
|
value.clear();
|
|
const auto& [key_data, value_data] = *m_cursor;
|
|
key.write(key_data);
|
|
value.write(value_data);
|
|
m_cursor++;
|
|
return Status::MORE;
|
|
}
|
|
|
|
bool MockableBatch::ReadKey(DataStream&& key, DataStream& value)
|
|
{
|
|
if (!m_pass) {
|
|
return false;
|
|
}
|
|
SerializeData key_data{key.begin(), key.end()};
|
|
const auto& it = m_records.find(key_data);
|
|
if (it == m_records.end()) {
|
|
return false;
|
|
}
|
|
value.clear();
|
|
value.write(it->second);
|
|
return true;
|
|
}
|
|
|
|
bool MockableBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
|
|
{
|
|
if (!m_pass) {
|
|
return false;
|
|
}
|
|
SerializeData key_data{key.begin(), key.end()};
|
|
SerializeData value_data{value.begin(), value.end()};
|
|
auto [it, inserted] = m_records.emplace(key_data, value_data);
|
|
if (!inserted && overwrite) { // Overwrite if requested
|
|
it->second = value_data;
|
|
inserted = true;
|
|
}
|
|
return inserted;
|
|
}
|
|
|
|
bool MockableBatch::EraseKey(DataStream&& key)
|
|
{
|
|
if (!m_pass) {
|
|
return false;
|
|
}
|
|
SerializeData key_data{key.begin(), key.end()};
|
|
m_records.erase(key_data);
|
|
return true;
|
|
}
|
|
|
|
bool MockableBatch::HasKey(DataStream&& key)
|
|
{
|
|
if (!m_pass) {
|
|
return false;
|
|
}
|
|
SerializeData key_data{key.begin(), key.end()};
|
|
return m_records.count(key_data) > 0;
|
|
}
|
|
|
|
bool MockableBatch::ErasePrefix(std::span<const std::byte> prefix)
|
|
{
|
|
if (!m_pass) {
|
|
return false;
|
|
}
|
|
auto it = m_records.begin();
|
|
while (it != m_records.end()) {
|
|
auto& key = it->first;
|
|
if (key.size() < prefix.size() || std::search(key.begin(), key.end(), prefix.begin(), prefix.end()) != key.begin()) {
|
|
it++;
|
|
continue;
|
|
}
|
|
it = m_records.erase(it);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<WalletDatabase> CreateMockableWalletDatabase(MockableData records)
|
|
{
|
|
return std::make_unique<MockableDatabase>(records);
|
|
}
|
|
|
|
MockableDatabase& GetMockableDatabase(CWallet& wallet)
|
|
{
|
|
return dynamic_cast<MockableDatabase&>(wallet.GetDatabase());
|
|
}
|
|
|
|
wallet::ScriptPubKeyMan* CreateDescriptor(CWallet& keystore, const std::string& desc_str, const bool success)
|
|
{
|
|
keystore.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
|
|
|
|
FlatSigningProvider keys;
|
|
std::string error;
|
|
auto parsed_descs = Parse(desc_str, keys, error, false);
|
|
Assert(success == (!parsed_descs.empty()));
|
|
if (!success) return nullptr;
|
|
auto& desc = parsed_descs.at(0);
|
|
|
|
const int64_t range_start = 0, range_end = 1, next_index = 0, timestamp = 1;
|
|
|
|
WalletDescriptor w_desc(std::move(desc), timestamp, range_start, range_end, next_index);
|
|
|
|
LOCK(keystore.cs_wallet);
|
|
auto spkm = Assert(keystore.AddWalletDescriptor(w_desc, keys,/*label=*/"", /*internal=*/false));
|
|
return spkm.value();
|
|
};
|
|
} // namespace wallet
|