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 = fsbridge::AbsPathJoin(GetWalletDir(), wallet_file);
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", 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             chain.initMessage(_("Loading wallet…").translated);
109             std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(&chain, name, std::move(database), options.create_flags, error, warnings) : nullptr;
110             if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
111             if (!pwallet) {
112                 chain.initError(error);
113                 return false;
114             }
115             AddWallet(pwallet);
116         }
117         return true;
118     } catch (const std::runtime_error& e) {
119         chain.initError(Untranslated(e.what()));
120         return false;
121     }
122 }
123 
StartWallets(CScheduler & scheduler,const ArgsManager & args)124 void StartWallets(CScheduler& scheduler, const ArgsManager& args)
125 {
126     for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
127         pwallet->postInitProcess();
128     }
129 
130     // Schedule periodic wallet flushes and tx rebroadcasts
131     if (args.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
132         scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
133     }
134     scheduler.scheduleEvery(MaybeResendWalletTxs, std::chrono::milliseconds{1000});
135 }
136 
FlushWallets()137 void FlushWallets()
138 {
139     for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
140         pwallet->Flush();
141     }
142 }
143 
StopWallets()144 void StopWallets()
145 {
146     for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
147         pwallet->Close();
148     }
149 }
150 
UnloadWallets()151 void UnloadWallets()
152 {
153     auto wallets = GetWallets();
154     while (!wallets.empty()) {
155         auto wallet = wallets.back();
156         wallets.pop_back();
157         std::vector<bilingual_str> warnings;
158         RemoveWallet(wallet, std::nullopt, warnings);
159         UnloadWallet(std::move(wallet));
160     }
161 }
162