Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto 2 : // Copyright (c) 2009-2022 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 <common/args.h> 9 : #include <interfaces/chain.h> 10 : #include <scheduler.h> 11 : #include <util/check.h> 12 : #include <util/fs.h> 13 : #include <util/string.h> 14 : #include <util/translation.h> 15 : #include <wallet/context.h> 16 : #include <wallet/spend.h> 17 2 : #include <wallet/wallet.h> 18 2 : #include <wallet/walletdb.h> 19 : 20 : #include <univalue.h> 21 : 22 : #include <system_error> 23 : 24 : namespace wallet { 25 0 : bool VerifyWallets(WalletContext& context) 26 : { 27 2 : interfaces::Chain& chain = *context.chain; 28 0 : ArgsManager& args = *Assert(context.args); 29 : 30 0 : if (args.IsArgSet("-walletdir")) { 31 0 : const fs::path wallet_dir{args.GetPathArg("-walletdir")}; 32 0 : 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 0 : fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); 37 0 : if (error || !fs::exists(canonical_wallet_dir)) { 38 0 : chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir))); 39 0 : return false; 40 0 : } else if (!fs::is_directory(canonical_wallet_dir)) { 41 0 : chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir))); 42 0 : return false; 43 : // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version 44 0 : } else if (!wallet_dir.is_absolute()) { 45 0 : chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir))); 46 0 : return false; 47 : } 48 0 : args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir)); 49 0 : } 50 : 51 0 : LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir())); 52 : 53 0 : 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 0 : if (!args.IsArgSet("wallet")) { 58 0 : DatabaseOptions options; 59 : DatabaseStatus status; 60 0 : ReadDatabaseArgs(args, options); 61 0 : bilingual_str error_string; 62 0 : options.require_existing = true; 63 0 : options.verify = false; 64 0 : if (MakeWalletDatabase("", options, status, error_string)) { 65 0 : common::SettingsValue wallets(common::SettingsValue::VARR); 66 0 : 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 0 : chain.updateRwSetting("wallet", wallets, /* write= */ false); 71 0 : } 72 0 : } 73 : 74 2 : // Keep track of each wallet absolute path to detect duplicates. 75 0 : std::set<fs::path> wallet_paths; 76 : 77 0 : for (const auto& wallet : chain.getSettingsList("wallet")) { 78 0 : const auto& wallet_file = wallet.get_str(); 79 0 : const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file)); 80 : 81 0 : if (!wallet_paths.insert(path).second) { 82 0 : chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file)); 83 0 : continue; 84 : } 85 : 86 0 : DatabaseOptions options; 87 : DatabaseStatus status; 88 0 : ReadDatabaseArgs(args, options); 89 0 : options.require_existing = true; 90 0 : options.verify = true; 91 2 : bilingual_str error_string; 92 0 : if (!MakeWalletDatabase(wallet_file, options, status, error_string)) { 93 0 : if (status == DatabaseStatus::FAILED_NOT_FOUND) { 94 0 : chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original))); 95 0 : } else { 96 0 : chain.initError(error_string); 97 0 : return false; 98 : } 99 2 : } 100 0 : } 101 : 102 0 : return true; 103 0 : } 104 : 105 0 : bool LoadWallets(WalletContext& context) 106 : { 107 0 : interfaces::Chain& chain = *context.chain; 108 : try { 109 0 : std::set<fs::path> wallet_paths; 110 0 : for (const auto& wallet : chain.getSettingsList("wallet")) { 111 0 : const auto& name = wallet.get_str(); 112 0 : if (!wallet_paths.insert(fs::PathFromString(name)).second) { 113 0 : continue; 114 : } 115 0 : DatabaseOptions options; 116 : DatabaseStatus status; 117 0 : ReadDatabaseArgs(*context.args, options); 118 0 : options.require_existing = true; 119 0 : options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets() 120 0 : bilingual_str error; 121 0 : std::vector<bilingual_str> warnings; 122 0 : std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error); 123 0 : if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) { 124 0 : continue; 125 : } 126 0 : chain.initMessage(_("Loading wallet…").translated); 127 0 : std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings) : nullptr; 128 0 : if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n"))); 129 0 : if (!pwallet) { 130 0 : chain.initError(error); 131 0 : return false; 132 : } 133 : 134 0 : NotifyWalletLoaded(context, pwallet); 135 0 : AddWallet(context, pwallet); 136 0 : } 137 0 : return true; 138 0 : } catch (const std::runtime_error& e) { 139 0 : chain.initError(Untranslated(e.what())); 140 0 : return false; 141 0 : } 142 0 : } 143 : 144 0 : void StartWallets(WalletContext& context, CScheduler& scheduler) 145 : { 146 0 : for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) { 147 0 : pwallet->postInitProcess(); 148 : } 149 : 150 : // Schedule periodic wallet flushes and tx rebroadcasts 151 0 : if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) { 152 0 : scheduler.scheduleEvery([&context] { MaybeCompactWalletDB(context); }, std::chrono::milliseconds{500}); 153 0 : } 154 0 : scheduler.scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min); 155 0 : } 156 : 157 0 : void FlushWallets(WalletContext& context) 158 : { 159 0 : for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) { 160 0 : pwallet->Flush(); 161 : } 162 0 : } 163 2 : 164 2 : void StopWallets(WalletContext& context) 165 2 : { 166 2 : for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) { 167 2 : pwallet->Close(); 168 2 : } 169 2 : } 170 2 : 171 0 : void UnloadWallets(WalletContext& context) 172 : { 173 0 : auto wallets = GetWallets(context); 174 0 : while (!wallets.empty()) { 175 0 : auto wallet = wallets.back(); 176 0 : wallets.pop_back(); 177 0 : std::vector<bilingual_str> warnings; 178 0 : RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings); 179 0 : UnloadWallet(std::move(wallet)); 180 0 : } 181 0 : } 182 : } // namespace wallet