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