Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
3752void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal)3753{3754// Activating ScriptPubKeyManager for a given output and change type is incompatible with legacy wallets.3755// Legacy wallets have only one ScriptPubKeyManager and it's active for all output and change types.3756Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));3758WalletLogPrintf("Setting spkMan to active: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(type), internal ? "true" : "false");3759auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;3760auto& spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers;3761auto spk_man = m_spk_managers.at(id).get();3762spk_mans[type] = spk_man;3764const auto it = spk_mans_other.find(type);3765if (it != spk_mans_other.end() && it->second == spk_man) {3766spk_mans_other.erase(type);3767}3769NotifyCanGetAddressesChanged();3770}3772void CWallet::DeactivateScriptPubKeyMan(uint256 id, OutputType type, bool internal)3773{3774auto spk_man = GetScriptPubKeyMan(type, internal);3775if (spk_man != nullptr && spk_man->GetID() == id) {3776WalletLogPrintf("Deactivate spkMan: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(type), internal ? "true" : "false");3777WalletBatch batch(GetDatabase());3778if (!batch.EraseActiveScriptPubKeyMan(static_cast<uint8_t>(type), internal)) {3779throw std::runtime_error(std::string(__func__) + ": erasing active ScriptPubKeyMan id failed");3780}3782auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;3783spk_mans.erase(type);3784}3786NotifyCanGetAddressesChanged();3787}3789DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const3790{3791auto spk_man_pair = std::find_if(m_spk_managers.begin(), m_spk_managers.end(), [&desc](const auto& item) {3792DescriptorScriptPubKeyMan* spk_manager = dynamic_cast<DescriptorScriptPubKeyMan*>(item.second.get());3793return spk_manager != nullptr && spk_manager->HasWalletDescriptor(desc);3794});3796if (spk_man_pair != m_spk_managers.end()) {3797return dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man_pair->second.get());3798}3800return nullptr;3801}3803std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const3804{3805// only active ScriptPubKeyMan can be internal3806if (!GetActiveScriptPubKeyMans().contains(spk_man)) {3807return std::nullopt;3808}3810const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);3811if (!desc_spk_man) {3812throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type.");3813}3815LOCK(desc_spk_man->cs_desc_man);3816const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType();3817assert(type.has_value());3819return GetScriptPubKeyMan(*type, /* internal= */ true) == desc_spk_man;3820}3822util::Result<std::reference_wrapper<DescriptorScriptPubKeyMan>> CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal)3823{3824AssertLockHeld(cs_wallet);3826Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));3828auto spk_man = GetDescriptorScriptPubKeyMan(desc);3829if (spk_man) {3830WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());3831if (auto spkm_res = spk_man->UpdateWalletDescriptor(desc, signing_provider); !spkm_res) {3832return util::Error{util::ErrorString(spkm_res)};3833}3834} else {3835auto new_spk_man = DescriptorScriptPubKeyMan::CreateFromImport(*this, desc, m_keypool_size, signing_provider);3836spk_man = new_spk_man.get();3838// Save the descriptor to memory3839uint256 id = new_spk_man->GetID();3840AddScriptPubKeyMan(id, std::move(new_spk_man));3842// Write the existing cache to disk3843WalletBatch batch(GetDatabase());3844if (!batch.WriteDescriptorCacheItems(id, desc.cache)) {3845return util::Error{_("Unable to write descriptor cache")};3846}3847}3849// Apply the label if necessary3850// Note: we disable labels for descriptors that are ranged or that don't produce output scripts (i.e. unused())3851if (!desc.descriptor->IsRange() && desc.descriptor->HasScripts()) {3852auto script_pub_keys = spk_man->GetScriptPubKeys();3853if (script_pub_keys.empty()) {3854return util::Error{_("Could not generate scriptPubKeys (cache is empty)")};3855}3857if (!internal) {3858for (const auto& script : script_pub_keys) {3859CTxDestination dest;3860if (ExtractDestination(script, dest)) {3861SetAddressBook(dest, label, AddressPurpose::RECEIVE);3862}3863}3864}3865}3867// Save the descriptor to DB3868spk_man->WriteDescriptor();3870// Break balance caches so that outputs that are now IsMine in already known txs will be included in the balance3871MarkDirty();3873return std::reference_wrapper(*spk_man);3874}3876bool CWallet::MigrateToSQLite(bilingual_str& error)3877{3878AssertLockHeld(cs_wallet);3880WalletLogPrintf("Migrating wallet storage database from BerkeleyDB to SQLite.\n");3882if (m_database->Format() == "sqlite") {3883error = _("Error: This wallet already uses SQLite");3884return false;3885}3887// Get all of the records for DB type migration3888std::unique_ptr<DatabaseBatch> batch = m_database->MakeBatch();3889std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor();3890std::vector<std::pair<SerializeData, SerializeData>> records;3891if (!cursor) {3892error = _("Error: Unable to begin reading all records in the database");3893return false;3894}3895DatabaseCursor::Status status = DatabaseCursor::Status::FAIL;3896while (true) {3897DataStream ss_key{};3898DataStream ss_value{};3899status = cursor->Next(ss_key, ss_value);3900if (status != DatabaseCursor::Status::MORE) {3901break;3902}3903SerializeData key(ss_key.begin(), ss_key.end());3904SerializeData value(ss_value.begin(), ss_value.end());3905records.emplace_back(key, value);3906}3907cursor.reset();3908batch.reset();3909if (status != DatabaseCursor::Status::DONE) {3910error = _("Error: Unable to read all records in the database");3911return false;3912}3914// Close this database and delete the file3915fs::path db_path = fs::PathFromString(m_database->Filename());3916m_database->Close();3917fs::remove(db_path);3919// Generate the path for the location of the migrated wallet3920// Wallets that are plain files rather than wallet directories will be migrated to be wallet directories.3921const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(m_name));3923// Make new DB3924DatabaseOptions opts;3925opts.require_create = true;3926opts.require_format = DatabaseFormat::SQLITE;3927DatabaseStatus db_status;3928std::unique_ptr<WalletDatabase> new_db = MakeDatabase(wallet_path, opts, db_status, error);3929assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists.3930m_database.reset();3931m_database = std::move(new_db);3933// Write existing records into the new DB3934batch = m_database->MakeBatch();3935bool began = batch->TxnBegin();3936assert(began); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.3937for (const auto& [key, value] : records) {3938if (!batch->Write(std::span{key}, std::span{value})) {3939batch->TxnAbort();3940m_database->Close();3941fs::remove(m_database->Filename());3942assert(false); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.3943}3944}3945bool committed = batch->TxnCommit();3946assert(committed); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.3947return true;3948}3950std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& error) const3951{3952AssertLockHeld(cs_wallet);3954LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM();3955if (!Assume(legacy_spkm)) {3956// This shouldn't happen3957error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));3958return std::nullopt;3959}3961std::optional<MigrationData> res = legacy_spkm->MigrateToDescriptor();3962if (res == std::nullopt) {3963error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure to provide the wallet's passphrase if it is encrypted.");3964return std::nullopt;3965}3966return res;3967}3969util::Result<void> CWallet::ApplyMigrationData(WalletBatch& local_wallet_batch, MigrationData& data)3970{3971AssertLockHeld(cs_wallet);3973LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM();3974if (!Assume(legacy_spkm)) {3975// This shouldn't happen3976return util::Error{Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"))};3977}3979// Note: when the legacy wallet has no spendable scripts, it must be empty at the end of the process.3980bool has_spendable_material = !data.desc_spkms.empty() || data.master_key.key.IsValid();3982// Get all invalid or non-watched scripts that will not be migrated3983std::set<CTxDestination> not_migrated_dests;3984for (const auto& script : legacy_spkm->GetNotMineScriptPubKeys()) {3985CTxDestination dest;3986if (ExtractDestination(script, dest)) not_migrated_dests.emplace(dest);3987}3989// When the legacy wallet has no spendable scripts, the main wallet will be empty, leaving its script cache empty as well.3990// The watch-only and/or solvable wallet(s) will contain the scripts in their respective caches.3991if (!data.desc_spkms.empty()) Assume(!m_cached_spks.empty());3992if (!data.watch_descs.empty()) Assume(!data.watchonly_wallet->m_cached_spks.empty());3993if (!data.solvable_descs.empty()) Assume(!data.solvable_wallet->m_cached_spks.empty());3995for (auto& desc_spkm : data.desc_spkms) {3996if (m_spk_managers.contains(desc_spkm->GetID())) {3997return util::Error{_("Error: Duplicate descriptors created during migration. Your wallet may be corrupted.")};3998}3999uint256 id = desc_spkm->GetID();4000AddScriptPubKeyMan(id, std::move(desc_spkm));