mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-15 22:32:37 -03:00
470316c3bf
WalletInit::Start calls postInitProcess() for each wallet. Previously each call to postInitProcess() would attempt to schedule wallet background flushing. Just start wallet background flushing once from WalletInit::Start().
295 lines
14 KiB
C++
295 lines
14 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2017 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 <chainparams.h>
|
|
#include <init.h>
|
|
#include <net.h>
|
|
#include <scheduler.h>
|
|
#include <util.h>
|
|
#include <utilmoneystr.h>
|
|
#include <validation.h>
|
|
#include <walletinitinterface.h>
|
|
#include <wallet/rpcwallet.h>
|
|
#include <wallet/wallet.h>
|
|
#include <wallet/walletutil.h>
|
|
|
|
class WalletInit : public WalletInitInterface {
|
|
public:
|
|
|
|
//! Return the wallets help message.
|
|
void AddWalletOptions() const override;
|
|
|
|
//! Wallets parameter interaction
|
|
bool ParameterInteraction() const override;
|
|
|
|
//! Register wallet RPCs.
|
|
void RegisterRPC(CRPCTable &tableRPC) const override;
|
|
|
|
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
|
|
// This function will perform salvage on the wallet if requested, as long as only one wallet is
|
|
// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
|
bool Verify() const override;
|
|
|
|
//! Load wallet databases.
|
|
bool Open() const override;
|
|
|
|
//! Complete startup of wallets.
|
|
void Start(CScheduler& scheduler) const override;
|
|
|
|
//! Flush all wallets in preparation for shutdown.
|
|
void Flush() const override;
|
|
|
|
//! Stop all wallets. Wallets will be flushed first.
|
|
void Stop() const override;
|
|
|
|
//! Close all wallets.
|
|
void Close() const override;
|
|
};
|
|
|
|
const WalletInitInterface& g_wallet_init_interface = WalletInit();
|
|
|
|
void WalletInit::AddWalletOptions() const
|
|
{
|
|
gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). "
|
|
"Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"),
|
|
CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
|
|
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
|
|
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
|
|
CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-rescan", _("Rescan the block chain for missing wallet transactions on startup"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-upgradewallet", _("Upgrade wallet to latest format on startup"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET);
|
|
gArgs.AddArg("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +
|
|
" " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"), false, OptionsCategory::WALLET);
|
|
|
|
gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST);
|
|
gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST);
|
|
gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST);
|
|
gArgs.AddArg("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST);
|
|
}
|
|
|
|
bool WalletInit::ParameterInteraction() const
|
|
{
|
|
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
|
for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
|
|
LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
gArgs.SoftSetArg("-wallet", "");
|
|
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
|
|
|
|
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
|
|
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
|
|
}
|
|
|
|
if (gArgs.GetBoolArg("-salvagewallet", false)) {
|
|
if (is_multiwallet) {
|
|
return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet"));
|
|
}
|
|
// Rewrite just private keys: rescan to find transactions
|
|
if (gArgs.SoftSetBoolArg("-rescan", true)) {
|
|
LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__);
|
|
}
|
|
}
|
|
|
|
bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false);
|
|
// -zapwallettxes implies dropping the mempool on startup
|
|
if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) {
|
|
LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__);
|
|
}
|
|
|
|
// -zapwallettxes implies a rescan
|
|
if (zapwallettxes) {
|
|
if (is_multiwallet) {
|
|
return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes"));
|
|
}
|
|
if (gArgs.SoftSetBoolArg("-rescan", true)) {
|
|
LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__);
|
|
}
|
|
}
|
|
|
|
if (is_multiwallet) {
|
|
if (gArgs.GetBoolArg("-upgradewallet", false)) {
|
|
return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet"));
|
|
}
|
|
}
|
|
|
|
if (gArgs.GetBoolArg("-sysperms", false))
|
|
return InitError("-sysperms is not allowed in combination with enabled wallet functionality");
|
|
if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false))
|
|
return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
|
|
|
|
if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
|
|
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
|
|
_("The wallet will avoid paying less than the minimum relay fee."));
|
|
|
|
if (gArgs.IsArgSet("-maxtxfee"))
|
|
{
|
|
CAmount nMaxFee = 0;
|
|
if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee))
|
|
return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")));
|
|
if (nMaxFee > HIGH_MAX_TX_FEE)
|
|
InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
|
|
maxTxFee = nMaxFee;
|
|
if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
|
|
{
|
|
return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
|
|
gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WalletInit::RegisterRPC(CRPCTable &t) const
|
|
{
|
|
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
|
return;
|
|
}
|
|
|
|
RegisterWalletRPCCommands(t);
|
|
}
|
|
|
|
bool WalletInit::Verify() const
|
|
{
|
|
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
|
return true;
|
|
}
|
|
|
|
if (gArgs.IsArgSet("-walletdir")) {
|
|
fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
|
|
if (!fs::exists(wallet_dir)) {
|
|
return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
|
|
} else if (!fs::is_directory(wallet_dir)) {
|
|
return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
|
|
} else if (!wallet_dir.is_absolute()) {
|
|
return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
|
|
}
|
|
}
|
|
|
|
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
|
|
|
|
uiInterface.InitMessage(_("Verifying wallet(s)..."));
|
|
|
|
// Keep track of each wallet absolute path to detect duplicates.
|
|
std::set<fs::path> wallet_paths;
|
|
|
|
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
|
// Do some checking on wallet path. It should be either a:
|
|
//
|
|
// 1. Path where a directory can be created.
|
|
// 2. Path to an existing directory.
|
|
// 3. Path to a symlink to a directory.
|
|
// 4. For backwards compatibility, the name of a data file in -walletdir.
|
|
fs::path wallet_path = fs::absolute(walletFile, GetWalletDir());
|
|
fs::file_type path_type = fs::symlink_status(wallet_path).type();
|
|
if (!(path_type == fs::file_not_found || path_type == fs::directory_file ||
|
|
(path_type == fs::symlink_file && fs::is_directory(wallet_path)) ||
|
|
(path_type == fs::regular_file && fs::path(walletFile).filename() == walletFile))) {
|
|
return InitError(strprintf(
|
|
_("Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
|
|
"database/log.?????????? files can be stored, a location where such a directory could be created, "
|
|
"or (for backwards compatibility) the name of an existing data file in -walletdir (%s)"),
|
|
walletFile, GetWalletDir()));
|
|
}
|
|
|
|
if (!wallet_paths.insert(wallet_path).second) {
|
|
return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile));
|
|
}
|
|
|
|
std::string strError;
|
|
if (!WalletBatch::VerifyEnvironment(wallet_path, strError)) {
|
|
return InitError(strError);
|
|
}
|
|
|
|
if (gArgs.GetBoolArg("-salvagewallet", false)) {
|
|
// Recover readable keypairs:
|
|
CWallet dummyWallet("dummy", WalletDatabase::CreateDummy());
|
|
std::string backup_filename;
|
|
if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string strWarning;
|
|
bool dbV = WalletBatch::VerifyDatabaseFile(wallet_path, strWarning, strError);
|
|
if (!strWarning.empty()) {
|
|
InitWarning(strWarning);
|
|
}
|
|
if (!dbV) {
|
|
InitError(strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WalletInit::Open() const
|
|
{
|
|
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
|
LogPrintf("Wallet disabled!\n");
|
|
return true;
|
|
}
|
|
|
|
for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
|
|
CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
|
|
if (!pwallet) {
|
|
return false;
|
|
}
|
|
AddWallet(pwallet);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WalletInit::Start(CScheduler& scheduler) const
|
|
{
|
|
for (CWallet* pwallet : GetWallets()) {
|
|
pwallet->postInitProcess();
|
|
}
|
|
|
|
// Run a thread to flush wallet periodically
|
|
scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
|
|
}
|
|
|
|
void WalletInit::Flush() const
|
|
{
|
|
for (CWallet* pwallet : GetWallets()) {
|
|
pwallet->Flush(false);
|
|
}
|
|
}
|
|
|
|
void WalletInit::Stop() const
|
|
{
|
|
for (CWallet* pwallet : GetWallets()) {
|
|
pwallet->Flush(true);
|
|
}
|
|
}
|
|
|
|
void WalletInit::Close() const
|
|
{
|
|
for (CWallet* pwallet : GetWallets()) {
|
|
RemoveWallet(pwallet);
|
|
delete pwallet;
|
|
}
|
|
}
|