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.
251wallet->WalletLogPrintf("Releasing wallet %s..\n", name);252delete wallet;253// Wallet is now released, notify WaitForDeleteWallet, if any.254{255LOCK(g_wallet_release_mutex);256if (g_unloading_wallet_set.erase(name) == 0) {257// WaitForDeleteWallet was not called for this wallet, all done.258return;259}260}261g_wallet_release_cv.notify_all();262}264void WaitForDeleteWallet(std::shared_ptr<CWallet>&& wallet)265{266// Mark wallet for unloading.267const std::string name = wallet->GetName();268{269LOCK(g_wallet_release_mutex);270g_unloading_wallet_set.insert(name);271// Do not expect to be the only one removing this wallet.272// Multiple threads could simultaneously be waiting for deletion.273}275// Time to ditch our shared_ptr and wait for FlushAndDeleteWallet call.276wallet.reset();277{278WAIT_LOCK(g_wallet_release_mutex, lock);279while (g_unloading_wallet_set.contains(name)) {280g_wallet_release_cv.wait(lock);281}282}283}285namespace {286std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)287{288try {289std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);290if (!database) {291error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;292return nullptr;293}295context.chain->initMessage(_("Loading wallet…"));296std::shared_ptr<CWallet> wallet = CWallet::LoadExisting(context, name, std::move(database), error, warnings);297if (!wallet) {298error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;299status = DatabaseStatus::FAILED_LOAD;300return nullptr;301}303NotifyWalletLoaded(context, wallet);304AddWallet(context, wallet);305wallet->postInitProcess();307// Write the wallet setting308UpdateWalletSetting(*context.chain, name, load_on_start, warnings);310return wallet;311} catch (const std::runtime_error& e) {312error = Untranslated(e.what());313status = DatabaseStatus::FAILED_LOAD;314return nullptr;315}316}318class FastWalletRescanFilter319{320public:321FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)322{323// create initial filter with scripts from all ScriptPubKeyMans324for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {325auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};326assert(desc_spkm != nullptr);327AddScriptPubKeys(desc_spkm);328// save each range descriptor's end for possible future filter updates329if (desc_spkm->IsHDEnabled()) {330m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange());331}332}333}335void UpdateIfNeeded()336{337// repopulate filter with new scripts if top-up has happened since last iteration338for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) {339auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))};340assert(desc_spkm != nullptr);341int32_t current_range_end{desc_spkm->GetEndRange()};342if (current_range_end > last_range_end) {343AddScriptPubKeys(desc_spkm, last_range_end);344m_last_range_ends.at(desc_spkm->GetID()) = current_range_end;345}346}347}349std::optional<bool> MatchesBlock(const uint256& block_hash) const350{351return m_wallet.chain().blockFilterMatchesAny(BlockFilterType::BASIC, block_hash, m_filter_set);352}354private:355const CWallet& m_wallet;356/** Map for keeping track of each range descriptor's last seen end range.357* This information is used to detect whether new addresses were derived358* (that is, if the current end range is larger than the saved end range)359* after processing a block and hence a filter set update is needed to360* take possible keypool top-ups into account.361*/362std::map<uint256, int32_t> m_last_range_ends;363GCSFilter::ElementSet m_filter_set;365void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0)366{367for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) {368m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end());369}370}371};372} // namespace374std::shared_ptr<CWallet> LoadWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)375{376auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));377if (!result.second) {378error = Untranslated("Wallet already loading.");379status = DatabaseStatus::FAILED_LOAD;380return nullptr;381}382auto wallet = LoadWalletInternal(context, name, load_on_start, options, status, error, warnings);383WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));384return wallet;385}387std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)388{389// Wallet must have a non-empty name390if (name.empty()) {391error = Untranslated("Wallet name cannot be empty");392status = DatabaseStatus::FAILED_NEW_UNNAMED;393return nullptr;394}396uint64_t wallet_creation_flags = options.create_flags;397const SecureString& passphrase = options.create_passphrase;398bool born_encrypted = !passphrase.empty();400// Only descriptor wallets can be created401Assert(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS);402options.require_format = DatabaseFormat::SQLITE;405// Private keys must be disabled for an external signer wallet406if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {407error = Untranslated("Private keys must be disabled when using an external signer");408status = DatabaseStatus::FAILED_CREATE;409return nullptr;410}412// Do not allow a passphrase when private keys are disabled413if (born_encrypted && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {414error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");415status = DatabaseStatus::FAILED_CREATE;416return nullptr;417}419// Wallet::Verify will check if we're trying to create a wallet with a duplicate name.420std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);421if (!database) {422error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;423status = DatabaseStatus::FAILED_VERIFY;424return nullptr;425}427// Make the wallet428context.chain->initMessage(_("Creating wallet…"));429std::shared_ptr<CWallet> wallet = CWallet::CreateNew(context, name, std::move(database), wallet_creation_flags, born_encrypted, error, warnings);430if (!wallet) {431error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;432status = DatabaseStatus::FAILED_CREATE;433return nullptr;434}436// Encrypt the wallet437if (born_encrypted) {438if (!wallet->EncryptWallet(passphrase)) {439error = Untranslated("Error: Wallet created but failed to encrypt.");440status = DatabaseStatus::FAILED_ENCRYPT;441return nullptr;442}443}445WITH_LOCK(wallet->cs_wallet, wallet->LogStats());446NotifyWalletLoaded(context, wallet);447AddWallet(context, wallet);448wallet->postInitProcess();450// Write the wallet settings451UpdateWalletSetting(*context.chain, name, load_on_start, warnings);453status = DatabaseStatus::SUCCESS;454return wallet;455}457// Re-creates wallet from the backup file by renaming and moving it into the wallet's directory.458// If 'load_after_restore=true', the wallet object will be fully initialized and appended to the context.459std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings, bool load_after_restore, bool allow_unnamed)460{461// Error if the wallet name is empty and allow_unnamed == false462// allow_unnamed == true is only used by migration to migrate an unnamed wallet463if (!allow_unnamed && wallet_name.empty()) {464error = Untranslated("Wallet name cannot be empty");465status = DatabaseStatus::FAILED_NEW_UNNAMED;466return nullptr;467}469DatabaseOptions options;470ReadDatabaseArgs(*context.args, options);471options.require_existing = true;473const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name));474auto wallet_file = wallet_path / "wallet.dat";475std::shared_ptr<CWallet> wallet;476bool wallet_file_copied = false;477bool created_parent_dir = false;479try {480if (!fs::exists(backup_file)) {481error = Untranslated("Backup file does not exist");482status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;483return nullptr;484}486// Wallet directories are allowed to exist, but must not contain a .dat file.487// Any existing wallet database is treated as a hard failure to prevent overwriting.488if (fs::exists(wallet_path)) {489// If this is a file, it is the db and we don't want to overwrite it.490if (!fs::is_directory(wallet_path)) {491error = Untranslated(strprintf("Failed to restore wallet. Database file exists '%s'.", fs::PathToString(wallet_path)));492status = DatabaseStatus::FAILED_ALREADY_EXISTS;493return nullptr;494}496// Check we are not going to overwrite an existing db file497if (fs::exists(wallet_file)) {498error = Untranslated(strprintf("Failed to restore wallet. Database file exists in '%s'.", fs::PathToString(wallet_file)));499status = DatabaseStatus::FAILED_ALREADY_EXISTS;500return nullptr;