diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -625,11 +625,14 @@ // Map of scripts to descriptor range index using ScriptPubKeyMap = std::map; + // Map of pubkeys involved in scripts to descriptor range index + using PubKeyMap = std::map; using CryptedKeyMap = std::map>>; using KeyMap = std::map; ScriptPubKeyMap m_map_script_pub_keys GUARDED_BY(cs_desc_man); + PubKeyMap m_map_pubkeys GUARDED_BY(cs_desc_man); int32_t m_max_cached_index = -1; OutputType m_address_type; @@ -648,6 +651,21 @@ KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); + // Fetch the SigningProvider for the given script and optionally include + // private keys + std::unique_ptr + GetSigningProvider(const CScript &script, + bool include_private = false) const; + // Fetch the SigningProvider for the given pubkey and always include private + // keys. This should only be called by signing code. + std::unique_ptr + GetSigningProvider(const CPubKey &pubkey) const; + // Fetch the SigningProvider for a given index and optionally include + // private keys. Called by the above functions. + std::unique_ptr + GetSigningProvider(int32_t index, bool include_private = false) const + EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); + public: DescriptorScriptPubKeyMan(WalletStorage &storage, WalletDescriptor &descriptor) diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1726,6 +1726,17 @@ for (const CScript &script : scripts_temp) { m_map_script_pub_keys[script] = i; } + for (const auto &pk_pair : out_keys.pubkeys) { + const CPubKey &pubkey = pk_pair.second; + if (m_map_pubkeys.count(pubkey) != 0) { + // We don't need to give an error here. + // It doesn't matter which of many valid indexes the pubkey has, + // we just need an index where we can derive it and it's private + // key + continue; + } + m_map_pubkeys[pubkey] = i; + } // Write the cache for (const auto &parent_xpub_pair : temp_cache.GetCachedParentExtPubKeys()) { @@ -1937,9 +1948,63 @@ return m_wallet_descriptor.creation_time; } +std::unique_ptr +DescriptorScriptPubKeyMan::GetSigningProvider(const CScript &script, + bool include_private) const { + LOCK(cs_desc_man); + + // Find the index of the script + auto it = m_map_script_pub_keys.find(script); + if (it == m_map_script_pub_keys.end()) { + return nullptr; + } + int32_t index = it->second; + + return GetSigningProvider(index, include_private); +} + +std::unique_ptr +DescriptorScriptPubKeyMan::GetSigningProvider(const CPubKey &pubkey) const { + LOCK(cs_desc_man); + + // Find index of the pubkey + auto it = m_map_pubkeys.find(pubkey); + if (it == m_map_pubkeys.end()) { + return nullptr; + } + int32_t index = it->second; + + // Always try to get the signing provider with private keys. This function + // should only be called during signing anyways + return GetSigningProvider(index, true); +} + +std::unique_ptr +DescriptorScriptPubKeyMan::GetSigningProvider(int32_t index, + bool include_private) const { + AssertLockHeld(cs_desc_man); + // Get the scripts, keys, and key origins for this script + std::unique_ptr out_keys = + std::make_unique(); + std::vector scripts_temp; + if (!m_wallet_descriptor.descriptor->ExpandFromCache( + index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) { + return nullptr; + } + + if (HavePrivateKeys() && include_private) { + FlatSigningProvider master_provider; + master_provider.keys = GetKeys(); + m_wallet_descriptor.descriptor->ExpandPrivate(index, master_provider, + *out_keys); + } + + return out_keys; +} + std::unique_ptr DescriptorScriptPubKeyMan::GetSolvingProvider(const CScript &script) const { - return nullptr; + return GetSigningProvider(script, false); } bool DescriptorScriptPubKeyMan::CanProvide(const CScript &script, @@ -2009,6 +2074,17 @@ } m_map_script_pub_keys[script] = i; } + for (const auto &pk_pair : out_keys.pubkeys) { + const CPubKey &pubkey = pk_pair.second; + if (m_map_pubkeys.count(pubkey) != 0) { + // We don't need to give an error here. + // It doesn't matter which of many valid indexes the pubkey has, + // we just need an index where we can derive it and it's private + // key + continue; + } + m_map_pubkeys[pubkey] = i; + } m_max_cached_index++; } }