1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 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/string.h>
12 #include <util/system.h>
13 #include <util/translation.h>
14 #include <wallet/wallet.h>
15 #include <wallet/walletdb.h>
16
17 #include <univalue.h>
18
VerifyWallets(interfaces::Chain & chain)19 bool VerifyWallets(interfaces::Chain& chain)
20 {
21 if (gArgs.IsArgSet("-walletdir")) {
22 fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
23 boost::system::error_code error;
24 // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
25 fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
26 if (error || !fs::exists(wallet_dir)) {
27 chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
28 return false;
29 } else if (!fs::is_directory(wallet_dir)) {
30 chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
31 return false;
32 // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
33 } else if (!wallet_dir.is_absolute()) {
34 chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
35 return false;
36 }
37 gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
38 }
39
40 LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
41
42 chain.initMessage(_("Verifying wallet(s)...").translated);
43
44 // For backwards compatibility if an unnamed top level wallet exists in the
45 // wallets directory, include it in the default list of wallets to load.
46 if (!gArgs.IsArgSet("wallet")) {
47 DatabaseOptions options;
48 DatabaseStatus status;
49 bilingual_str error_string;
50 options.require_existing = true;
51 options.verify = false;
52 if (MakeWalletDatabase("", options, status, error_string)) {
53 gArgs.LockSettings([&](util::Settings& settings) {
54 util::SettingsValue wallets(util::SettingsValue::VARR);
55 wallets.push_back(""); // Default wallet name is ""
56 settings.rw_settings["wallet"] = wallets;
57 });
58 }
59 }
60
61 // Keep track of each wallet absolute path to detect duplicates.
62 std::set<fs::path> wallet_paths;
63
64 for (const auto& wallet_file : gArgs.GetArgs("-wallet")) {
65 const fs::path path = fs::absolute(wallet_file, GetWalletDir());
66
67 if (!wallet_paths.insert(path).second) {
68 chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
69 continue;
70 }
71
72 DatabaseOptions options;
73 DatabaseStatus status;
74 options.require_existing = true;
75 options.verify = true;
76 bilingual_str error_string;
77 if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
78 if (status == DatabaseStatus::FAILED_NOT_FOUND) {
79 chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s\n", error_string.original)));
80 } else {
81 chain.initError(error_string);
82 return false;
83 }
84 }
85 }
86
87 return true;
88 }
89
LoadWallets(interfaces::Chain & chain)90 bool LoadWallets(interfaces::Chain& chain)
91 {
92 try {
93 std::set<fs::path> wallet_paths;
94 for (const std::string& name : gArgs.GetArgs("-wallet")) {
95 if (!wallet_paths.insert(name).second) {
96 continue;
97 }
98 DatabaseOptions options;
99 DatabaseStatus status;
100 options.require_existing = true;
101 options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
102 bilingual_str error;
103 std::vector<bilingual_str> warnings;
104 std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
105 if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
106 continue;
107 }
108 std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
109 if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
110 if (!pwallet) {
111 chain.initError(error);
112 return false;
113 }
114 AddWallet(pwallet);
115 }
116 return true;
117 } catch (const std::runtime_error& e) {
118 chain.initError(Untranslated(e.what()));
119 return false;
120 }
121 }
122
StartWallets(CScheduler & scheduler,const ArgsManager & args)123 void StartWallets(CScheduler& scheduler, const ArgsManager& args)
124 {
125 for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
126 pwallet->postInitProcess();
127 }
128
129 // Schedule periodic wallet flushes and tx rebroadcasts
130 if (args.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
131 scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
132 }
133 scheduler.scheduleEvery(MaybeResendWalletTxs, std::chrono::milliseconds{1000});
134 }
135
FlushWallets()136 void FlushWallets()
137 {
138 for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
139 pwallet->Flush();
140 }
141 }
142
StopWallets()143 void StopWallets()
144 {
145 for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
146 pwallet->Close();
147 }
148 }
149
UnloadWallets()150 void UnloadWallets()
151 {
152 auto wallets = GetWallets();
153 while (!wallets.empty()) {
154 auto wallet = wallets.back();
155 wallets.pop_back();
156 std::vector<bilingual_str> warnings;
157 RemoveWallet(wallet, nullopt, warnings);
158 UnloadWallet(std::move(wallet));
159 }
160 }
161