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 : : using util::Join;
25 : :
26 : : namespace wallet {
27 : 406 : bool VerifyWallets(WalletContext& context)
28 : : {
29 : 406 : interfaces::Chain& chain = *context.chain;
30 : 406 : ArgsManager& args = *Assert(context.args);
31 : :
32 [ + - + + ]: 406 : if (args.IsArgSet("-walletdir")) {
33 [ + - + - ]: 48 : const fs::path wallet_dir{args.GetPathArg("-walletdir")};
34 [ + - ]: 24 : std::error_code error;
35 : : // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
36 : : // It also lets the fs::exists and fs::is_directory checks below pass on windows, since they return false
37 : : // if a path has trailing slashes, and it strips trailing slashes.
38 [ + - ]: 48 : fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
39 [ + + - + ]: 43 : if (error || !fs::exists(canonical_wallet_dir)) {
40 [ - + + - : 15 : chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
+ - ]
41 : 5 : return false;
42 [ + - + + ]: 19 : } else if (!fs::is_directory(canonical_wallet_dir)) {
43 [ - + + - : 15 : chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
+ - ]
44 : 5 : return false;
45 : : // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
46 [ + + ]: 14 : } else if (!wallet_dir.is_absolute()) {
47 [ - + + - : 9 : chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
+ - ]
48 : 3 : return false;
49 : : }
50 [ - + + - : 33 : args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
+ - ]
51 : 48 : }
52 : :
53 [ - + + - ]: 1179 : LogInfo("Using wallet directory %s", fs::PathToString(GetWalletDir()));
54 : : // Print general DB information
55 : 393 : LogDBInfo();
56 : :
57 [ + - ]: 393 : chain.initMessage(_("Verifying wallet(s)…"));
58 : :
59 : : // For backwards compatibility if an unnamed top level wallet exists in the
60 : : // wallets directory, include it in the default list of wallets to load.
61 [ + - + + ]: 393 : if (!args.IsArgSet("wallet")) {
62 [ + - ]: 271 : DatabaseOptions options;
63 : 271 : DatabaseStatus status;
64 [ + - ]: 271 : ReadDatabaseArgs(args, options);
65 [ + - ]: 271 : bilingual_str error_string;
66 : 271 : options.require_existing = true;
67 : 271 : options.verify = false;
68 [ + - + - : 271 : if (MakeWalletDatabase("", options, status, error_string)) {
+ + ]
69 : 1 : common::SettingsValue wallets(common::SettingsValue::VARR);
70 [ + - + - ]: 1 : wallets.push_back(""); // Default wallet name is ""
71 : : // Pass write=false because no need to write file and probably
72 : : // better not to. If unnamed wallet needs to be added next startup
73 : : // and the setting is empty, this code will just run again.
74 [ + - + - ]: 2 : chain.overwriteRwSetting("wallet", std::move(wallets), interfaces::SettingsAction::SKIP_WRITE);
75 : 1 : }
76 : 271 : }
77 : :
78 : : // Keep track of each wallet absolute path to detect duplicates.
79 [ + - ]: 393 : std::set<fs::path> wallet_paths;
80 : :
81 [ + - + - : 497 : for (const auto& wallet : chain.getSettingsList("wallet")) {
+ + ]
82 [ + + ]: 117 : if (!wallet.isStr()) {
83 [ + - + - ]: 8 : chain.initError(_("Invalid value detected for '-wallet' or '-nowallet'. "
84 : : "'-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets"));
85 : 8 : return false;
86 : : }
87 [ + - ]: 109 : const auto& wallet_file = wallet.get_str();
88 [ + - + - : 218 : const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
+ - ]
89 : :
90 [ + - + + ]: 109 : if (!wallet_paths.insert(path).second) {
91 [ + - + - ]: 2 : chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
92 : 2 : continue;
93 : : }
94 : :
95 [ + - ]: 107 : DatabaseOptions options;
96 : 107 : DatabaseStatus status;
97 [ + - ]: 107 : ReadDatabaseArgs(args, options);
98 : 107 : options.require_existing = true;
99 : 107 : options.verify = true;
100 [ + - ]: 107 : bilingual_str error_string;
101 [ + - + + ]: 107 : if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
102 [ - + ]: 6 : if (status == DatabaseStatus::FAILED_NOT_FOUND) {
103 [ # # # # : 0 : chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
# # ]
104 [ + + ]: 6 : } else if (status == DatabaseStatus::FAILED_LEGACY_DISABLED) {
105 : : // Skipping legacy wallets as they will not be loaded.
106 : : // This will be properly communicated to the user during the loading process.
107 : 1 : continue;
108 : : } else {
109 [ + - ]: 5 : chain.initError(error_string);
110 : 5 : return false;
111 : : }
112 : : }
113 : 224 : }
114 : :
115 : 380 : return true;
116 : 393 : }
117 : :
118 : 350 : bool LoadWallets(WalletContext& context)
119 : : {
120 : 350 : interfaces::Chain& chain = *context.chain;
121 : 350 : try {
122 [ + - ]: 350 : std::set<fs::path> wallet_paths;
123 [ + - + - : 452 : for (const auto& wallet : chain.getSettingsList("wallet")) {
+ + ]
124 [ - + ]: 102 : if (!wallet.isStr()) {
125 [ # # # # ]: 0 : chain.initError(_("Invalid value detected for '-wallet' or '-nowallet'. "
126 : : "'-wallet' requires a string value, while '-nowallet' accepts only '1' to disable all wallets"));
127 : 0 : return false;
128 : : }
129 [ + - ]: 102 : const auto& name = wallet.get_str();
130 [ + - + - : 204 : if (!wallet_paths.insert(fs::PathFromString(name)).second) {
+ + ]
131 : 2 : continue;
132 : : }
133 [ + - ]: 100 : DatabaseOptions options;
134 : 100 : DatabaseStatus status;
135 [ + - ]: 100 : ReadDatabaseArgs(*context.args, options);
136 : 100 : options.require_existing = true;
137 : 100 : options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
138 [ + - ]: 100 : bilingual_str error;
139 : 100 : std::vector<bilingual_str> warnings;
140 [ + - ]: 100 : std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
141 [ + + ]: 100 : if (!database) {
142 [ - + ]: 1 : if (status == DatabaseStatus::FAILED_NOT_FOUND) continue;
143 [ + - ]: 1 : if (status == DatabaseStatus::FAILED_LEGACY_DISABLED) {
144 : : // Inform user that legacy wallet is not loaded and suggest upgrade options
145 [ + - ]: 1 : chain.initWarning(error);
146 : 1 : continue;
147 : : }
148 : : }
149 [ + - + - ]: 99 : chain.initMessage(_("Loading wallet…"));
150 [ + - + - ]: 99 : std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings) : nullptr;
151 [ + + + - : 101 : if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
+ - + - +
- ]
152 [ - + ]: 99 : if (!pwallet) {
153 [ # # ]: 0 : chain.initError(error);
154 [ # # ]: 0 : return false;
155 : : }
156 : :
157 [ + - ]: 99 : NotifyWalletLoaded(context, pwallet);
158 [ + - ]: 99 : AddWallet(context, pwallet);
159 : 200 : }
160 : 350 : return true;
161 [ - - ]: 350 : } catch (const std::runtime_error& e) {
162 [ - - - - : 0 : chain.initError(Untranslated(e.what()));
- - ]
163 : 0 : return false;
164 : 0 : }
165 : : }
166 : :
167 : 350 : void StartWallets(WalletContext& context)
168 : : {
169 [ + + ]: 449 : for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
170 [ + - ]: 99 : pwallet->postInitProcess();
171 : 350 : }
172 : :
173 [ + - ]: 482 : context.scheduler->scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
174 : 350 : }
175 : :
176 : 822 : void UnloadWallets(WalletContext& context)
177 : : {
178 : 822 : auto wallets = GetWallets(context);
179 [ + + ]: 2222 : while (!wallets.empty()) {
180 [ + - ]: 578 : auto wallet = wallets.back();
181 : 578 : wallets.pop_back();
182 : 578 : std::vector<bilingual_str> warnings;
183 [ + - ]: 578 : RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings);
184 [ + - ]: 578 : WaitForDeleteWallet(std::move(wallet));
185 [ - + ]: 578 : }
186 : 822 : }
187 : : } // namespace wallet
|