Branch data 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 : : #include <wallet/wallet.h>
18 : : #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 : : // 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 : 0 : 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 : 2 : }
99 : 0 : }
100 [ # # # ]: 0 : }
101 : :
102 : 0 : return true;
103 : 0 : }
104 : :
105 : 0 : bool LoadWallets(WalletContext& context)
106 : 2 : {
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)
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 : context.scheduler->scheduleEvery([&context] { MaybeCompactWalletDB(context); }, 500ms);
153 : 0 : }
154 [ # # ][ # # ]: 0 : context.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
|