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.
4001}4003// Remove the LegacyDataSPKM's records from disk4004if (!legacy_spkm->DeleteRecordsWithDB(local_wallet_batch)) {4005return util::Error{_("Error: cannot remove legacy wallet records")};4006}4008// Remove the LegacyDataSPKM from memory4009m_spk_managers.erase(legacy_spkm->GetID());4010m_external_spk_managers.clear();4011m_internal_spk_managers.clear();4013// Setup new descriptors (only if we are migrating any key material)4014SetWalletFlagWithDB(local_wallet_batch, WALLET_FLAG_DESCRIPTORS | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);4015if (has_spendable_material && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {4016// Use the existing master key if we have it4017if (data.master_key.key.IsValid()) {4018SetupDescriptorScriptPubKeyMans(local_wallet_batch, data.master_key);4019} else {4020// Setup with a new seed if we don't.4021SetupOwnDescriptorScriptPubKeyMans(local_wallet_batch);4022}4023}4025// Get best block locator so that we can copy it to the watchonly and solvables4026// Note: The best block locator was introduced in #152 so ancient wallets do not have it4027CBlockLocator best_block_locator;4028(void)local_wallet_batch.ReadBestBlock(best_block_locator);4030// Update m_txos to match the descriptors remaining in this wallet4031m_txos.clear();4032RefreshAllTXOs();4034// Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.4035// We need to go through these in the tx insertion order so that lookups to spends works.4036std::vector<Txid> txids_to_delete;4037std::unique_ptr<WalletBatch> watchonly_batch;4038if (data.watchonly_wallet) {4039watchonly_batch = std::make_unique<WalletBatch>(data.watchonly_wallet->GetDatabase());4040if (!watchonly_batch->TxnBegin()) return util::Error{strprintf(_("Error: database transaction cannot be executed for wallet %s"), data.watchonly_wallet->GetName())};4041// Copy the next tx order pos to the watchonly wallet4042LOCK(data.watchonly_wallet->cs_wallet);4043data.watchonly_wallet->nOrderPosNext = nOrderPosNext;4044watchonly_batch->WriteOrderPosNext(data.watchonly_wallet->nOrderPosNext);4045// Write the locator record. An empty locator is valid and triggers rescan on load.4046if (!watchonly_batch->WriteBestBlock(best_block_locator)) {4047return util::Error{_("Error: Unable to write watchonly wallet best block locator record")};4048}4049}4050std::unique_ptr<WalletBatch> solvables_batch;4051if (data.solvable_wallet) {4052solvables_batch = std::make_unique<WalletBatch>(data.solvable_wallet->GetDatabase());4053if (!solvables_batch->TxnBegin()) return util::Error{strprintf(_("Error: database transaction cannot be executed for wallet %s"), data.solvable_wallet->GetName())};4054// Write the locator record. An empty locator is valid and triggers rescan on load.4055if (!solvables_batch->WriteBestBlock(best_block_locator)) {4056return util::Error{_("Error: Unable to write solvable wallet best block locator record")};4057}4058}4059for (const auto& [_pos, wtx] : wtxOrdered) {4060// Check it is the watchonly wallet's4061// solvable_wallet doesn't need to be checked because transactions for those scripts weren't being watched for4062bool is_mine = IsMine(*wtx->GetTx()) || IsFromMe(*wtx->GetTx());4063if (data.watchonly_wallet) {4064LOCK(data.watchonly_wallet->cs_wallet);4065if (data.watchonly_wallet->IsMine(*wtx->GetTx()) || data.watchonly_wallet->IsFromMe(*wtx->GetTx())) {4066// Add to watchonly wallet4067const Txid& hash = wtx->GetHash();4068DataStream wtx_ser;4069wtx_ser << *wtx;4070CWalletTx copy_wtx(deserialize, wtx_ser, wtx->GetTxs());4071if (!data.watchonly_wallet->LoadToWallet(std::move(copy_wtx))) {4072return util::Error{strprintf(_("Error: Could not add watchonly tx %s to watchonly wallet"), wtx->GetHash().GetHex())};4073}4074watchonly_batch->WriteFullTx(data.watchonly_wallet->mapWallet.at(hash));4075// Mark as to remove from the migrated wallet only if it does not also belong to it4076if (!is_mine) {4077txids_to_delete.push_back(hash);4078continue;4079}4080}4081}4082if (!is_mine) {4083// Both not ours and not in the watchonly wallet4084return util::Error{strprintf(_("Error: Transaction %s in wallet cannot be identified to belong to migrated wallets"), wtx->GetHash().GetHex())};4085}4086// Rewrite the transaction so that anything that may have changed about it in memory also persists to disk4087local_wallet_batch.WriteTxMetadata(*wtx);4088}4090// Do the removes4091if (txids_to_delete.size() > 0) {4092if (auto res = RemoveTxs(local_wallet_batch, txids_to_delete); !res) {4093return util::Error{_("Error: Could not delete watchonly transactions. ") + util::ErrorString(res)};4094}4095}4097// Pair external wallets with their corresponding db handler4098std::vector<std::pair<std::shared_ptr<CWallet>, std::unique_ptr<WalletBatch>>> wallets_vec;4099if (data.watchonly_wallet) wallets_vec.emplace_back(data.watchonly_wallet, std::move(watchonly_batch));4100if (data.solvable_wallet) wallets_vec.emplace_back(data.solvable_wallet, std::move(solvables_batch));4102// Write address book entry to disk4103auto func_store_addr = [](WalletBatch& batch, const CTxDestination& dest, const CAddressBookData& entry) {4104auto address{EncodeDestination(dest)};4105if (entry.purpose) batch.WritePurpose(address, PurposeToString(*entry.purpose));4106if (entry.label) batch.WriteName(address, *entry.label);4107for (const auto& [id, request] : entry.receive_requests) {4108batch.WriteAddressReceiveRequest(dest, id, request);4109}4110if (entry.previously_spent) batch.WriteAddressPreviouslySpent(dest, true);4111};4113// Check the address book data in the same way we did for transactions4114std::vector<CTxDestination> dests_to_delete;4115for (const auto& [dest, record] : m_address_book) {4116// Ensure "receive" entries that are no longer part of the original wallet are transferred to another wallet4117// Entries for everything else ("send") will be cloned to all wallets.4118bool require_transfer = record.purpose == AddressPurpose::RECEIVE && !IsMine(dest);4119bool copied = false;4120for (auto& [wallet, batch] : wallets_vec) {4121LOCK(wallet->cs_wallet);4122if (require_transfer && !wallet->IsMine(dest)) continue;4124// Copy the entire address book entry4125wallet->m_address_book[dest] = record;4126func_store_addr(*batch, dest, record);4128copied = true;4129// Only delete 'receive' records that are no longer part of the original wallet4130if (require_transfer) {4131dests_to_delete.push_back(dest);4132break;4133}4134}4136// Fail immediately if we ever found an entry that was ours and cannot be transferred4137// to any of the created wallets (watch-only, solvable).4138// Means that no inferred descriptor maps to the stored entry. Which mustn't happen.4139if (require_transfer && !copied) {4141// Skip invalid/non-watched scripts that will not be migrated4142if (not_migrated_dests.contains(dest)) {4143dests_to_delete.push_back(dest);4144continue;4145}4147return util::Error{_("Error: Address book data in wallet cannot be identified to belong to migrated wallets")};4148}4149}4151// Persist external wallets address book entries4152for (auto& [wallet, batch] : wallets_vec) {4153if (!batch->TxnCommit()) {4154return util::Error{strprintf(_("Error: Unable to write data to disk for wallet %s"), wallet->GetName())};4155}4156}4158// Remove the things to delete in this wallet4159if (dests_to_delete.size() > 0) {4160for (const auto& dest : dests_to_delete) {4161if (!DelAddressBookWithDB(local_wallet_batch, dest)) {4162return util::Error{_("Error: Unable to remove watchonly address book data")};4163}4164}4165}4167// If there was no key material in the main wallet, there should be no records on it anymore.4168// This wallet will be discarded at the end of the process. Only wallets that contain the4169// migrated records will be presented to the user.4170if (!has_spendable_material) {4171if (!m_address_book.empty()) return util::Error{_("Error: Not all address book records were migrated")};4172if (!mapWallet.empty()) return util::Error{_("Error: Not all transaction records were migrated")};4173}4175return {}; // all good4176}4178bool CWallet::CanGrindR() const4179{4180return !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);4181}4183// Returns wallet prefix for migration.4184// Used to name the backup file and newly created wallets.4185// E.g. a watch-only wallet is named "<prefix>_watchonly".4186static std::string MigrationPrefixName(CWallet& wallet)4187{4188const std::string& name{wallet.GetName()};4189return name.empty() ? "default_wallet" : name;4190}4192bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res, const bool load_on_startup = true) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)4193{4194AssertLockHeld(wallet.cs_wallet);4196// Get all of the descriptors from the legacy wallet4197std::optional<MigrationData> data = wallet.GetDescriptorsForLegacy(error);4198if (data == std::nullopt) return false;4200// Create the watchonly and solvable wallets if necessary4201if (data->watch_descs.size() > 0 || data->solvable_descs.size() > 0) {4202DatabaseOptions options;4203options.require_existing = false;4204options.require_create = true;4205options.require_format = DatabaseFormat::SQLITE;4207WalletContext empty_context;4208empty_context.args = context.args;4210// Make the wallets4211options.create_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_DESCRIPTORS;4212if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {4213options.create_flags |= WALLET_FLAG_AVOID_REUSE;4214}4215if (wallet.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {4216options.create_flags |= WALLET_FLAG_KEY_ORIGIN_METADATA;4217}4218if (data->watch_descs.size() > 0) {4219wallet.WalletLogPrintf("Making a new watchonly wallet containing the watched scripts\n");4221DatabaseStatus status;4222std::vector<bilingual_str> warnings;4223std::string wallet_name = MigrationPrefixName(wallet) + "_watchonly";4224std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);4225if (!database) {4226error = strprintf(_("Wallet file creation failed: %s"), error);4227return false;4228}4230data->watchonly_wallet = CWallet::CreateNew(empty_context, wallet_name, std::move(database), options.create_flags, /*born_encrypted=*/false, error, warnings);4231if (!data->watchonly_wallet) {4232error = _("Error: Failed to create new watchonly wallet");4233return false;4234}4235res.watchonly_wallet = data->watchonly_wallet;4236LOCK(data->watchonly_wallet->cs_wallet);4238// Parse the descriptors and add them to the new wallet4239for (const auto& [desc_str, creation_time] : data->watch_descs) {4240// Parse the descriptor4241FlatSigningProvider keys;4242std::string parse_err;4243std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, parse_err, /*require_checksum=*/ true);4244// LegacyDataSPKM should not produce invalid, multipath, or ranged watch-only descriptors.4245assert(descs.size() == 1);4246assert(!descs.at(0)->IsRange());4248// Add to the wallet4249WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0);4250if (auto spkm_res = data->watchonly_wallet->AddWalletDescriptor(w_desc, keys, "", false); !spkm_res) {