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