diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 5ec51a737cd..98bf5542799 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -596,16 +596,15 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() // Construct the combo descriptor std::string desc_str = "combo(" + origin_str + HexStr(key.GetPubKey()) + ")"; - FlatSigningProvider keys; + FlatSigningProvider provider; std::string error; - std::vector> descs = Parse(desc_str, keys, error, false); + std::vector> descs = Parse(desc_str, provider, error, false); CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys - auto desc_spk_man = std::make_unique(m_storage, w_desc, /*keypool_size=*/0); - WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey())); - desc_spk_man->TopUpWithDB(batch); + provider.keys.emplace(key.GetPubKey().GetID(), key); + auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider); auto desc_spks = desc_spk_man->GetScriptPubKeys(); // Remove the scriptPubKeys from our current set @@ -644,17 +643,16 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() // Make the combo descriptor std::string xpub = EncodeExtPubKey(master_key.Neuter()); std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)"; - FlatSigningProvider keys; + FlatSigningProvider provider; std::string error; - std::vector> descs = Parse(desc_str, keys, error, false); + std::vector> descs = Parse(desc_str, provider, error, false); CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor uint32_t chain_counter = std::max((i == 1 ? chain.nInternalChainCounter : chain.nExternalChainCounter), (uint32_t)0); WalletDescriptor w_desc(std::move(descs.at(0)), 0, 0, chain_counter, 0); // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys - auto desc_spk_man = std::make_unique(m_storage, w_desc, /*keypool_size=*/0); - WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey())); - desc_spk_man->TopUpWithDB(batch); + provider.keys.emplace(master_key.key.GetPubKey().GetID(), master_key.key); + auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider); auto desc_spks = desc_spk_man->GetScriptPubKeys(); // Remove the scriptPubKeys from our current set @@ -727,16 +725,15 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() desc->Expand(0, provider, desc_spks, provider); } else { // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys - WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); - auto desc_spk_man = std::make_unique(m_storage, w_desc, /*keypool_size=*/0); for (const auto& keyid : privkeyids) { CKey key; if (!GetKey(keyid, key)) { continue; } - WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey())); + keys.keys.emplace(key.GetPubKey().GetID(), key); } - desc_spk_man->TopUpWithDB(batch); + WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); + auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, keys); auto desc_spks_set = desc_spk_man->GetScriptPubKeys(); desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end()); @@ -821,6 +818,23 @@ bool LegacyDataSPKM::DeleteRecordsWithDB(WalletBatch& batch) return batch.EraseRecords(DBKeys::LEGACY_TYPES); } +std::unique_ptr DescriptorScriptPubKeyMan::CreateFromImport(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider) +{ + auto spkm = std::unique_ptr(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size)); + LOCK(spkm->cs_desc_man); + WalletBatch batch(storage.GetDatabase()); + spkm->UpdateWithSigningProvider(batch, provider); + return spkm; +} + +std::unique_ptr DescriptorScriptPubKeyMan::CreateFromMigration(WalletStorage& storage, WalletBatch& batch, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider) +{ + auto spkm = std::unique_ptr(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size)); + LOCK(spkm->cs_desc_man); + spkm->UpdateWithSigningProvider(batch, provider); + return spkm; +} + DescriptorScriptPubKeyMan::DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys) : ScriptPubKeyMan(storage), m_map_keys(keys), @@ -1565,7 +1579,7 @@ void DescriptorScriptPubKeyMan::UpgradeDescriptorCache() } } -util::Result DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor) +util::Result DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider) { LOCK(cs_desc_man); std::string error; @@ -1578,10 +1592,29 @@ util::Result DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescr m_max_cached_index = -1; m_wallet_descriptor = descriptor; + WalletBatch batch(m_storage.GetDatabase()); + UpdateWithSigningProvider(batch, provider); NotifyFirstKeyTimeChanged(this, m_wallet_descriptor.creation_time); return {}; } +void DescriptorScriptPubKeyMan::UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider) +{ + AssertLockHeld(cs_desc_man); + // Add the private keys to the descriptor + for (const auto& entry : signing_provider.keys) { + const CKey& key = entry.second; + if (!AddDescriptorKeyWithDB(batch, key, key.GetPubKey())) { + throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed"); + } + } + + // Top up key pool, to generate scriptPubKeys + if (!TopUpWithDB(batch)) { + throw std::runtime_error("Could not top up scriptPubKeys"); + } +} + bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error) { LOCK(cs_desc_man); diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 2762ebc2e61..238888fcf0e 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -303,6 +303,13 @@ private: */ mutable std::map m_musig2_secnonces; + //! Create a new DescriptorScriptPubKeyMan from an existing descriptor (i.e. from an import) + DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size) + : ScriptPubKeyMan(storage), + m_keypool_size(keypool_size), + m_wallet_descriptor(descriptor) + {} + bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); @@ -316,6 +323,9 @@ private: void Load(); + void AddDescriptorKey(const CKey& key, const CPubKey &pubkey); + void UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); + protected: //! Create a DescriptorScriptPubKeyMan from existing data (i.e. during loading) DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys); @@ -326,18 +336,14 @@ protected: bool TopUpWithDB(WalletBatch& batch, unsigned int size = 0); public: - //! Create a new DescriptorScriptPubKeyMan from an existing descriptor (i.e. from an import) - DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size) - : ScriptPubKeyMan(storage), - m_keypool_size(keypool_size), - m_wallet_descriptor(descriptor) - {} DescriptorScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : ScriptPubKeyMan(storage), m_keypool_size(keypool_size) {} static std::unique_ptr LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys); + static std::unique_ptr CreateFromImport(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider); + static std::unique_ptr CreateFromMigration(WalletStorage& storage, WalletBatch& batch, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider); mutable RecursiveMutex cs_desc_man; @@ -391,9 +397,8 @@ public: uint256 GetID() const override; bool HasWalletDescriptor(const WalletDescriptor& desc) const; - util::Result UpdateWalletDescriptor(WalletDescriptor& descriptor); + util::Result UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider); bool CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error); - void AddDescriptorKey(const CKey& key, const CPubKey &pubkey); void WriteDescriptor(); WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7b94b659700..7f40fa7011f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3753,11 +3753,11 @@ util::Result> CWallet::AddWall auto spk_man = GetDescriptorScriptPubKeyMan(desc); if (spk_man) { WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString()); - if (auto spkm_res = spk_man->UpdateWalletDescriptor(desc); !spkm_res) { + if (auto spkm_res = spk_man->UpdateWalletDescriptor(desc, signing_provider); !spkm_res) { return util::Error{util::ErrorString(spkm_res)}; } } else { - auto new_spk_man = std::unique_ptr(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size)); + auto new_spk_man = DescriptorScriptPubKeyMan::CreateFromImport(*this, desc, m_keypool_size, signing_provider); spk_man = new_spk_man.get(); // Save the descriptor to memory @@ -3765,17 +3765,6 @@ util::Result> CWallet::AddWall AddScriptPubKeyMan(id, std::move(new_spk_man)); } - // Add the private keys to the descriptor - for (const auto& entry : signing_provider.keys) { - const CKey& key = entry.second; - spk_man->AddDescriptorKey(key, key.GetPubKey()); - } - - // Top up key pool, the manager will generate new scriptPubKeys internally - if (!spk_man->TopUp()) { - return util::Error{_("Could not top up scriptPubKeys")}; - } - // Apply the label if necessary // Note: we disable labels for ranged descriptors if (!desc.descriptor->IsRange()) {