From cc5d25a895e23b76667c6ed389849056c0e0de98 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Mon, 6 Nov 2023 14:57:54 -0500 Subject: [PATCH] Merge bitcoin/bitcoin#28799: wallet: cache descriptor ID to avoid repeated descriptor string creation 5e6bc6d830664a5afeb5d5bd7e7b3818a01376b7 test: remove custom rpc timeout for `wallet_miniscript.py`, reorder in test_runner (Sebastian Falbesoner) f811a24421fe102a96ab8f75427cc6a3c5503dc3 wallet: cache descriptor ID to avoid repeated descriptor string creation (Sebastian Falbesoner) Pull request description: Right now a wallet descriptor is converted to its string representation (via `Descriptor::ToString`) repeatedly at different instances: - on finding a `DescriptorScriptPubKeyMan` for a given descriptor (`CWallet::GetDescriptorScriptPubKeyMan`, e.g. used by the `importdescriptors` RPC); the string representation is created once for each spkm in the wallet and at each iteration again for the searched descriptor (`DescriptorScriptPubKeyMan::HasWalletDescriptor`) - whenever `DescriptorScriptPubKeyMan::GetID()` is called, e.g. in `TopUp` or any instances where a descriptor is written to the DB to determine the database key, also at less obvious places like `FastWalletRescanFilter` etc. As there is no good reason to calculate a fixed descriptor's string/ID more than once, add the ID as a field to `WalletDescriptor` and calculate it immediately at initialization (or deserialization). `HasWalletDescriptor` is changed to compare the spkm's and searched descriptor's ID instead of the string to take use of that. This speeds up the functional test `wallet_miniscript.py` by a factor of 5-6x on my machine (3m30.95s on master vs. 0m38.02s on PR). The recently introduced "max-size TapMiniscript" test-case introduced a descriptor that takes 2-3 seconds to create a string representation, so the repeated calls to that were significantly hurting the performance. Fixes https://github.com/bitcoin/bitcoin/issues/28800. ACKs for top commit: Sjors: ACK 5e6bc6d830664a5afeb5d5bd7e7b3818a01376b7 S3RK: Code Review ACK 5e6bc6d830664a5afeb5d5bd7e7b3818a01376b7 achow101: ACK 5e6bc6d830664a5afeb5d5bd7e7b3818a01376b7 BrandonOdiwuor: ACK 5e6bc6d830664a5afeb5d5bd7e7b3818a01376b7 Tree-SHA512: 98b43963a5dde6055bb26cecd3b878dadd837d6226af4c84142383310495da80b3c4bd552e73b9107f2f2ff1c11f5e18060c6fd3d9e44bbd5224114c4d245c1c --- src/wallet/scriptpubkeyman.cpp | 4 ++-- src/wallet/walletutil.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index a2646df64f4b24..873a8a65a7f5b2 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -2309,7 +2309,7 @@ std::unique_ptr DescriptorScriptPubKeyMan::GetMetadata(const CTxDe uint256 DescriptorScriptPubKeyMan::GetID() const { LOCK(cs_desc_man); - return DescriptorID(*m_wallet_descriptor.descriptor); + return m_wallet_descriptor.id; } void DescriptorScriptPubKeyMan::SetInternal(bool internal) @@ -2368,7 +2368,7 @@ bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKe bool DescriptorScriptPubKeyMan::HasWalletDescriptor(const WalletDescriptor& desc) const { LOCK(cs_desc_man); - return m_wallet_descriptor.descriptor != nullptr && desc.descriptor != nullptr && m_wallet_descriptor.descriptor->ToString() == desc.descriptor->ToString(); + return !m_wallet_descriptor.id.IsNull() && !desc.id.IsNull() && m_wallet_descriptor.id == desc.id; } void DescriptorScriptPubKeyMan::WriteDescriptor() diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index 2e67db11d407a4..50121891829e1f 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -67,6 +67,7 @@ class WalletDescriptor { public: std::shared_ptr descriptor; + uint256 id; // Descriptor ID (calculated once at descriptor initialization/deserialization) uint64_t creation_time = 0; int32_t range_start = 0; // First item in range; start of range, inclusive, i.e. [range_start, range_end). This never changes. int32_t range_end = 0; // Item after the last; end of range, exclusive, i.e. [range_start, range_end). This will increment with each TopUp() @@ -80,7 +81,8 @@ class WalletDescriptor descriptor = Parse(str, keys, error, true); if (!descriptor) { throw std::ios_base::failure("Invalid descriptor: " + error); - } + } + id = DescriptorID(*descriptor); } SERIALIZE_METHODS(WalletDescriptor, obj) @@ -92,7 +94,7 @@ class WalletDescriptor } WalletDescriptor() {} - WalletDescriptor(std::shared_ptr descriptor, uint64_t creation_time, int32_t range_start, int32_t range_end, int32_t next_index) : descriptor(descriptor), creation_time(creation_time), range_start(range_start), range_end(range_end), next_index(next_index) {} + WalletDescriptor(std::shared_ptr descriptor, uint64_t creation_time, int32_t range_start, int32_t range_end, int32_t next_index) : descriptor(descriptor), id(DescriptorID(*descriptor)), creation_time(creation_time), range_start(range_start), range_end(range_end), next_index(next_index) { } }; #endif // BITCOIN_WALLET_WALLETUTIL_H