diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -190,12 +190,6 @@ //! economical to spend. virtual CFeeRate relayDustFee() = 0; - //! Get node max tx fee setting (-maxtxfee). - //! This could be replaced by a per-wallet max fee, as proposed at - //! https://github.com/bitcoin/bitcoin/issues/15355 - //! But for the time being, wallets call this to access the node setting. - virtual Amount maxTxFee() = 0; - //! Check if pruning is enabled. virtual bool getPruneMode() = 0; diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -318,7 +318,6 @@ } CFeeRate relayMinFee() override { return ::minRelayTxFee; } CFeeRate relayDustFee() override { return ::dustRelayFee; } - Amount maxTxFee() override { return ::maxTxFee; } bool getPruneMode() override { return ::fPruneMode; } bool p2pEnabled() override { return g_connman != nullptr; } bool isReadyToBroadcast() override { diff --git a/src/interfaces/node.h b/src/interfaces/node.h --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -167,9 +167,6 @@ //! Get network active. virtual bool getNetworkActive() = 0; - //! Get max tx fee. - virtual Amount getMaxTxFee() = 0; - //! Estimate smart fee. virtual CFeeRate estimateSmartFee() = 0; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -234,7 +234,6 @@ bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); } - Amount getMaxTxFee() override { return ::maxTxFee; } CFeeRate estimateSmartFee() override { return g_mempool.estimateFee(); } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } UniValue executeRpc(Config &config, const std::string &command, diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -234,6 +234,9 @@ // Get default change type. virtual OutputType getDefaultChangeType() = 0; + //! Get max tx fee. + virtual Amount getDefaultMaxTxFee() = 0; + // Remove wallet. virtual void remove() = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -442,6 +442,9 @@ OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; } + Amount getDefaultMaxTxFee() override { + return m_wallet.m_default_max_tx_fee; + } void remove() override { RemoveWallet(m_shared_wallet); } std::unique_ptr handleUnload(UnloadFn fn) override { return MakeHandler(m_wallet.NotifyUnload.connect(fn)); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -606,7 +606,7 @@ tr("A fee higher than %1 is considered an absurdly high fee.") .arg(BitcoinUnits::formatWithUnit( model->getOptionsModel()->getDisplayUnit(), - model->node().getMaxTxFee())); + model->wallet().getDefaultMaxTxFee())); break; case WalletModel::PaymentRequestExpired: msgParams.first = tr("Payment request expired."); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -220,10 +220,10 @@ return TransactionCreationFailed; } - // reject absurdly high fee. (This can never happen because the - // wallet caps the fee at maxTxFee. This merely serves as a + // reject absurdly high fee. (This can never happen because the wallet caps + // the fee at m_default_max_tx_fee. This merely serves as a // belt-and-suspenders check) - if (nFeeRequired > m_node.getMaxTxFee()) { + if (nFeeRequired > m_wallet->getDefaultMaxTxFee()) { return AbsurdFee; } diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -66,14 +66,6 @@ static const Amount DEFAULT_MIN_RELAY_TX_FEE_PER_KB(1000 * SATOSHI); /** Default for -excessutxocharge for transactions transactions */ static const Amount DEFAULT_UTXO_FEE = Amount::zero(); -//! -maxtxfee default -static const Amount DEFAULT_TRANSACTION_MAXFEE(COIN / 10); -//! Discourage users to set fees higher than this amount (in satoshis) per kB -static const Amount HIGH_TX_FEE_PER_KB(COIN / 100); -/** - * -maxtxfee will warn if called with a higher fee than this amount (in satoshis - */ -static const Amount HIGH_MAX_TX_FEE(100 * HIGH_TX_FEE_PER_KB); /** * Default for -mempoolexpiry, expiration time for mempool transactions in * hours. @@ -194,11 +186,6 @@ * transaction creation) */ extern CFeeRate minRelayTxFee; -/** - * Absolute maximum transaction fee (in satoshis) used by wallet and mempool - * (rejects high fee in sendrawtransaction) - */ -extern Amount maxTxFee; /** * If the tip is older than this (in seconds), the node is considered to be in * initial block download. diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -109,7 +109,6 @@ arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE_PER_KB); -Amount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; CTxMemPool g_mempool; diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -23,7 +23,7 @@ GetMinimumFeeRate(wallet, coin_control).GetFeeCeiling(nTxBytes); // But always obey the maximum. - const Amount max_tx_fee = wallet.chain().maxTxFee(); + const Amount max_tx_fee = wallet.m_default_max_tx_fee; if (nFeeNeeded > max_tx_fee) { nFeeNeeded = max_tx_fee; } diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -223,37 +223,6 @@ .translated); } - if (minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) { - InitWarning( - AmountHighWarn("-minrelaytxfee") + " " + - _("The wallet will avoid paying less than the minimum relay fee.") - .translated); - } - - 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.") - .translated); - } - - 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)") - .translated, - gArgs.GetArg("-maxtxfee", ""), minRelayTxFee.ToString())); - } - } - return true; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -72,6 +72,13 @@ static const bool DEFAULT_AVOIDPARTIALSPENDS = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; +//! -maxtxfee default +constexpr Amount DEFAULT_TRANSACTION_MAXFEE{COIN / 10}; +//! Discourage users to set fees higher than this amount (in satoshis) per kB +constexpr Amount HIGH_TX_FEE_PER_KB{COIN / 100}; +//! -maxtxfee will warn if called with a higher fee than this amount (in +//! satoshis) +constexpr Amount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB}; class CChainParams; class CCoinControl; @@ -1158,6 +1165,11 @@ CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE}; OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE}; OutputType m_default_change_type{DEFAULT_CHANGE_TYPE}; + /** + * Absolute maximum transaction fee (in satoshis) used by default for the + * wallet. + */ + Amount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE}; bool NewKeyPool(); size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -4684,6 +4684,37 @@ return nullptr; } } + + if (gArgs.IsArgSet("-maxtxfee")) { + Amount nMaxFee = Amount::zero(); + if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) { + chain.initError( + AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); + return nullptr; + } + if (nMaxFee > HIGH_MAX_TX_FEE) { + chain.initWarning(_("-maxtxfee is set very high! Fees this large " + "could be paid on a single transaction.") + .translated); + } + if (CFeeRate(nMaxFee, 1000) < chain.relayMinFee()) { + chain.initError(strprintf( + _("Invalid amount for -maxtxfee=: '%s' (must be at " + "least the minrelay fee of %s to prevent stuck transactions)") + .translated, + gArgs.GetArg("-maxtxfee", ""), chain.relayMinFee().ToString())); + return nullptr; + } + walletInstance->m_default_max_tx_fee = nMaxFee; + } + + if (chain.relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { + chain.initWarning( + AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.") + .translated); + } + walletInstance->m_spend_zero_conf_change = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); @@ -4901,7 +4932,7 @@ // because we think that this newly generated transaction's change is // unavailable as we're not yet aware that it is in the mempool. bool ret = locked_chain.submitToMemoryPool( - ::GetConfig(), tx, pwallet->chain().maxTxFee(), state); + ::GetConfig(), tx, pwallet->m_default_max_tx_fee, state); fInMempool |= ret; return ret; }