49ee2a0ad88e0e656234b769d806987784ff1e28 Avoid wallet code writing node settings file (Russell Yanofsky)
Pull request description:
Change wallet loading code to access settings through the Chain interface instead of writing settings.json directly. This is for running wallet and node in separate processes, since multiprocess code wouldn't easily work with different processes updating the same file.
---
This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/projects/10). The commit was first part of larger PR #10102.
ACKs for top commit:
jamesob:
ACK 49ee2a0ad88e0e656234b769d806987784ff1e28 ([`jamesob/ackr/22217.1.ryanofsky.refactor_avoid_wallet_co`](https://github.com/jamesob/bitcoin/tree/ackr/22217.1.ryanofsky.refactor_avoid_wallet_co))
ryanofsky:
> ACK [49ee2a0](49ee2a0ad8) ([`jamesob/ackr/22217.1.ryanofsky.refactor_avoid_wallet_co`](https://github.com/jamesob/bitcoin/tree/ackr/22217.1.ryanofsky.refactor_avoid_wallet_co))
Zero-1729:
crACK 49ee2a0ad88e0e656234b769d806987784ff1e28
meshcollider:
Code review ACK 49ee2a0ad88e0e656234b769d806987784ff1e28
Tree-SHA512: a81c63b87816f739e02e3992808f314294d6c7213babaafdaaf3c4650ebc97ee4f98f9a4684ce4ff87372df59989b8ad5929159c5686293a7cce04e97e2fabba
168 lines
6.4 KiB
C++
168 lines
6.4 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
// 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 <fs.h>
|
|
#include <interfaces/chain.h>
|
|
#include <scheduler.h>
|
|
#include <util/string.h>
|
|
#include <util/system.h>
|
|
#include <util/translation.h>
|
|
#include <wallet/context.h>
|
|
#include <wallet/wallet.h>
|
|
#include <wallet/walletdb.h>
|
|
|
|
#include <univalue.h>
|
|
|
|
bool VerifyWallets(WalletContext& context)
|
|
{
|
|
interfaces::Chain& chain = *context.chain;
|
|
if (gArgs.IsArgSet("-walletdir")) {
|
|
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
|
|
boost::system::error_code error;
|
|
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
|
|
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
|
|
if (error || !fs::exists(wallet_dir)) {
|
|
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
|
|
return false;
|
|
} else if (!fs::is_directory(wallet_dir)) {
|
|
chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
|
|
return false;
|
|
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
|
|
} else if (!wallet_dir.is_absolute()) {
|
|
chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
|
|
return false;
|
|
}
|
|
gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
|
|
}
|
|
|
|
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
|
|
|
|
chain.initMessage(_("Verifying wallet(s)…").translated);
|
|
|
|
// 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)) {
|
|
util::SettingsValue wallets(util::SettingsValue::VARR);
|
|
wallets.push_back(""); // Default wallet name is ""
|
|
// Pass write=false because no need to write file and probably
|
|
// better not to. If unnamed wallet needs to be added next startup
|
|
// and the setting is empty, this code will just run again.
|
|
chain.updateRwSetting("wallet", wallets, /* write= */ false);
|
|
}
|
|
}
|
|
|
|
// Keep track of each wallet absolute path to detect duplicates.
|
|
std::set<fs::path> wallet_paths;
|
|
|
|
for (const auto& wallet : chain.getSettingsList("wallet")) {
|
|
const auto& wallet_file = wallet.get_str();
|
|
const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), wallet_file);
|
|
|
|
if (!wallet_paths.insert(path).second) {
|
|
chain.initWarning(strprintf(_("Ignoring duplicate -wallet %s."), wallet_file));
|
|
continue;
|
|
}
|
|
|
|
DatabaseOptions options;
|
|
DatabaseStatus status;
|
|
options.require_existing = true;
|
|
options.verify = true;
|
|
bilingual_str error_string;
|
|
if (!MakeWalletDatabase(wallet_file, options, status, error_string)) {
|
|
if (status == DatabaseStatus::FAILED_NOT_FOUND) {
|
|
chain.initWarning(Untranslated(strprintf("Skipping -wallet path that doesn't exist. %s", error_string.original)));
|
|
} else {
|
|
chain.initError(error_string);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LoadWallets(WalletContext& context)
|
|
{
|
|
interfaces::Chain& chain = *context.chain;
|
|
try {
|
|
std::set<fs::path> wallet_paths;
|
|
for (const auto& wallet : chain.getSettingsList("wallet")) {
|
|
const auto& name = wallet.get_str();
|
|
if (!wallet_paths.insert(name).second) {
|
|
continue;
|
|
}
|
|
DatabaseOptions options;
|
|
DatabaseStatus status;
|
|
options.require_existing = true;
|
|
options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
|
|
bilingual_str error;
|
|
std::vector<bilingual_str> warnings;
|
|
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
|
|
if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) {
|
|
continue;
|
|
}
|
|
chain.initMessage(_("Loading wallet…").translated);
|
|
std::shared_ptr<CWallet> pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings) : nullptr;
|
|
if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n")));
|
|
if (!pwallet) {
|
|
chain.initError(error);
|
|
return false;
|
|
}
|
|
AddWallet(context, pwallet);
|
|
}
|
|
return true;
|
|
} catch (const std::runtime_error& e) {
|
|
chain.initError(Untranslated(e.what()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void StartWallets(WalletContext& context, CScheduler& scheduler)
|
|
{
|
|
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
|
|
pwallet->postInitProcess();
|
|
}
|
|
|
|
// Schedule periodic wallet flushes and tx rebroadcasts
|
|
if (context.args->GetBoolArg("-flushwallet", DEFAULT_FLUSHWALLET)) {
|
|
scheduler.scheduleEvery([&context] { MaybeCompactWalletDB(context); }, std::chrono::milliseconds{500});
|
|
}
|
|
scheduler.scheduleEvery([&context] { MaybeResendWalletTxs(context); }, std::chrono::milliseconds{1000});
|
|
}
|
|
|
|
void FlushWallets(WalletContext& context)
|
|
{
|
|
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
|
|
pwallet->Flush();
|
|
}
|
|
}
|
|
|
|
void StopWallets(WalletContext& context)
|
|
{
|
|
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
|
|
pwallet->Close();
|
|
}
|
|
}
|
|
|
|
void UnloadWallets(WalletContext& context)
|
|
{
|
|
auto wallets = GetWallets(context);
|
|
while (!wallets.empty()) {
|
|
auto wallet = wallets.back();
|
|
wallets.pop_back();
|
|
std::vector<bilingual_str> warnings;
|
|
RemoveWallet(context, wallet, /* load_on_startup= */ std::nullopt, warnings);
|
|
UnloadWallet(std::move(wallet));
|
|
}
|
|
}
|