LCOV - code coverage report
Current view: top level - src/wallet - load.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 36 115 31.3 %
Date: 2026-06-25 07:23:51 Functions: 4 10 40.0 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <wallet/load.h>
       7             : 
       8             : #include <coinjoin/client.h>
       9             : #include <coinjoin/options.h>
      10             : #include <fs.h>
      11             : #include <net.h>
      12             : #include <interfaces/chain.h>
      13             : #include <scheduler.h>
      14             : #include <util/check.h>
      15             : #include <util/string.h>
      16             : #include <util/system.h>
      17             : #include <util/translation.h>
      18             : #include <wallet/context.h>
      19             : #include <wallet/spend.h>
      20             : #include <wallet/wallet.h>
      21             : #include <wallet/walletdb.h>
      22             : 
      23             : #include <univalue.h>
      24             : 
      25             : #include <system_error>
      26             : 
      27             : namespace wallet {
      28           7 : bool VerifyWallets(WalletContext& context)
      29             : {
      30           7 :     interfaces::Chain& chain = *context.chain;
      31           7 :     ArgsManager& args = *Assert(context.args);
      32             : 
      33           7 :     if (args.IsArgSet("-walletdir")) {
      34           7 :         const fs::path wallet_dir{args.GetPathArg("-walletdir")};
      35           7 :         std::error_code error;
      36             :         // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
      37             :         // It also lets the fs::exists and fs::is_directory checks below pass on windows, since they return false
      38             :         // if a path has trailing slashes, and it strips trailing slashes.
      39           7 :         fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
      40           7 :         if (error || !fs::exists(canonical_wallet_dir)) {
      41           1 :             chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
      42           1 :             return false;
      43           6 :         } else if (!fs::is_directory(canonical_wallet_dir)) {
      44           1 :             chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
      45           1 :             return false;
      46             :         // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
      47           5 :         } else if (!wallet_dir.is_absolute()) {
      48           1 :             chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
      49           1 :             return false;
      50             :         }
      51           4 :         args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
      52           7 :     }
      53             : 
      54           4 :     LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir()));
      55             : 
      56           4 :     chain.initMessage(_("Verifying wallet(s)…").translated);
      57             : 
      58             :     // For backwards compatibility if an unnamed top level wallet exists in the
      59             :     // wallets directory, include it in the default list of wallets to load.
      60           4 :     if (!args.IsArgSet("wallet")) {
      61           4 :         DatabaseOptions options;
      62             :         DatabaseStatus status;
      63           4 :         ReadDatabaseArgs(args, options);
      64           4 :         bilingual_str error_string;
      65           4 :         options.require_existing = true;
      66           4 :         options.verify = false;
      67           4 :         if (MakeWalletDatabase("", options, status, error_string)) {
      68           0 :             util::SettingsValue wallets(util::SettingsValue::VARR);
      69           0 :             wallets.push_back(""); // Default wallet name is ""
      70             :             // Pass write=false because no need to write file and probably
      71             :             // better not to. If unnamed wallet needs to be added next startup
      72             :             // and the setting is empty, this code will just run again.
      73           0 :             chain.updateRwSetting("wallet", wallets, /* write= */ false);
      74           0 :         }
      75           4 :     }
      76             : 
      77             :     // Keep track of each wallet absolute path to detect duplicates.
      78           4 :     std::set<fs::path> wallet_paths;
      79             : 
      80           4 :     for (const auto& wallet : chain.getSettingsList("wallet")) {
      81           0 :         const auto& wallet_file = wallet.get_str();
      82           0 :         const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
      83             : 
      84           0 :         if (!wallet_paths.insert(path).second) {
      85           0 :             chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
      86           0 :             continue;
      87             :         }
      88             : 
      89           0 :         DatabaseOptions options;
      90             :         DatabaseStatus status;
      91           0 :         ReadDatabaseArgs(args, options);
      92           0 :         options.require_existing = true;
      93           0 :         options.verify = true;
      94           0 :         bilingual_str error_string;
      95           0 :         if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
      96           0 :             if (status == DatabaseStatus::FAILED_NOT_FOUND) {
      97           0 :                 chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
      98           0 :             } else {
      99           0 :                 chain.initError(error_string);
     100           0 :                 return false;
     101             :             }
     102           0 :         }
     103           0 :     }
     104             : 
     105           4 :     return true;
     106           7 : }
     107             : 
     108           0 : bool LoadWallets(WalletContext& context)
     109             : {
     110           0 :     interfaces::Chain& chain = *context.chain;
     111             :     try {
     112           0 :         std::set<fs::path> wallet_paths;
     113           0 :         for (const auto& wallet : chain.getSettingsList("wallet")) {
     114           0 :             const auto& name = wallet.get_str();
     115           0 :             if (!wallet_paths.insert(fs::PathFromString(name)).second) {
     116           0 :                 continue;
     117             :             }
     118           0 :             DatabaseOptions options;
     119             :             DatabaseStatus status;
     120           0 :             ReadDatabaseArgs(*context.args, options);
     121           0 :             options.require_existing = true;
     122           0 :             options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
     123           0 :             bilingual_str error_string;
     124           0 :             std::vector<bilingual_str> warnings;
     125           0 :             std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error_string);
     126           0 :             if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
     127           0 :                 continue;
     128             :             }
     129           0 :             chain.initMessage(_("Loading wallet…").translated);
     130           0 :             const std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error_string, warnings) : nullptr;
     131           0 :             if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
     132           0 :             if (!pwallet) {
     133           0 :                 chain.initError(error_string);
     134           0 :                 return false;
     135             :             }
     136             : 
     137           0 :             NotifyWalletLoaded(context, pwallet);
     138           0 :             AddWallet(context, pwallet);
     139           0 :         }
     140           0 :         return true;
     141           0 :     } catch (const std::runtime_error& e) {
     142           0 :         chain.initError(Untranslated(e.what()));
     143           0 :         return false;
     144           0 :     }
     145           0 : }
     146             : 
     147           0 : void StartWallets(WalletContext& context, CScheduler& scheduler)
     148             : {
     149           0 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
     150           0 :         pwallet->postInitProcess();
     151             :     }
     152             : 
     153             :     // Schedule periodic wallet flushes and tx rebroadcasts
     154           0 :     if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
     155           0 :         scheduler.scheduleEvery([&context] { MaybeCompactWalletDB(context); }, std::chrono::milliseconds{500});
     156           0 :     }
     157           0 :     scheduler.scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
     158           0 : }
     159             : 
     160           0 : void FlushWallets(WalletContext& context)
     161             : {
     162           0 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
     163           0 :         if (CCoinJoinClientOptions::IsEnabled()) {
     164           0 :             assert(pwallet->coinjoin_available());
     165             :             // Stop CoinJoin, release keys
     166           0 :             pwallet->coinjoin_loader().FlushWallet(pwallet->GetName());
     167           0 :         }
     168           0 :         pwallet->Flush();
     169             :     }
     170           0 : }
     171             : 
     172           0 : void StopWallets(WalletContext& context)
     173             : {
     174           0 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
     175           0 :         pwallet->Close();
     176             :     }
     177           0 : }
     178             : 
     179         199 : void UnloadWallets(WalletContext& context)
     180             : {
     181         199 :     auto wallets = GetWallets(context);
     182         199 :     while (!wallets.empty()) {
     183           0 :         auto wallet = wallets.back();
     184           0 :         wallets.pop_back();
     185           0 :         std::vector<bilingual_str> warnings;
     186           0 :         RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings);
     187           0 :         UnloadWallet(std::move(wallet));
     188           0 :     }
     189         199 : }
     190             : } // namespace wallet

Generated by: LCOV version 1.16