diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index ed3c8bfd8e4..8ad8aec19e1 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -27,6 +27,9 @@ namespace wallet { +using HDPubKeyMap = CWallet::HDPubKeyMap; +using HDKeyFilter = CWallet::HDKeyFilter; + static const std::map WALLET_FLAG_CAVEATS{ {WALLET_FLAG_AVOID_REUSE, "You need to rescan the blockchain in order to correctly mark used " @@ -689,31 +692,15 @@ RPCMethod gethdkeys() EnsureWalletIsUnlocked(*wallet); } - - std::set spkms; - if (active_only) { - spkms = wallet->GetActiveScriptPubKeyMans(); - } else { - spkms = wallet->GetAllScriptPubKeyMans(); - } - std::map>> wallet_xpubs; std::map wallet_xprvs; - for (auto* spkm : spkms) { - auto* desc_spkm{dynamic_cast(spkm)}; - CHECK_NONFATAL(desc_spkm); - LOCK(desc_spkm->cs_desc_man); - WalletDescriptor w_desc = desc_spkm->GetWalletDescriptor(); - - // Retrieve the pubkeys from the descriptor - std::set desc_pubkeys; - std::set desc_xpubs; - w_desc.descriptor->GetPubKeys(desc_pubkeys, desc_xpubs); - for (const CExtPubKey& xpub : desc_xpubs) { + for (const auto& [xpub, spkms] : wallet->GetHDPubKeys(active_only ? HDKeyFilter::Active : HDKeyFilter::All)) { + for (auto* desc_spkm : spkms) { + LOCK(desc_spkm->cs_desc_man); std::string desc_str; bool ok = desc_spkm->GetDescriptorString(desc_str, /*priv=*/false); CHECK_NONFATAL(ok); - wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID())); + wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*desc_spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID())); if (std::optional key = priv ? desc_spkm->GetKey(xpub.pubkey.GetID()) : std::nullopt) { wallet_xprvs[xpub] = CExtKey(xpub, *key); } @@ -800,11 +787,11 @@ static RPCMethod createwalletdescriptor() CExtPubKey xpub; if (hdkey.isNull()) { - std::set active_xpubs = pwallet->GetActiveHDPubKeys(); + HDPubKeyMap active_xpubs = pwallet->GetHDPubKeys(HDKeyFilter::Active); if (active_xpubs.size() != 1) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'"); } - xpub = *active_xpubs.begin(); + xpub = active_xpubs.begin()->first; } else { xpub = DecodeExtPubKey(hdkey.get_str()); if (!xpub.pubkey.IsValid()) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index eb40aea0cd6..513e3ebe3c4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4535,25 +4535,26 @@ void CWallet::TopUpCallback(const std::set& spks, ScriptPubKeyMan* spkm CacheNewScriptPubKeys(spks, spkm); } -std::set CWallet::GetActiveHDPubKeys() const +CWallet::HDPubKeyMap CWallet::GetHDPubKeys(HDKeyFilter filter) const { AssertLockHeld(cs_wallet); Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); - std::set active_xpubs; - for (const auto& spkm : GetActiveScriptPubKeyMans()) { - const DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast(spkm); - assert(desc_spkm); + HDPubKeyMap xpubs; + for (const auto& spkm : filter == HDKeyFilter::Active ? GetActiveScriptPubKeyMans() : GetAllScriptPubKeyMans()) { + auto* desc_spkm = Assert(dynamic_cast(spkm)); LOCK(desc_spkm->cs_desc_man); WalletDescriptor w_desc = desc_spkm->GetWalletDescriptor(); std::set desc_pubkeys; std::set desc_xpubs; w_desc.descriptor->GetPubKeys(desc_pubkeys, desc_xpubs); - active_xpubs.merge(std::move(desc_xpubs)); + for (const CExtPubKey& xpub : desc_xpubs) { + xpubs[xpub].insert(desc_spkm); + } } - return active_xpubs; + return xpubs; } std::optional CWallet::GetKey(const CKeyID& keyid) const diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 9b1bb8b6ddc..4d12c404d64 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1078,8 +1078,14 @@ public: void TopUpCallback(const std::set& spks, ScriptPubKeyMan* spkm) override; - //! Retrieve the xpubs in use by the active descriptors - std::set GetActiveHDPubKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + //! Which descriptors GetHDPubKeys() should consider. + enum class HDKeyFilter { + Active, //!< Only active descriptors + All, //!< All descriptors + }; + using HDPubKeyMap = std::map>; + //! Retrieve descriptor xpubs matching the requested filter. + HDPubKeyMap GetHDPubKeys(HDKeyFilter filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Find the private key for the given key id from the wallet's descriptors, if available //! Returns nullopt when no descriptor has the key or if the wallet is locked.