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