Repository: bitcoin/bitcoin
File: src/wallet/wallet.cpp (C++)

Code before:
        // First, delete the db files we have created throughout this process and nothing else
        for (const fs::path& file : wallet_files_to_remove) {
            fs::remove(file);
        }

        // Second, delete the created wallet directories and nothing else. They must be empty at this point.
        for (const fs::path& dir : wallet_empty_dirs_to_remove) {
            Assert(fs::is_empty(dir));
            fs::remove(dir);
        }

        // Restore the backup
        // Convert the backup file to the wallet db file by renaming it and moving it into the wallet's directory.
        bilingual_str restore_error;
        const auto& ptr_wallet = RestoreWallet(context, backup_path, wallet_name, /*load_on_start=*/std::nullopt, status, restore_error, warnings, /*load_after_restore=*/false);
        if (!restore_error.empty()) {
            error += restore_error + _("\nUnable to restore backup of wallet.");
            return util::Error{error};
        }
        // Verify that the legacy wallet is not loaded after restoring from the backup.
        assert(!ptr_wallet);

        return util::Error{error};
    }
    return res;
}

void CWallet::CacheNewScriptPubKeys(const std::set<CScript>& spks, ScriptPubKeyMan* spkm)
{
    for (const auto& script : spks) {
        m_cached_spks[script].push_back(spkm);
    }
}

void CWallet::TopUpCallback(const std::set<CScript>& spks, ScriptPubKeyMan* spkm)
{
    // Update scriptPubKey cache
    CacheNewScriptPubKeys(spks, spkm);
}

std::set<CExtPubKey> CWallet::GetActiveHDPubKeys() const
{
    AssertLockHeld(cs_wallet);

    Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));

    std::set<CExtPubKey> active_xpubs;
    for (const auto& spkm : GetActiveScriptPubKeyMans()) {
        const DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm);
        assert(desc_spkm);
        LOCK(desc_spkm->cs_desc_man);

Review comment (bug):
In commit "wallet: fix unnamed legacy wallet migration failure" (239f78641256bcce6ca5db3e34c49e50af9df5ca)

I think commit message could be improved. It doesn't look like this bug depends on on the wallet being named or unnamed. If the wallet name is `"wallet.dat"` or is the name of any top-level database file not inside a dedicated wallet directory, it looks like this bug would still happen. So would recommend replacing "unnamed legacy wallet" with "bare wallet file" or "top-level wallet file". (For reference the change which started creating wallets as directories rather than bare files was be8ab7d082228d09ca529d1a08730d7d5aacb0ed).

Also, IMO it would be helpful to mention the commit which introduced the directory deletion bug: f6ee59b6e2995a3916fb4f0d4cbe15ece2054494, since if you just look at the remove code, you might think the bug has been around since that code was written, but actually it started more recently after f6ee59b6e2995a3916fb4f0d4cbe15ece2054494 removed a `fs::copy_file` call that previously failed in this case and prevented the `fs::remove_all` call from being reached.

Code after:

        // Unload the wallets
        for (std::shared_ptr<CWallet>& w : created_wallets) {
            if (w->HaveChain()) {
                // Unloading for wallets that were loaded for normal use
                if (!RemoveWallet(context, w, /*load_on_start=*/false)) {
                    error += _("\nUnable to cleanup failed migration");
                    return util::Error{error};
                }
                WaitForDeleteWallet(std::move(w));
            } else {
                // Unloading for wallets in local context
                assert(w.use_count() == 1);
                w.reset();
            }
        }

        // First, delete the db files we have created throughout this process and nothing else
        for (const fs::path& file : wallet_files_to_remove) {
            fs::remove(file);
        }

        // Second, delete the created wallet directories and nothing else. They must be empty at this point.
        for (const fs::path& dir : wallet_empty_dirs_to_remove) {
            Assume(fs::is_empty(dir));
            fs::remove(dir);
        }

        // Restore the backup
        // Convert the backup file to the wallet db file by renaming it and moving it into the wallet's directory.
        bilingual_str restore_error;
        const auto& ptr_wallet = RestoreWallet(context, backup_path, wallet_name, /*load_on_start=*/std::nullopt, status, restore_error, warnings, /*load_after_restore=*/false);
        if (!restore_error.empty()) {
            error += restore_error + _("\nUnable to restore backup of wallet.");
            return util::Error{error};
        }
        // Verify that the legacy wallet is not loaded after restoring from the backup.
        assert(!ptr_wallet);

        return util::Error{error};
    }
    return res;
}

void CWallet::CacheNewScriptPubKeys(const std::set<CScript>& spks, ScriptPubKeyMan* spkm)
{
    for (const auto& script : spks) {
        m_cached_spks[script].push_back(spkm);
    }
}