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