Inside Bitcoin
Opening the selected file…
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.
1// Copyright (c) 2009-2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <wallet/load.h>8#include <common/args.h>9#include <interfaces/chain.h>10#include <scheduler.h>11#include <util/check.h>12#include <util/fs.h>13#include <util/string.h>14#include <util/translation.h>15#include <wallet/context.h>16#include <wallet/spend.h>17#include <wallet/wallet.h>18#include <wallet/walletdb.h>20#include <univalue.h>22#include <system_error>24using util::Join;26namespace wallet {27bool VerifyWallets(WalletContext& context)28{29interfaces::Chain& chain = *context.chain;30ArgsManager& args = *Assert(context.args);32if (args.IsArgSet("-walletdir")) {33const fs::path wallet_dir{args.GetPathArg("-walletdir")};34std::error_code error;35// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory36// It also lets the fs::exists and fs::is_directory checks below pass on windows, since they return false37// if a path has trailing slashes, and it strips trailing slashes.38fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);39if (error || !fs::exists(canonical_wallet_dir)) {40chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));41return false;42} else if (!fs::is_directory(canonical_wallet_dir)) {43chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));44return false;45// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version46} else if (!wallet_dir.is_absolute()) {47chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));48return false;49}50args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));51}53LogInfo("Using wallet directory %s", fs::PathToString(GetWalletDir()));54// Print general DB information55LogDBInfo();57chain.initMessage(_("Verifying wallet(s)…"));59// For backwards compatibility if an unnamed top level wallet exists in the60// wallets directory, include it in the default list of wallets to load.61if (!args.IsArgSet("wallet")) {62DatabaseOptions options;63DatabaseStatus status;64ReadDatabaseArgs(args, options);65bilingual_str error_string;66options.require_existing = true;67options.verify = false;68if (MakeWalletDatabase("", options, status, error_string)) {69common::SettingsValue wallets(common::SettingsValue::VARR);70wallets.push_back(""); // Default wallet name is ""71// Pass write=false because no need to write file and probably72// better not to. If unnamed wallet needs to be added next startup73// and the setting is empty, this code will just run again.74chain.overwriteRwSetting("wallet", std::move(wallets), interfaces::SettingsAction::SKIP_WRITE);75}76}78// Keep track of each wallet absolute path to detect duplicates.79std::set<fs::path> wallet_paths;81for (const auto& wallet : chain.getSettingsList("wallet")) {82if (!wallet.isStr()) {83chain.initError(_("Invalid value detected for '-wallet' or '-nowallet'. "84"'-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets"));85return false;86}87const auto& wallet_file = wallet.get_str();88const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));90if (!wallet_paths.insert(path).second) {91chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));92continue;93}95DatabaseOptions options;96DatabaseStatus status;97ReadDatabaseArgs(args, options);98options.require_existing = true;99options.verify = true;100bilingual_str error_string;101if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {102if (status == DatabaseStatus::FAILED_NOT_FOUND) {103chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));104} else if (status == DatabaseStatus::FAILED_LEGACY_DISABLED) {105// Skipping legacy wallets as they will not be loaded.106// This will be properly communicated to the user during the loading process.107continue;108} else {109chain.initError(error_string);110return false;111}112}113}115return true;116}118bool LoadWallets(WalletContext& context)119{120interfaces::Chain& chain = *context.chain;121try {122std::set<fs::path> wallet_paths;123for (const auto& wallet : chain.getSettingsList("wallet")) {124if (!wallet.isStr()) {125chain.initError(_("Invalid value detected for '-wallet' or '-nowallet'. "126"'-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets"));127return false;128}129const auto& name = wallet.get_str();130if (!wallet_paths.insert(fs::PathFromString(name)).second) {131continue;132}133DatabaseOptions options;134DatabaseStatus status;135ReadDatabaseArgs(*context.args, options);136options.require_existing = true;137options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()138bilingual_str error;139std::vector<bilingual_str> warnings;140std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);141if (!database) {142if (status == DatabaseStatus::FAILED_NOT_FOUND) continue;143if (status == DatabaseStatus::FAILED_LEGACY_DISABLED) {144// Inform user that legacy wallet is not loaded and suggest upgrade options145chain.initWarning(error);146continue;147}148}149chain.initMessage(_("Loading wallet…"));150std::shared_ptr<CWallet> pwallet = database ? CWallet::LoadExisting(context, name, std::move(database), error, warnings) : nullptr;151if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));152if (!pwallet) {153chain.initError(error);154return false;155}157NotifyWalletLoaded(context, pwallet);158AddWallet(context, pwallet);159}160return true;161} catch (const std::runtime_error& e) {162chain.initError(Untranslated(e.what()));163return false;164}165}167void StartWallets(WalletContext& context)168{169for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {170pwallet->postInitProcess();171}173context.scheduler->scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);174}176void UnloadWallets(WalletContext& context)177{178auto wallets = GetWallets(context);179while (!wallets.empty()) {180auto wallet = wallets.back();181wallets.pop_back();182std::vector<bilingual_str> warnings;183RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings);184WaitForDeleteWallet(std::move(wallet));185}186}187} // namespace wallet