2021-11-16 16:19:47 +01:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2023-08-16 19:27:31 +02:00
|
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
2021-11-16 16:19:47 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
|
|
|
#include <wallet/load.h>
|
|
|
|
|
|
|
|
#include <coinjoin/client.h>
|
|
|
|
#include <coinjoin/options.h>
|
2022-10-17 04:33:10 +02:00
|
|
|
#include <fs.h>
|
|
|
|
#include <net.h>
|
2021-11-16 16:19:47 +01:00
|
|
|
#include <interfaces/chain.h>
|
|
|
|
#include <scheduler.h>
|
2022-03-24 21:07:40 +01:00
|
|
|
#include <util/string.h>
|
2021-11-16 16:19:47 +01:00
|
|
|
#include <util/system.h>
|
2022-03-24 05:13:51 +01:00
|
|
|
#include <util/translation.h>
|
2021-11-16 16:19:47 +01:00
|
|
|
#include <wallet/wallet.h>
|
2022-05-09 12:50:36 +02:00
|
|
|
#include <wallet/walletdb.h>
|
2021-11-16 16:19:47 +01:00
|
|
|
|
2022-06-21 15:30:55 +02:00
|
|
|
#include <univalue.h>
|
|
|
|
|
2024-08-06 19:40:56 +02:00
|
|
|
#include <system_error>
|
|
|
|
|
2022-11-30 20:23:31 +01:00
|
|
|
bool VerifyWallets(interfaces::Chain& chain)
|
2021-11-16 16:19:47 +01:00
|
|
|
{
|
|
|
|
if (gArgs.IsArgSet("-walletdir")) {
|
2024-08-06 19:39:26 +02:00
|
|
|
fs::path wallet_dir = fs::PathFromString(gArgs.GetArg("-walletdir", ""));
|
2024-08-06 19:40:56 +02:00
|
|
|
std::error_code error;
|
2021-11-16 16:19:47 +01:00
|
|
|
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
|
2024-08-06 19:40:56 +02:00
|
|
|
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
|
2021-11-16 16:19:47 +01:00
|
|
|
if (error || !fs::exists(wallet_dir)) {
|
2024-08-06 19:39:26 +02:00
|
|
|
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), fs::PathToString(wallet_dir)));
|
2021-11-16 16:19:47 +01:00
|
|
|
return false;
|
|
|
|
} else if (!fs::is_directory(wallet_dir)) {
|
2024-08-06 19:39:26 +02:00
|
|
|
chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), fs::PathToString(wallet_dir)));
|
2021-11-16 16:19:47 +01:00
|
|
|
return false;
|
|
|
|
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
|
|
|
|
} else if (!wallet_dir.is_absolute()) {
|
2024-08-06 19:39:26 +02:00
|
|
|
chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), fs::PathToString(wallet_dir)));
|
2021-11-16 16:19:47 +01:00
|
|
|
return false;
|
|
|
|
}
|
2024-08-06 19:39:26 +02:00
|
|
|
gArgs.ForceSetArg("-walletdir", fs::PathToString(canonical_wallet_dir));
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
|
2024-08-06 19:39:26 +02:00
|
|
|
LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir()));
|
2021-11-16 16:19:47 +01:00
|
|
|
|
2024-05-18 04:18:41 +02:00
|
|
|
chain.initMessage(_("Verifying wallet(s)…").translated);
|
2021-11-16 16:19:47 +01:00
|
|
|
|
2022-11-30 20:23:31 +01:00
|
|
|
// For backwards compatibility if an unnamed top level wallet exists in the
|
|
|
|
// wallets directory, include it in the default list of wallets to load.
|
|
|
|
if (!gArgs.IsArgSet("wallet")) {
|
|
|
|
DatabaseOptions options;
|
|
|
|
DatabaseStatus status;
|
|
|
|
bilingual_str error_string;
|
|
|
|
options.require_existing = true;
|
|
|
|
options.verify = false;
|
|
|
|
if (MakeWalletDatabase("", options, status, error_string)) {
|
|
|
|
gArgs.LockSettings([&](util::Settings& settings) {
|
|
|
|
util::SettingsValue wallets(util::SettingsValue::VARR);
|
|
|
|
wallets.push_back(""); // Default wallet name is ""
|
|
|
|
settings.rw_settings["wallet"] = wallets;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-16 16:19:47 +01:00
|
|
|
// Keep track of each wallet absolute path to detect duplicates.
|
|
|
|
std::set<fs::path> wallet_paths;
|
|
|
|
|
2022-11-30 20:23:31 +01:00
|
|
|
for (const auto& wallet_file : gArgs.GetArgs("-wallet")) {
|
2024-08-06 19:39:26 +02:00
|
|
|
const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
|
2021-11-16 16:19:47 +01:00
|
|
|
|
2022-10-17 04:33:10 +02:00
|
|
|
if (!wallet_paths.insert(path).second) {
|
2020-11-05 07:50:51 +01:00
|
|
|
chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
|
|
|
|
continue;
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
|
2022-10-17 04:33:10 +02:00
|
|
|
DatabaseOptions options;
|
|
|
|
DatabaseStatus status;
|
2022-11-30 20:24:02 +01:00
|
|
|
options.require_existing = true;
|
2022-10-17 04:33:10 +02:00
|
|
|
options.verify = true;
|
2022-04-07 06:43:16 +02:00
|
|
|
bilingual_str error_string;
|
2022-10-17 04:33:10 +02:00
|
|
|
if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
|
2022-11-30 20:24:02 +01:00
|
|
|
if (status == DatabaseStatus::FAILED_NOT_FOUND) {
|
2021-03-24 18:50:48 +01:00
|
|
|
chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
|
2022-11-30 20:24:02 +01:00
|
|
|
} else {
|
|
|
|
chain.initError(error_string);
|
|
|
|
return false;
|
|
|
|
}
|
2022-04-07 06:43:16 +02:00
|
|
|
}
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-07 16:09:56 +01:00
|
|
|
bool LoadWallets(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoin_loader)
|
2021-11-16 16:19:47 +01:00
|
|
|
{
|
2020-03-28 03:14:08 +01:00
|
|
|
try {
|
2020-11-05 07:50:51 +01:00
|
|
|
std::set<fs::path> wallet_paths;
|
2022-11-30 20:23:31 +01:00
|
|
|
for (const std::string& name : gArgs.GetArgs("-wallet")) {
|
2024-08-06 19:39:26 +02:00
|
|
|
if (!wallet_paths.insert(fs::PathFromString(name)).second) {
|
2020-11-05 07:50:51 +01:00
|
|
|
continue;
|
|
|
|
}
|
2022-10-17 04:33:10 +02:00
|
|
|
DatabaseOptions options;
|
|
|
|
DatabaseStatus status;
|
2022-11-30 20:24:02 +01:00
|
|
|
options.require_existing = true;
|
2022-10-17 04:33:10 +02:00
|
|
|
options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
|
2020-03-28 03:14:08 +01:00
|
|
|
bilingual_str error_string;
|
|
|
|
std::vector<bilingual_str> warnings;
|
2022-10-17 04:33:10 +02:00
|
|
|
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error_string);
|
2022-11-30 20:24:02 +01:00
|
|
|
if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-19 15:57:15 +02:00
|
|
|
chain.initMessage(_("Loading wallet...").translated);
|
2024-07-29 07:29:45 +02:00
|
|
|
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(&chain, &coinjoin_loader, name, std::move(database), options.create_flags, error_string, warnings) : nullptr;
|
2020-03-28 03:14:08 +01:00
|
|
|
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
|
|
|
|
if (!pwallet) {
|
|
|
|
chain.initError(error_string);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
AddWallet(pwallet);
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
2020-03-28 03:14:08 +01:00
|
|
|
return true;
|
|
|
|
} catch (const std::runtime_error& e) {
|
|
|
|
chain.initError(Untranslated(e.what()));
|
|
|
|
return false;
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-12 16:11:56 +02:00
|
|
|
void StartWallets(CScheduler& scheduler, const ArgsManager& args)
|
2021-11-16 16:19:47 +01:00
|
|
|
{
|
|
|
|
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
|
|
|
pwallet->postInitProcess();
|
|
|
|
}
|
|
|
|
|
2021-12-12 16:57:51 +01:00
|
|
|
// Schedule periodic wallet flushes and tx rebroadcasts
|
2022-06-12 16:11:56 +02:00
|
|
|
if (args.GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
|
2023-01-15 12:04:56 +01:00
|
|
|
scheduler.scheduleEvery(MaybeCompactWalletDB, std::chrono::milliseconds{500});
|
2022-05-09 12:50:36 +02:00
|
|
|
}
|
2023-01-15 12:04:56 +01:00
|
|
|
scheduler.scheduleEvery(MaybeResendWalletTxs, std::chrono::milliseconds{1000});
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FlushWallets()
|
|
|
|
{
|
|
|
|
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
|
|
|
if (CCoinJoinClientOptions::IsEnabled()) {
|
|
|
|
// Stop CoinJoin, release keys
|
2024-01-01 17:33:07 +01:00
|
|
|
pwallet->coinjoin_loader().FlushWallet(pwallet->GetName());
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
2022-03-04 09:26:11 +01:00
|
|
|
pwallet->Flush();
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StopWallets()
|
|
|
|
{
|
|
|
|
for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) {
|
2022-03-04 09:26:11 +01:00
|
|
|
pwallet->Close();
|
2021-11-16 16:19:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void UnloadWallets()
|
|
|
|
{
|
|
|
|
auto wallets = GetWallets();
|
|
|
|
while (!wallets.empty()) {
|
|
|
|
auto wallet = wallets.back();
|
|
|
|
wallets.pop_back();
|
2020-09-03 18:24:23 +02:00
|
|
|
std::vector<bilingual_str> warnings;
|
2022-10-15 22:11:49 +02:00
|
|
|
RemoveWallet(wallet, std::nullopt, warnings);
|
2021-11-16 16:19:47 +01:00
|
|
|
UnloadWallet(std::move(wallet));
|
|
|
|
}
|
|
|
|
}
|