Bitcoin Core  31.0.0
P2P Digital Currency
load.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present 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 {
28 {
29  interfaces::Chain& chain = *context.chain;
30  ArgsManager& args = *Assert(context.args);
31 
32  if (args.IsArgSet("-walletdir")) {
33  const fs::path wallet_dir{args.GetPathArg("-walletdir")};
34  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  fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
39  if (error || !fs::exists(canonical_wallet_dir)) {
40  chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
41  return false;
42  } else if (!fs::is_directory(canonical_wallet_dir)) {
43  chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
44  return false;
45  // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
46  } else if (!wallet_dir.is_absolute()) {
47  chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
48  return false;
49  }
50  args.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
51  }
52 
53  LogInfo("Using wallet directory %s", fs::PathToString(GetWalletDir()));
54  // Print general DB information
55  LogDBInfo();
56 
57  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  if (!args.IsArgSet("wallet")) {
62  DatabaseOptions options;
63  DatabaseStatus status;
64  ReadDatabaseArgs(args, options);
65  bilingual_str error_string;
66  options.require_existing = true;
67  options.verify = false;
68  if (MakeWalletDatabase("", options, status, error_string)) {
70  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  chain.overwriteRwSetting("wallet", std::move(wallets), interfaces::SettingsAction::SKIP_WRITE);
75  }
76  }
77 
78  // Keep track of each wallet absolute path to detect duplicates.
79  std::set<fs::path> wallet_paths;
80 
81  for (const auto& wallet : chain.getSettingsList("wallet")) {
82  if (!wallet.isStr()) {
83  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  return false;
86  }
87  const auto& wallet_file = wallet.get_str();
88  const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
89 
90  if (!wallet_paths.insert(path).second) {
91  chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
92  continue;
93  }
94 
95  DatabaseOptions options;
96  DatabaseStatus status;
97  ReadDatabaseArgs(args, options);
98  options.require_existing = true;
99  options.verify = true;
100  bilingual_str error_string;
101  if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
102  if (status == DatabaseStatus::FAILED_NOT_FOUND) {
103  chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
104  } 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  continue;
108  } else {
109  chain.initError(error_string);
110  return false;
111  }
112  }
113  }
114 
115  return true;
116 }
117 
119 {
120  interfaces::Chain& chain = *context.chain;
121  try {
122  std::set<fs::path> wallet_paths;
123  for (const auto& wallet : chain.getSettingsList("wallet")) {
124  if (!wallet.isStr()) {
125  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  return false;
128  }
129  const auto& name = wallet.get_str();
130  if (!wallet_paths.insert(fs::PathFromString(name)).second) {
131  continue;
132  }
133  DatabaseOptions options;
134  DatabaseStatus status;
135  ReadDatabaseArgs(*context.args, options);
136  options.require_existing = true;
137  options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
138  bilingual_str error;
139  std::vector<bilingual_str> warnings;
140  std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
141  if (!database) {
142  if (status == DatabaseStatus::FAILED_NOT_FOUND) continue;
144  // Inform user that legacy wallet is not loaded and suggest upgrade options
145  chain.initWarning(error);
146  continue;
147  }
148  }
149  chain.initMessage(_("Loading wallet…"));
150  std::shared_ptr<CWallet> pwallet = database ? CWallet::LoadExisting(context, name, std::move(database), error, warnings) : nullptr;
151  if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
152  if (!pwallet) {
153  chain.initError(error);
154  return false;
155  }
156 
157  NotifyWalletLoaded(context, pwallet);
158  AddWallet(context, pwallet);
159  }
160  return true;
161  } catch (const std::runtime_error& e) {
162  chain.initError(Untranslated(e.what()));
163  return false;
164  }
165 }
166 
168 {
169  for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
170  pwallet->postInitProcess();
171  }
172 
173  context.scheduler->scheduleEvery([&context] { MaybeResendWalletTxs(context); }, 1min);
174 }
175 
177 {
178  auto wallets = GetWallets(context);
179  while (!wallets.empty()) {
180  auto wallet = wallets.back();
181  wallets.pop_back();
182  std::vector<bilingual_str> warnings;
183  RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt, warnings);
184  WaitForDeleteWallet(std::move(wallet));
185  }
186 }
187 } // namespace wallet
fs::path GetPathArg(std::string arg, const fs::path &default_value={}) const
Return path argument or default value.
Definition: args.cpp:276
void push_back(UniValue val)
Definition: univalue.cpp:103
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: args.cpp:375
void ReadDatabaseArgs(const ArgsManager &args, DatabaseOptions &options)
Definition: db.cpp:153
fs::path AbsPathJoin(const fs::path &base, const fs::path &path)
Helper function for joining two paths.
Definition: fs.cpp:34
static std::shared_ptr< CWallet > LoadExisting(WalletContext &context, const std::string &name, std::unique_ptr< WalletDatabase > database, bilingual_str &error, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:3127
Bilingual messages:
Definition: translation.h:24
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
virtual std::vector< common::SettingsValue > getSettingsList(const std::string &arg)=0
Get list of settings values.
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:82
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: args.cpp:571
fs::path GetWalletDir()
Get the path of the wallet directory.
Definition: walletutil.cpp:13
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
CScheduler * scheduler
Definition: context.h:38
bool LoadWallets(WalletContext &context)
Load wallet databases.
Definition: load.cpp:118
void MaybeResendWalletTxs(WalletContext &context)
Called periodically by the schedule thread.
Definition: wallet.cpp:2142
void StartWallets(WalletContext &context)
Complete startup of wallets.
Definition: load.cpp:167
bool VerifyWallets(WalletContext &context)
Responsible for reading and validating the -wallet arguments and verifying the wallet database...
Definition: load.cpp:27
static bool exists(const path &p)
Definition: fs.h:95
void WaitForDeleteWallet(std::shared_ptr< CWallet > &&wallet)
Explicitly delete the wallet.
Definition: wallet.cpp:254
ArgsManager & args
Definition: bitcoind.cpp:277
const char * name
Definition: rest.cpp:48
#define LogInfo(...)
Definition: log.h:95
virtual bool overwriteRwSetting(const std::string &name, common::SettingsValue value, SettingsAction action=SettingsAction::WRITE)=0
Replace a setting in <datadir>/settings.json with a new value.
void NotifyWalletLoaded(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
Definition: wallet.cpp:223
virtual void initWarning(const bilingual_str &message)=0
Send init warning.
bool RemoveWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet, std::optional< bool > load_on_start, std::vector< bilingual_str > &warnings)
Definition: wallet.cpp:163
DatabaseStatus
Definition: db.h:186
std::vector< std::shared_ptr< CWallet > > GetWallets(WalletContext &context)
Definition: wallet.cpp:194
std::string original
Definition: translation.h:25
Interface giving clients (wallet processes, maybe other analysis tools in the future) ability to acce...
Definition: chain.h:117
virtual void initMessage(const std::string &message)=0
Send init message.
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:157
WalletContext struct containing references to state shared between CWallet instances, like the reference to the chain interface, and the list of opened wallets.
Definition: context.h:36
bool require_existing
Definition: db.h:173
interfaces::Chain * chain
Definition: context.h:37
void UnloadWallets(WalletContext &context)
Definition: load.cpp:176
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:180
void LogDBInfo()
Definition: walletdb.cpp:66
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
bool verify
Check data integrity on load.
Definition: db.h:180
ArgsManager * args
Definition: context.h:39
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:205
virtual void initError(const bilingual_str &message)=0
Send init error.
bool AddWallet(WalletContext &context, const std::shared_ptr< CWallet > &wallet)
Definition: wallet.cpp:151
void scheduleEvery(Function f, std::chrono::milliseconds delta) EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Repeat f until the scheduler is stopped.
Definition: scheduler.cpp:108
std::unique_ptr< WalletDatabase > MakeWalletDatabase(const std::string &name, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error_string)
Definition: wallet.cpp:2940
#define Assert(val)
Identity function.
Definition: check.h:113