LCOV - code coverage report
Current view: top level - src/wallet - load.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 111 115 96.5 %
Date: 2026-06-25 07:23:43 Functions: 10 10 100.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        1443 : bool VerifyWallets(WalletContext& context)
      29             : {
      30        1443 :     interfaces::Chain& chain = *context.chain;
      31        1443 :     ArgsManager& args = *Assert(context.args);
      32             : 
      33        1443 :     if (args.IsArgSet("-walletdir")) {
      34          59 :         const fs::path wallet_dir{args.GetPathArg("-walletdir")};
      35          59 :         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          59 :         fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
      40          59 :         if (error || !fs::exists(canonical_wallet_dir)) {
      41          13 :             chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
      42          13 :             return false;
      43          46 :         } else if (!fs::is_directory(canonical_wallet_dir)) {
      44          13 :             chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
      45          13 :             return false;
      46             :         // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
      47          33 :         } else if (!wallet_dir.is_absolute()) {
      48           7 :             chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
      49           7 :             return false;
      50             :         }
      51          26 :         args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
      52          59 :     }
      53             : 
      54        1410 :     LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir()));
      55             : 
      56        1410 :     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        1410 :     if (!args.IsArgSet("wallet")) {
      61         771 :         DatabaseOptions options;
      62             :         DatabaseStatus status;
      63         771 :         ReadDatabaseArgs(args, options);
      64         771 :         bilingual_str error_string;
      65         771 :         options.require_existing = true;
      66         771 :         options.verify = false;
      67         771 :         if (MakeWalletDatabase("", options, status, error_string)) {
      68           2 :             util::SettingsValue wallets(util::SettingsValue::VARR);
      69           2 :             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           2 :             chain.updateRwSetting("wallet", wallets, /* write= */ false);
      74           2 :         }
      75         771 :     }
      76             : 
      77             :     // Keep track of each wallet absolute path to detect duplicates.
      78        1410 :     std::set<fs::path> wallet_paths;
      79             : 
      80        2006 :     for (const auto& wallet : chain.getSettingsList("wallet")) {
      81         596 :         const auto& wallet_file = wallet.get_str();
      82         596 :         const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
      83             : 
      84         596 :         if (!wallet_paths.insert(path).second) {
      85           6 :             chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
      86           6 :             continue;
      87             :         }
      88             : 
      89         590 :         DatabaseOptions options;
      90             :         DatabaseStatus status;
      91         590 :         ReadDatabaseArgs(args, options);
      92         590 :         options.require_existing = true;
      93         590 :         options.verify = true;
      94         590 :         bilingual_str error_string;
      95         590 :         if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
      96          16 :             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          16 :                 chain.initError(error_string);
     100          16 :                 return false;
     101             :             }
     102           0 :         }
     103         596 :     }
     104             : 
     105        1394 :     return true;
     106        1443 : }
     107             : 
     108        1354 : bool LoadWallets(WalletContext& context)
     109             : {
     110        1354 :     interfaces::Chain& chain = *context.chain;
     111             :     try {
     112        1354 :         std::set<fs::path> wallet_paths;
     113        1930 :         for (const auto& wallet : chain.getSettingsList("wallet")) {
     114         576 :             const auto& name = wallet.get_str();
     115         576 :             if (!wallet_paths.insert(fs::PathFromString(name)).second) {
     116           6 :                 continue;
     117             :             }
     118         570 :             DatabaseOptions options;
     119             :             DatabaseStatus status;
     120         570 :             ReadDatabaseArgs(*context.args, options);
     121         570 :             options.require_existing = true;
     122         570 :             options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
     123         570 :             bilingual_str error_string;
     124         570 :             std::vector<bilingual_str> warnings;
     125         570 :             std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error_string);
     126         570 :             if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
     127           0 :                 continue;
     128             :             }
     129         570 :             chain.initMessage(_("Loading wallet…").translated);
     130         570 :             const std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error_string, warnings) : nullptr;
     131         566 :             if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
     132         566 :             if (!pwallet) {
     133          18 :                 chain.initError(error_string);
     134          18 :                 return false;
     135             :             }
     136             : 
     137         548 :             NotifyWalletLoaded(context, pwallet);
     138         548 :             AddWallet(context, pwallet);
     139         570 :         }
     140        1332 :         return true;
     141        1354 :     } catch (const std::runtime_error& e) {
     142           4 :         chain.initError(Untranslated(e.what()));
     143           4 :         return false;
     144           4 :     }
     145        1358 : }
     146             : 
     147        1328 : void StartWallets(WalletContext& context, CScheduler& scheduler)
     148             : {
     149        1872 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
     150         544 :         pwallet->postInitProcess();
     151             :     }
     152             : 
     153             :     // Schedule periodic wallet flushes and tx rebroadcasts
     154        1328 :     if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
     155       47655 :         scheduler.scheduleEvery([&context] { MaybeCompactWalletDB(context); }, std::chrono::milliseconds{500});
     156        1328 :     }
     157        2289 :     scheduler.scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
     158        1328 : }
     159             : 
     160        1436 : void FlushWallets(WalletContext& context)
     161             : {
     162        3227 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
     163        1791 :         if (CCoinJoinClientOptions::IsEnabled()) {
     164        1791 :             assert(pwallet->coinjoin_available());
     165             :             // Stop CoinJoin, release keys
     166        1791 :             pwallet->coinjoin_loader().FlushWallet(pwallet->GetName());
     167        1791 :         }
     168        1791 :         pwallet->Flush();
     169             :     }
     170        1436 : }
     171             : 
     172        1436 : void StopWallets(WalletContext& context)
     173             : {
     174        3227 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
     175        1791 :         pwallet->Close();
     176             :     }
     177        1436 : }
     178             : 
     179        1635 : void UnloadWallets(WalletContext& context)
     180             : {
     181        1635 :     auto wallets = GetWallets(context);
     182        3426 :     while (!wallets.empty()) {
     183        1791 :         auto wallet = wallets.back();
     184        1791 :         wallets.pop_back();
     185        1791 :         std::vector<bilingual_str> warnings;
     186        1791 :         RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings);
     187        1791 :         UnloadWallet(std::move(wallet));
     188        1791 :     }
     189        1635 : }
     190             : } // namespace wallet

Generated by: LCOV version 1.16