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