diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -186,6 +186,8 @@ wallet/db.h \ wallet/finaltx.h \ wallet/rpcdump.h \ + wallet/fees.h \ + wallet/init.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ @@ -268,6 +270,8 @@ wallet/crypter.cpp \ wallet/db.cpp \ wallet/finaltx.cpp \ + wallet/fees.cpp \ + wallet/init.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -43,6 +43,7 @@ #include "validation.h" #include "validationinterface.h" #ifdef ENABLE_WALLET +#include "wallet/init.h" #include "wallet/rpcdump.h" #include "wallet/wallet.h" #endif @@ -599,7 +600,7 @@ DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET - strUsage += CWallet::GetWalletHelpString(showDebug); + strUsage += GetWalletHelpString(showDebug); #endif #if ENABLE_ZMQ @@ -1607,7 +1608,7 @@ return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } - // High fee check is done afterward in CWallet::ParameterInteraction() + // High fee check is done afterward in WalletParameterInteraction() config.SetMinFeePerKB(CFeeRate(n)); } else { config.SetMinFeePerKB(CFeeRate(DEFAULT_MIN_RELAY_TX_FEE)); @@ -1646,7 +1647,7 @@ nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); #ifdef ENABLE_WALLET - if (!CWallet::ParameterInteraction()) { + if (!WalletParameterInteraction()) { return false; } #endif @@ -1803,7 +1804,7 @@ // Step 5: verify wallet database integrity #ifdef ENABLE_WALLET - if (!CWallet::Verify(chainparams)) { + if (!WalletVerify(chainparams)) { return false; } #endif @@ -2160,7 +2161,7 @@ // Step 8: load wallet #ifdef ENABLE_WALLET - if (!CWallet::InitLoadWallet(chainparams)) { + if (!InitLoadWallet(chainparams)) { return false; } #else diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -18,6 +18,7 @@ #include "policy/policy.h" #include "validation.h" // For mempool #include "wallet/coincontrol.h" +#include "wallet/fees.h" #include "wallet/wallet.h" #include @@ -549,7 +550,7 @@ } // Fee - nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool); + nPayFee = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); if (nPayFee > Amount::zero() && coinControl->nMinimumTotalFee > nPayFee) { nPayFee = coinControl->nMinimumTotalFee; @@ -645,12 +646,11 @@ // how many satoshis the estimated fee can vary per byte we guess wrong double dFeeVary; if (payTxFee.GetFeePerK() > Amount::zero()) { - dFeeVary = - std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / - (1000 * SATOSHI); + dFeeVary = std::max(GetRequiredFee(1000), payTxFee.GetFeePerK()) / + (1000 * SATOSHI); } else { dFeeVary = - std::max(CWallet::GetRequiredFee(1000), + std::max(GetRequiredFee(1000), mempool.estimateSmartFee(nTxConfirmTarget).GetFeePerK()) / (1000 * SATOSHI); } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -21,6 +21,7 @@ #include "ui_interface.h" #include "validation.h" // mempool and minRelayTxFee #include "wallet/coincontrol.h" +#include "wallet/fees.h" #include "wallet/wallet.h" #include @@ -220,7 +221,7 @@ SLOT(updateGlobalFeeVariables())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); - ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000)); + ui->customFee->setSingleStep(GetRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -636,7 +637,7 @@ void SendCoinsDialog::setMinimumFee() { ui->radioCustomPerKilobyte->setChecked(true); - ui->customFee->setValue(CWallet::GetRequiredFee(1000)); + ui->customFee->setValue(GetRequiredFee(1000)); } void SendCoinsDialog::updateFeeSectionControls() { @@ -709,7 +710,7 @@ tr("Pay only the required fee of %1") .arg(BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), - CWallet::GetRequiredFee(1000)) + + GetRequiredFee(1000)) + "/kB")); } @@ -727,7 +728,7 @@ BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), std::max(CWallet::fallbackFee.GetFeePerK(), - CWallet::GetRequiredFee(1000))) + + GetRequiredFee(1000))) + "/kB"); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelSmartFee2->show(); @@ -736,7 +737,7 @@ ui->labelSmartFee->setText( BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), - std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + + std::max(feeRate.GetFeePerK(), GetRequiredFee(1000))) + "/kB"); ui->labelSmartFee2->hide(); ui->labelFeeEstimation->setText( diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -12,7 +12,9 @@ add_library(wallet crypter.cpp db.cpp + fees.cpp finaltx.cpp + init.cpp rpcdump.cpp rpcwallet.cpp wallet.cpp diff --git a/src/wallet/fees.h b/src/wallet/fees.h new file mode 100644 --- /dev/null +++ b/src/wallet/fees.h @@ -0,0 +1,37 @@ +// 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. + +#ifndef BITCOIN_WALLET_FEES_H +#define BITCOIN_WALLET_FEES_H + +#include "amount.h" + +class CBlockPolicyEstimator; +class CCoinControl; +class CFeeRate; +class CTxMemPool; +struct FeeCalculation; + +/** + * Return the minimum required fee taking into account the + * floating relay fee and user set minimum transaction fee + */ +Amount GetRequiredFee(unsigned int nTxBytes); + +/** + * Estimate the minimum fee considering user set parameters + * and the required fee + */ +Amount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, + const CTxMemPool &pool); + +/** + * Estimate the minimum fee considering user set parameters + * and the required fee + */ +Amount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, + const CTxMemPool &pool, Amount targetFee); + +#endif // BITCOIN_WALLET_FEES_H diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp new file mode 100644 --- /dev/null +++ b/src/wallet/fees.cpp @@ -0,0 +1,52 @@ +// 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 "wallet/fees.h" + +#include "config.h" +#include "policy/policy.h" +#include "txmempool.h" +#include "util.h" +#include "validation.h" +#include "wallet/coincontrol.h" +#include "wallet/wallet.h" + +Amount GetRequiredFee(unsigned int nTxBytes) { + return std::max(CWallet::minTxFee.GetFee(nTxBytes), + GetConfig().GetMinFeePerKB().GetFee(nTxBytes)); +} + +Amount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, + const CTxMemPool &pool, Amount targetFee) { + Amount nFeeNeeded = targetFee; + // User didn't set: use -txconfirmtarget to estimate... + if (nFeeNeeded == Amount::zero()) { + int estimateFoundTarget = nConfirmTarget; + nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget) + .GetFee(nTxBytes); + // ... unless we don't have enough mempool data for estimatefee, then + // use fallbackFee. + if (nFeeNeeded == Amount::zero()) { + nFeeNeeded = CWallet::fallbackFee.GetFee(nTxBytes); + } + } + + // Prevent user from paying a fee below minRelayTxFee or minTxFee. + nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); + + // But always obey the maximum. + if (nFeeNeeded > maxTxFee) { + nFeeNeeded = maxTxFee; + } + + return nFeeNeeded; +} + +Amount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, + const CTxMemPool &pool) { + // payTxFee is the user-set global for desired feerate. + return GetMinimumFee(nTxBytes, nConfirmTarget, pool, + payTxFee.GetFee(nTxBytes)); +} \ No newline at end of file diff --git a/src/wallet/init.h b/src/wallet/init.h new file mode 100644 --- /dev/null +++ b/src/wallet/init.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef BITCOIN_WALLET_INIT_H +#define BITCOIN_WALLET_INIT_H + +#include "chainparams.h" + +#include + +//! Return the wallets help message. +std::string GetWalletHelpString(bool showDebug); + +//! Wallets parameter interaction +bool WalletParameterInteraction(); + +//! 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 (CWallet::ParameterInteraction forbids -salvagewallet, +// -zapwallettxes or -upgradewallet with multiwallet). +bool WalletVerify(const CChainParams &chainParams); + +//! Load wallet databases. +bool InitLoadWallet(const CChainParams &chainParams); + +#endif // BITCOIN_WALLET_INIT_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp new file mode 100644 --- /dev/null +++ b/src/wallet/init.cpp @@ -0,0 +1,360 @@ +// 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 "wallet/init.h" + +#include "config.h" +#include "net.h" +#include "util.h" +#include "utilmoneystr.h" +#include "validation.h" +#include "wallet/wallet.h" + +std::string GetWalletHelpString(bool showDebug) { + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt( + "-disablewallet", + _("Do not load the wallet and disable wallet RPC calls")); + strUsage += HelpMessageOpt( + "-keypool=", strprintf(_("Set key pool size to (default: %u)"), + DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt( + "-fallbackfee=", + 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))); + strUsage += HelpMessageOpt( + "-mintxfee=", + strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee " + "for transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); + strUsage += HelpMessageOpt( + "-paytxfee=", + strprintf( + _("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt( + "-rescan", + _("Rescan the block chain for missing wallet transactions on startup")); + strUsage += HelpMessageOpt( + "-salvagewallet", + _("Attempt to recover private keys from a corrupt wallet on startup")); + + strUsage += + HelpMessageOpt("-spendzeroconfchange", + strprintf(_("Spend unconfirmed change when sending " + "transactions (default: %d)"), + DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += + HelpMessageOpt("-txconfirmtarget=", + strprintf(_("If paytxfee is not set, include enough fee " + "so transactions begin confirmation on " + "average within n blocks (default: %u)"), + DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt( + "-usehd", + _("Use hierarchical deterministic key generation (HD) after BIP32. " + "Only has effect during wallet creation/first start") + + " " + strprintf(_("(default: %d)"), DEFAULT_USE_HD_WALLET)); + strUsage += HelpMessageOpt("-upgradewallet", + _("Upgrade wallet to latest format on startup")); + strUsage += + HelpMessageOpt("-wallet=", + _("Specify wallet file (within data directory)") + " " + + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt( + "-walletbroadcast", + _("Make the wallet broadcast transactions") + " " + + strprintf(_("(default: %d)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletnotify=", + _("Execute command when a wallet transaction " + "changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt( + "-zapwallettxes=", + _("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)")); + + if (showDebug) { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + + strUsage += HelpMessageOpt( + "-dblogsize=", + strprintf("Flush wallet database activity from memory to disk log " + "every megabytes (default: %u)", + DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt( + "-flushwallet", + strprintf("Run a thread to flush wallet periodically (default: %d)", + DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt( + "-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db " + "environment (default: %d)", + DEFAULT_WALLET_PRIVDB)); + strUsage += HelpMessageOpt( + "-walletrejectlongchains", + strprintf(_("Wallet will not create transactions that violate " + "mempool chain limits (default: %d)"), + DEFAULT_WALLET_REJECT_LONG_CHAINS)); + } + + return strUsage; +} + +bool WalletParameterInteraction() { + CFeeRate minRelayTxFee = GetConfig().GetMinFeePerKB(); + + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + return true; + } + + 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) && + gArgs.SoftSetBoolArg("-rescan", true)) { + if (is_multiwallet) { + return InitError( + strprintf("%s is only allowed with a single wallet file", + "-salvagewallet")); + } + // Rewrite just private keys: rescan to find transactions + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting " + "-rescan=1\n", + __func__); + } + + int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + // -zapwallettxes implies dropping the mempool on startup + if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting " + "-persistmempool=0\n", + __func__, zapwallettxes); + } + + // -zapwallettxes implies a rescan + if (zapwallettxes != 0) { + 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=%s -> setting " + "-rescan=1\n", + __func__, zapwallettxes); + } + LogPrintf("%s: parameter interaction: -zapwallettxes= -> 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("-mintxfee")) { + Amount n = Amount::zero(); + auto parsed = ParseMoney(gArgs.GetArg("-mintxfee", ""), n); + if (!parsed || Amount::zero() == n) { + return InitError( + AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); + } + + if (n > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on " + "every transaction.")); + } + + CWallet::minTxFee = CFeeRate(n); + } + + if (gArgs.IsArgSet("-fallbackfee")) { + Amount nFeePerK = Amount::zero(); + if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { + return InitError( + strprintf(_("Invalid amount for -fallbackfee=: '%s'"), + gArgs.GetArg("-fallbackfee", ""))); + } + + if (nFeePerK > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee " + "estimates are not available.")); + } + + CWallet::fallbackFee = CFeeRate(nFeePerK); + } + + if (gArgs.IsArgSet("-paytxfee")) { + Amount nFeePerK = Amount::zero(); + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { + return InitError( + AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); + } + + if (nFeePerK > HIGH_TX_FEE_PER_KB) { + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you " + "send a transaction.")); + } + + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < minRelayTxFee) { + return InitError(strprintf( + _("Invalid amount for -paytxfee=: '%s' (must " + "be at least %s)"), + gArgs.GetArg("-paytxfee", ""), minRelayTxFee.ToString())); + } + } + + if (gArgs.IsArgSet("-maxtxfee")) { + Amount nMaxFee = Amount::zero(); + 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=: '%s' (must " + "be at least the minrelay fee of %s to prevent " + "stuck transactions)"), + gArgs.GetArg("-maxtxfee", ""), minRelayTxFee.ToString())); + } + } + + nTxConfirmTarget = + gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = + gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + + return true; +} + +bool WalletVerify(const CChainParams &chainParams) { + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + return true; + } + + uiInterface.InitMessage(_("Verifying wallet(s)...")); + + // Keep track of each wallet absolute path to detect duplicates. + std::set wallet_paths; + + for (const std::string &walletFile : gArgs.GetArgs("-wallet")) { + if (fs::path(walletFile).filename() != walletFile) { + return InitError( + strprintf(_("Error loading wallet %s. -wallet parameter must " + "only specify a filename (not a path)."), + walletFile)); + } + + if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return InitError(strprintf(_("Error loading wallet %s. Invalid " + "characters in -wallet filename."), + walletFile)); + } + + fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + + if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || + fs::is_symlink(wallet_path))) { + return InitError(strprintf(_("Error loading wallet %s. -wallet " + "filename must be a regular file."), + walletFile)); + } + + if (!wallet_paths.insert(wallet_path).second) { + return InitError(strprintf(_("Error loading wallet %s. Duplicate " + "-wallet filename specified."), + walletFile)); + } + + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), + strError)) { + return InitError(strError); + } + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + // Recover readable keypairs: + CWallet dummyWallet(chainParams); + std::string backup_filename; + if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, + CWalletDB::RecoverKeysOnlyFilter, + backup_filename)) { + return false; + } + } + + std::string strWarning; + bool dbV = CWalletDB::VerifyDatabaseFile( + walletFile, GetDataDir().string(), strWarning, strError); + if (!strWarning.empty()) { + InitWarning(strWarning); + } + if (!dbV) { + InitError(strError); + return false; + } + } + + return true; +} + +bool InitLoadWallet(const CChainParams &chainParams) { + 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(chainParams, walletFile); + if (!pwallet) { + return false; + } + vpwallets.push_back(pwallet); + } + + return true; +} diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -7,6 +7,7 @@ class CRPCTable; class JSONRPCRequest; +class CWallet; void RegisterWalletRPCCommands(CRPCTable &t); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "../init.h" #include "amount.h" #include "chain.h" #include "chainparams.h" // for GetConsensus. @@ -10,7 +11,6 @@ #include "consensus/validation.h" #include "core_io.h" #include "dstencode.h" -#include "init.h" #include "net.h" #include "policy/fees.h" #include "policy/policy.h" @@ -22,6 +22,9 @@ #include "utilmoneystr.h" #include "validation.h" #include "wallet.h" +#include "wallet/coincontrol.h" +#include "wallet/wallet.h" +#include "wallet/walletdb.h" #include "walletdb.h" #include diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -952,25 +952,6 @@ static CFeeRate minTxFee; static CFeeRate fallbackFee; - /** - * Estimate the minimum fee considering user set parameters and the required - * fee - */ - static Amount GetMinimumFee(unsigned int nTxBytes, - unsigned int nConfirmTarget, - const CTxMemPool &pool); - /** - * Estimate the minimum fee considering required fee and targetFee or if 0 - * then fee estimation for nConfirmTarget - */ - static Amount GetMinimumFee(unsigned int nTxBytes, - unsigned int nConfirmTarget, - const CTxMemPool &pool, Amount targetFee); - /** - * Return the minimum required fee taking into account the floating relay - * fee and user set minimum transaction fee - */ - static Amount GetRequiredFee(unsigned int nTxBytes); bool NewKeyPool(); size_t KeypoolCountExternalKeys(); @@ -1072,13 +1053,6 @@ //! Flush wallet (bitdb flush) void Flush(bool shutdown = false); - //! 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 (CWallet::ParameterInteraction forbids - // -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - static bool Verify(const CChainParams &chainParams); - /** * Address book entry changed. * @note called with lock cs_wallet held. @@ -1116,16 +1090,10 @@ */ bool AbandonTransaction(const TxId &txid); - /* Returns the wallets help message */ - static std::string GetWalletHelpString(bool showDebug); - - /** - * Initializes the wallet, returns a new CWallet instance or a null pointer - * in case of an error. - */ + /* Initializes the wallet, returns a new CWallet instance or a null pointer + * in case of an error */ static CWallet *CreateWalletFromFile(const CChainParams &chainParams, const std::string walletFile); - static bool InitLoadWallet(const CChainParams &chainParams); /** * Wallet post-init setup @@ -1134,9 +1102,6 @@ */ void postInitProcess(CScheduler &scheduler); - /* Wallets parameter interaction */ - static bool ParameterInteraction(); - bool BackupWallet(const std::string &strDest); /* Set the HD chain model (chain child index counters) */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -30,6 +30,7 @@ #include "utilmoneystr.h" #include "validation.h" #include "wallet/coincontrol.h" +#include "wallet/fees.h" #include "wallet/finaltx.h" #include @@ -578,77 +579,6 @@ dbw->Flush(shutdown); } -bool CWallet::Verify(const CChainParams &chainParams) { - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - return true; - } - - uiInterface.InitMessage(_("Verifying wallet(s)...")); - - // Keep track of each wallet absolute path to detect duplicates. - std::set wallet_paths; - - for (const std::string &walletFile : gArgs.GetArgs("-wallet")) { - if (fs::path(walletFile).filename() != walletFile) { - return InitError( - strprintf(_("Error loading wallet %s. -wallet parameter must " - "only specify a filename (not a path)."), - walletFile)); - } - - if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. Invalid " - "characters in -wallet filename."), - walletFile)); - } - - fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); - - if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || - fs::is_symlink(wallet_path))) { - return InitError(strprintf(_("Error loading wallet %s. -wallet " - "filename must be a regular file."), - walletFile)); - } - - if (!wallet_paths.insert(wallet_path).second) { - return InitError(strprintf(_("Error loading wallet %s. Duplicate " - "-wallet filename specified."), - walletFile)); - } - - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), - strError)) { - return InitError(strError); - } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - // Recover readable keypairs: - CWallet dummyWallet(chainParams); - std::string backup_filename; - if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, - CWalletDB::RecoverKeysOnlyFilter, - backup_filename)) { - return false; - } - } - - std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile( - walletFile, GetDataDir().string(), strWarning, strError); - if (!strWarning.empty()) { - InitWarning(strWarning); - } - if (!dbV) { - InitError(strError); - return false; - } - } - - return true; -} - void CWallet::SyncMetaData( std::pair range) { // We want all the wallet transactions in range to have the same metadata as @@ -2702,6 +2632,7 @@ std::string &strFailReason, const CCoinControl *coinControl, bool sign) { Amount nValue = Amount::zero(); + int nChangePosRequest = nChangePosInOut; unsigned int nSubtractFeeFromAmount = 0; for (const auto &recipient : vecSend) { @@ -3160,46 +3091,6 @@ return true; } -Amount CWallet::GetRequiredFee(unsigned int nTxBytes) { - return std::max(minTxFee.GetFee(nTxBytes), - GetConfig().GetMinFeePerKB().GetFee(nTxBytes)); -} - -Amount CWallet::GetMinimumFee(unsigned int nTxBytes, - unsigned int nConfirmTarget, - const CTxMemPool &pool) { - // payTxFee is the user-set global for desired feerate. - return GetMinimumFee(nTxBytes, nConfirmTarget, pool, - payTxFee.GetFee(nTxBytes)); -} - -Amount CWallet::GetMinimumFee(unsigned int nTxBytes, - unsigned int nConfirmTarget, - const CTxMemPool &pool, Amount targetFee) { - Amount nFeeNeeded = targetFee; - // User didn't set: use -txconfirmtarget to estimate... - if (nFeeNeeded == Amount::zero()) { - int estimateFoundTarget = nConfirmTarget; - nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget) - .GetFee(nTxBytes); - // ... unless we don't have enough mempool data for estimatefee, then - // use fallbackFee. - if (nFeeNeeded == Amount::zero()) { - nFeeNeeded = fallbackFee.GetFee(nTxBytes); - } - } - - // Prevent user from paying a fee below minRelayTxFee or minTxFee. - nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); - - // But always obey the maximum. - if (nFeeNeeded > maxTxFee) { - nFeeNeeded = maxTxFee; - } - - return nFeeNeeded; -} - DBErrors CWallet::LoadWallet(bool &fFirstRunRet) { fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(*dbw, "cr+").LoadWallet(this); @@ -4036,98 +3927,6 @@ return false; } -std::string CWallet::GetWalletHelpString(bool showDebug) { - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt( - "-disablewallet", - _("Do not load the wallet and disable wallet RPC calls")); - strUsage += HelpMessageOpt( - "-keypool=", strprintf(_("Set key pool size to (default: %u)"), - DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt( - "-fallbackfee=", - 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))); - strUsage += HelpMessageOpt( - "-mintxfee=", - strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee " - "for transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); - strUsage += HelpMessageOpt( - "-paytxfee=", - strprintf( - _("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt( - "-rescan", - _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt( - "-salvagewallet", - _("Attempt to recover private keys from a corrupt wallet on startup")); - - strUsage += - HelpMessageOpt("-spendzeroconfchange", - strprintf(_("Spend unconfirmed change when sending " - "transactions (default: %d)"), - DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += - HelpMessageOpt("-txconfirmtarget=", - strprintf(_("If paytxfee is not set, include enough fee " - "so transactions begin confirmation on " - "average within n blocks (default: %u)"), - DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt( - "-usehd", - _("Use hierarchical deterministic key generation (HD) after BIP32. " - "Only has effect during wallet creation/first start") + - " " + strprintf(_("(default: %d)"), DEFAULT_USE_HD_WALLET)); - strUsage += HelpMessageOpt("-upgradewallet", - _("Upgrade wallet to latest format on startup")); - strUsage += - HelpMessageOpt("-wallet=", - _("Specify wallet file (within data directory)") + " " + - strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); - strUsage += HelpMessageOpt( - "-walletbroadcast", - _("Make the wallet broadcast transactions") + " " + - strprintf(_("(default: %d)"), DEFAULT_WALLETBROADCAST)); - strUsage += HelpMessageOpt("-walletnotify=", - _("Execute command when a wallet transaction " - "changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt( - "-zapwallettxes=", - _("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)")); - - if (showDebug) { - strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); - - strUsage += HelpMessageOpt( - "-dblogsize=", - strprintf("Flush wallet database activity from memory to disk log " - "every megabytes (default: %u)", - DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt( - "-flushwallet", - strprintf("Run a thread to flush wallet periodically (default: %d)", - DEFAULT_FLUSHWALLET)); - strUsage += HelpMessageOpt( - "-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db " - "environment (default: %d)", - DEFAULT_WALLET_PRIVDB)); - strUsage += HelpMessageOpt( - "-walletrejectlongchains", - strprintf(_("Wallet will not create transactions that violate " - "mempool chain limits (default: %d)"), - DEFAULT_WALLET_REJECT_LONG_CHAINS)); - } - - return strUsage; -} - CWallet *CWallet::CreateWalletFromFile(const CChainParams &chainParams, const std::string walletFile) { // Needed to restore wallet transaction meta data after -zapwallettxes @@ -4340,23 +4139,6 @@ return walletInstance; } -bool CWallet::InitLoadWallet(const CChainParams &chainParams) { - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - LogPrintf("Wallet disabled!\n"); - return true; - } - - for (const std::string &walletFile : gArgs.GetArgs("-wallet")) { - CWallet *const pwallet = CreateWalletFromFile(chainParams, walletFile); - if (!pwallet) { - return false; - } - vpwallets.push_back(pwallet); - } - - return true; -} - std::atomic CWallet::fFlushScheduled(false); void CWallet::postInitProcess(CScheduler &scheduler) { @@ -4370,172 +4152,6 @@ } } -bool CWallet::ParameterInteraction() { - CFeeRate minRelayTxFee = GetConfig().GetMinFeePerKB(); - - gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - return true; - } - - 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) && - gArgs.SoftSetBoolArg("-rescan", true)) { - if (is_multiwallet) { - return InitError( - strprintf("%s is only allowed with a single wallet file", - "-salvagewallet")); - } - // Rewrite just private keys: rescan to find transactions - LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting " - "-rescan=1\n", - __func__); - } - - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); - // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting " - "-persistmempool=0\n", - __func__, zapwallettxes); - } - - // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { - 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=%s -> setting " - "-rescan=1\n", - __func__, zapwallettxes); - } - LogPrintf("%s: parameter interaction: -zapwallettxes= -> 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("-mintxfee")) { - Amount n = Amount::zero(); - auto parsed = ParseMoney(gArgs.GetArg("-mintxfee", ""), n); - if (!parsed || Amount::zero() == n) { - return InitError( - AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); - } - - if (n > HIGH_TX_FEE_PER_KB) { - InitWarning(AmountHighWarn("-mintxfee") + " " + - _("This is the minimum transaction fee you pay on " - "every transaction.")); - } - - CWallet::minTxFee = CFeeRate(n); - } - - if (gArgs.IsArgSet("-fallbackfee")) { - Amount nFeePerK = Amount::zero(); - if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { - return InitError( - strprintf(_("Invalid amount for -fallbackfee=: '%s'"), - gArgs.GetArg("-fallbackfee", ""))); - } - - if (nFeePerK > HIGH_TX_FEE_PER_KB) { - InitWarning(AmountHighWarn("-fallbackfee") + " " + - _("This is the transaction fee you may pay when fee " - "estimates are not available.")); - } - - CWallet::fallbackFee = CFeeRate(nFeePerK); - } - - if (gArgs.IsArgSet("-paytxfee")) { - Amount nFeePerK = Amount::zero(); - if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { - return InitError( - AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); - } - - if (nFeePerK > HIGH_TX_FEE_PER_KB) { - InitWarning(AmountHighWarn("-paytxfee") + " " + - _("This is the transaction fee you will pay if you " - "send a transaction.")); - } - - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < minRelayTxFee) { - return InitError(strprintf( - _("Invalid amount for -paytxfee=: '%s' (must " - "be at least %s)"), - gArgs.GetArg("-paytxfee", ""), minRelayTxFee.ToString())); - } - } - - if (gArgs.IsArgSet("-maxtxfee")) { - Amount nMaxFee = Amount::zero(); - 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=: '%s' (must " - "be at least the minrelay fee of %s to prevent " - "stuck transactions)"), - gArgs.GetArg("-maxtxfee", ""), minRelayTxFee.ToString())); - } - } - - nTxConfirmTarget = - gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = - gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - - return true; -} - bool CWallet::BackupWallet(const std::string &strDest) { return dbw->Backup(strDest); }