aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-aux/m4/bitcoin_find_bdb48.m42
-rw-r--r--configure.ac7
-rw-r--r--src/bench/base58.cpp27
-rw-r--r--src/bench/verify_script.cpp10
-rw-r--r--src/bitcoin-cli.cpp1
-rw-r--r--src/bitcoind.cpp7
-rw-r--r--src/init.cpp12
-rw-r--r--src/init.h16
-rw-r--r--src/policy/fees.cpp112
-rw-r--r--src/policy/fees.h2
-rw-r--r--src/protocol.h2
-rw-r--r--src/qt/bitcoin.cpp62
-rw-r--r--src/qt/coincontroldialog.cpp12
-rw-r--r--src/qt/sendcoinsdialog.cpp67
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/walletmodel.cpp9
-rw-r--r--src/qt/walletmodel.h2
-rw-r--r--src/random.cpp20
-rw-r--r--src/rpc/mining.cpp13
-rw-r--r--src/rpc/mining.h3
-rw-r--r--src/test/dbwrapper_tests.cpp23
-rw-r--r--src/test/policyestimator_tests.cpp10
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-rw-r--r--src/util.h6
-rw-r--r--src/validationinterface.h1
-rw-r--r--src/wallet/coincontrol.h16
-rw-r--r--src/wallet/feebumper.cpp8
-rw-r--r--src/wallet/feebumper.h3
-rw-r--r--src/wallet/rpcwallet.cpp44
-rw-r--r--src/wallet/rpcwallet.h2
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/wallet.cpp122
-rw-r--r--src/wallet/wallet.h6
-rwxr-xr-xtest/functional/zapwallettxes.py100
34 files changed, 370 insertions, 366 deletions
diff --git a/build-aux/m4/bitcoin_find_bdb48.m4 b/build-aux/m4/bitcoin_find_bdb48.m4
index 980f1e8f19..b9bf7bf46e 100644
--- a/build-aux/m4/bitcoin_find_bdb48.m4
+++ b/build-aux/m4/bitcoin_find_bdb48.m4
@@ -12,7 +12,7 @@ AC_DEFUN([BITCOIN_FIND_BDB48],[
bdbpath=X
bdb48path=X
bdbdirlist=
- for _vn in 4.8 48 4 5 ''; do
+ for _vn in 4.8 48 4 5 5.3 ''; do
for _pfx in b lib ''; do
bdbdirlist="$bdbdirlist ${_pfx}db${_vn}"
done
diff --git a/configure.ac b/configure.ac
index 6b5d891fa5..835dcdbf57 100644
--- a/configure.ac
+++ b/configure.ac
@@ -19,6 +19,12 @@ BITCOIN_GUI_NAME=bitcoin-qt
BITCOIN_CLI_NAME=bitcoin-cli
BITCOIN_TX_NAME=bitcoin-tx
+dnl Unless the user specified ARFLAGS, force it to be cr
+AC_ARG_VAR(ARFLAGS, [Flags for the archiver, defaults to <cr> if not set])
+if test "x${ARFLAGS+set}" != "xset"; then
+ ARFLAGS="cr"
+fi
+
AC_CANONICAL_HOST
AH_TOP([#ifndef BITCOIN_CONFIG_H])
@@ -1285,4 +1291,5 @@ echo " CPPFLAGS = $CPPFLAGS"
echo " CXX = $CXX"
echo " CXXFLAGS = $CXXFLAGS"
echo " LDFLAGS = $LDFLAGS"
+echo " ARFLAGS = $ARFLAGS"
echo
diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp
index 3319c179bf..65e27a615d 100644
--- a/src/bench/base58.cpp
+++ b/src/bench/base58.cpp
@@ -7,34 +7,37 @@
#include "validation.h"
#include "base58.h"
+#include <array>
#include <vector>
#include <string>
static void Base58Encode(benchmark::State& state)
{
- unsigned char buff[32] = {
- 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147,
- 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90,
- 200, 24
+ static const std::array<unsigned char, 32> buff = {
+ {
+ 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147,
+ 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90,
+ 200, 24
+ }
};
- unsigned char* b = buff;
while (state.KeepRunning()) {
- EncodeBase58(b, b + 32);
+ EncodeBase58(buff.begin(), buff.end());
}
}
static void Base58CheckEncode(benchmark::State& state)
{
- unsigned char buff[32] = {
- 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147,
- 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90,
- 200, 24
+ static const std::array<unsigned char, 32> buff = {
+ {
+ 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147,
+ 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90,
+ 200, 24
+ }
};
- unsigned char* b = buff;
std::vector<unsigned char> vch;
- vch.assign(b, b + 32);
+ vch.assign(buff.begin(), buff.end());
while (state.KeepRunning()) {
EncodeBase58Check(vch);
}
diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp
index 23bbadc88d..ef7381c120 100644
--- a/src/bench/verify_script.cpp
+++ b/src/bench/verify_script.cpp
@@ -11,6 +11,8 @@
#include "script/sign.h"
#include "streams.h"
+#include <array>
+
// FIXME: Dedup with BuildCreditingTransaction in test/script_tests.cpp.
static CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey)
{
@@ -55,8 +57,12 @@ static void VerifyScriptBench(benchmark::State& state)
// Keypair.
CKey key;
- const unsigned char vchKey[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
- key.Set(vchKey, vchKey + 32, false);
+ static const std::array<unsigned char, 32> vchKey = {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+ }
+ };
+ key.Set(vchKey.begin(), vchKey.end(), false);
CPubKey pubkey = key.GetPubKey();
uint160 pubkeyHash;
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin());
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 92f6a21ebb..8b48c7f8e4 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -10,7 +10,6 @@
#include "chainparamsbase.h"
#include "clientversion.h"
#include "fs.h"
-#include "utilstrencodings.h"
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util.h"
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 374678310c..f3844e9d47 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -159,7 +159,12 @@ bool AppInit(int argc, char* argv[])
return false;
#endif // HAVE_DECL_DAEMON
}
-
+ // Lock data directory after daemonization
+ if (!AppInitLockDataDirectory())
+ {
+ // If locking the data directory failed, exit immediately
+ exit(EXIT_FAILURE);
+ }
fRet = AppInitMain(threadGroup, scheduler);
}
catch (const std::exception& e) {
diff --git a/src/init.cpp b/src/init.cpp
index 1e85642019..d9b98be739 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1170,13 +1170,13 @@ bool AppInitSanityChecks()
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
// Probe the data directory lock to give an early error message, if possible
+ // We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
+ // and a fork will cause weird behavior to it.
return LockDataDirectory(true);
}
-bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
+bool AppInitLockDataDirectory()
{
- const CChainParams& chainparams = Params();
- // ********************************************************* Step 4a: application initialization
// After daemonization get the data directory lock again and hold on to it until exit
// This creates a slight window for a race condition to happen, however this condition is harmless: it
// will at most make us exit without printing a message to console.
@@ -1184,7 +1184,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Detailed error printed inside LockDataDirectory
return false;
}
+ return true;
+}
+bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
+{
+ const CChainParams& chainparams = Params();
+ // ********************************************************* Step 4a: application initialization
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
diff --git a/src/init.h b/src/init.h
index 8222794374..a0a824738c 100644
--- a/src/init.h
+++ b/src/init.h
@@ -27,27 +27,33 @@ void InitLogging();
void InitParameterInteraction();
/** Initialize bitcoin core: Basic context setup.
- * @note This can be done before daemonization.
+ * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read.
*/
bool AppInitBasicSetup();
/**
* Initialization: parameter interaction.
- * @note This can be done before daemonization.
+ * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called.
*/
bool AppInitParameterInteraction();
/**
* Initialization sanity checks: ecc init, sanity checks, dir lock.
- * @note This can be done before daemonization.
+ * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called.
*/
bool AppInitSanityChecks();
/**
- * Bitcoin core main initialization.
- * @note This should only be done after daemonization.
+ * Lock bitcoin core data directory.
+ * @note This should only be done after daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called.
*/
+bool AppInitLockDataDirectory();
+/**
+ * Bitcoin core main initialization.
+ * @note This should only be done after daemonization. Call Shutdown() if this function fails.
+ * @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
+ */
bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler);
/** The help message mode determines what help message to show */
diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp
index b0a6a2e3d8..0f186fa845 100644
--- a/src/policy/fees.cpp
+++ b/src/policy/fees.cpp
@@ -117,7 +117,7 @@ public:
* Create new TxConfirmStats. This is called by BlockPolicyEstimator's
* constructor with default values.
* @param defaultBuckets contains the upper limits for the bucket boundaries
- * @param maxConfirms max number of confirms to track
+ * @param maxPeriods max number of periods to track
* @param decay how much to decay the historical moving average per block
*/
TxConfirmStats(const std::vector<double>& defaultBuckets, const std::map<double, unsigned int>& defaultBucketMap,
@@ -826,8 +826,10 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget,
* estimates, however, required the 95% threshold at 2 * target be met for any
* longer time horizons also.
*/
-CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const
+CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
{
+ LOCK(cs_feeEstimator);
+
if (feeCalc) {
feeCalc->desiredTarget = confTarget;
feeCalc->returnedTarget = confTarget;
@@ -835,80 +837,70 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation
double median = -1;
EstimationResult tempResult;
- {
- LOCK(cs_feeEstimator);
- // Return failure if trying to analyze a target we're not tracking
- if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms())
- return CFeeRate(0);
+ // Return failure if trying to analyze a target we're not tracking
+ if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms())
+ return CFeeRate(0);
- // It's not possible to get reasonable estimates for confTarget of 1
- if (confTarget == 1)
- confTarget = 2;
+ // It's not possible to get reasonable estimates for confTarget of 1
+ if (confTarget == 1)
+ confTarget = 2;
- unsigned int maxUsableEstimate = MaxUsableEstimate();
- if (maxUsableEstimate <= 1)
- return CFeeRate(0);
+ unsigned int maxUsableEstimate = MaxUsableEstimate();
+ if (maxUsableEstimate <= 1)
+ return CFeeRate(0);
- if ((unsigned int)confTarget > maxUsableEstimate) {
- confTarget = maxUsableEstimate;
- }
+ if ((unsigned int)confTarget > maxUsableEstimate) {
+ confTarget = maxUsableEstimate;
+ }
- assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
- /** true is passed to estimateCombined fee for target/2 and target so
- * that we check the max confirms for shorter time horizons as well.
- * This is necessary to preserve monotonically increasing estimates.
- * For non-conservative estimates we do the same thing for 2*target, but
- * for conservative estimates we want to skip these shorter horizons
- * checks for 2*target because we are taking the max over all time
- * horizons so we already have monotonically increasing estimates and
- * the purpose of conservative estimates is not to let short term
- * fluctuations lower our estimates by too much.
- */
- double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
+ assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
+ /** true is passed to estimateCombined fee for target/2 and target so
+ * that we check the max confirms for shorter time horizons as well.
+ * This is necessary to preserve monotonically increasing estimates.
+ * For non-conservative estimates we do the same thing for 2*target, but
+ * for conservative estimates we want to skip these shorter horizons
+ * checks for 2*target because we are taking the max over all time
+ * horizons so we already have monotonically increasing estimates and
+ * the purpose of conservative estimates is not to let short term
+ * fluctuations lower our estimates by too much.
+ */
+ double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
+ if (feeCalc) {
+ feeCalc->est = tempResult;
+ feeCalc->reason = FeeReason::HALF_ESTIMATE;
+ }
+ median = halfEst;
+ double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
+ if (actualEst > median) {
+ median = actualEst;
if (feeCalc) {
feeCalc->est = tempResult;
- feeCalc->reason = FeeReason::HALF_ESTIMATE;
+ feeCalc->reason = FeeReason::FULL_ESTIMATE;
}
- median = halfEst;
- double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
- if (actualEst > median) {
- median = actualEst;
- if (feeCalc) {
- feeCalc->est = tempResult;
- feeCalc->reason = FeeReason::FULL_ESTIMATE;
- }
+ }
+ double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
+ if (doubleEst > median) {
+ median = doubleEst;
+ if (feeCalc) {
+ feeCalc->est = tempResult;
+ feeCalc->reason = FeeReason::DOUBLE_ESTIMATE;
}
- double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
- if (doubleEst > median) {
- median = doubleEst;
+ }
+
+ if (conservative || median == -1) {
+ double consEst = estimateConservativeFee(2 * confTarget, &tempResult);
+ if (consEst > median) {
+ median = consEst;
if (feeCalc) {
feeCalc->est = tempResult;
- feeCalc->reason = FeeReason::DOUBLE_ESTIMATE;
+ feeCalc->reason = FeeReason::CONSERVATIVE;
}
}
-
- if (conservative || median == -1) {
- double consEst = estimateConservativeFee(2 * confTarget, &tempResult);
- if (consEst > median) {
- median = consEst;
- if (feeCalc) {
- feeCalc->est = tempResult;
- feeCalc->reason = FeeReason::CONSERVATIVE;
- }
- }
- }
- } // Must unlock cs_feeEstimator before taking mempool locks
+ }
if (feeCalc) feeCalc->returnedTarget = confTarget;
- // If mempool is limiting txs , return at least the min feerate from the mempool
- CAmount minPoolFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
- if (minPoolFee > 0 && minPoolFee > median) {
- if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
- return CFeeRate(minPoolFee);
- }
-
if (median < 0)
return CFeeRate(0);
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 4c80371c5c..f4ef793643 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -208,7 +208,7 @@ public:
* the closest target where one can be given. 'conservative' estimates are
* valid over longer time horizons also.
*/
- CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const;
+ CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const;
/** Return a specific fee estimate calculation with a given success
* threshold and time horizon, and optionally return detailed data about
diff --git a/src/protocol.h b/src/protocol.h
index eba39ab1e5..7890bb627d 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -163,7 +163,7 @@ extern const char *PONG;
/**
* The notfound message is a reply to a getdata message which requested an
* object the receiving node does not have available for relay.
- * @ince protocol version 70001.
+ * @since protocol version 70001.
* @see https://bitcoin.org/en/developer-reference#notfound
*/
extern const char *NOTFOUND;
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 8a745cadce..4a4116c670 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -178,6 +178,10 @@ class BitcoinCore: public QObject
Q_OBJECT
public:
explicit BitcoinCore();
+ /** Basic initialization, before starting initialization/shutdown thread.
+ * Return true on success.
+ */
+ static bool baseInitialize();
public Q_SLOTS:
void initialize();
@@ -270,26 +274,32 @@ void BitcoinCore::handleRunawayException(const std::exception *e)
Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui")));
}
+bool BitcoinCore::baseInitialize()
+{
+ if (!AppInitBasicSetup())
+ {
+ return false;
+ }
+ if (!AppInitParameterInteraction())
+ {
+ return false;
+ }
+ if (!AppInitSanityChecks())
+ {
+ return false;
+ }
+ if (!AppInitLockDataDirectory())
+ {
+ return false;
+ }
+ return true;
+}
+
void BitcoinCore::initialize()
{
try
{
qDebug() << __func__ << ": Running initialization in thread";
- if (!AppInitBasicSetup())
- {
- Q_EMIT initializeResult(false);
- return;
- }
- if (!AppInitParameterInteraction())
- {
- Q_EMIT initializeResult(false);
- return;
- }
- if (!AppInitSanityChecks())
- {
- Q_EMIT initializeResult(false);
- return;
- }
bool rv = AppInitMain(threadGroup, scheduler);
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
@@ -689,16 +699,26 @@ int main(int argc, char *argv[])
if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
+ int rv = EXIT_SUCCESS;
try
{
app.createWindow(networkStyle.data());
- app.requestInitialize();
+ // Perform base initialization before spinning up initialization/shutdown thread
+ // This is acceptable because this function only contains steps that are quick to execute,
+ // so the GUI thread won't be held up.
+ if (BitcoinCore::baseInitialize()) {
+ app.requestInitialize();
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
- WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
+ WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
#endif
- app.exec();
- app.requestShutdown();
- app.exec();
+ app.exec();
+ app.requestShutdown();
+ app.exec();
+ rv = app.getReturnValue();
+ } else {
+ // A dialog with detailed error will have been shown by InitError()
+ rv = EXIT_FAILURE;
+ }
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
@@ -706,6 +726,6 @@ int main(int argc, char *argv[])
PrintExceptionContinue(NULL, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
}
- return app.getReturnValue();
+ return rv;
}
#endif // BITCOIN_QT_TEST
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index c19420beb5..f3ee0fbe39 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -490,8 +490,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
else nBytesInputs += 148;
}
- bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, coinControl->signalRbf);
-
// calculation
if (nQuantity > 0)
{
@@ -512,7 +510,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes -= 34;
// Fee
- nPayFee = CWallet::GetMinimumFee(nBytes, coinControl->nConfirmTarget, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */, false /* ignoreGlobalPayTxFee */, conservative_estimate);
+ nPayFee = CWallet::GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */);
if (nPayAmount > 0)
{
@@ -583,12 +581,8 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold.");
// how many satoshis the estimated fee can vary per byte we guess wrong
- double dFeeVary;
- if (payTxFee.GetFeePerK() > 0)
- dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), payTxFee.GetFeePerK()) / 1000;
- else {
- dFeeVary = (double)std::max(CWallet::GetRequiredFee(1000), ::feeEstimator.estimateSmartFee(coinControl->nConfirmTarget, NULL, ::mempool, conservative_estimate).GetFeePerK()) / 1000;
- }
+ double dFeeVary = (double)nPayFee / nBytes;
+
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 86401d3bb4..a01886c3ea 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -175,18 +175,13 @@ void SendCoinsDialog::setModel(WalletModel *_model)
ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
}
connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSmartFeeLabel()));
- connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateGlobalFeeVariables()));
connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
- connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
- connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
- connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
- connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel()));
connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
@@ -194,7 +189,6 @@ void SendCoinsDialog::setModel(WalletModel *_model)
updateFeeSectionControls();
updateMinFeeLabel();
updateSmartFeeLabel();
- updateGlobalFeeVariables();
// set default rbf checkbox state
ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked);
@@ -274,14 +268,10 @@ void SendCoinsDialog::on_sendButton_clicked()
CCoinControl ctrl;
if (model->getOptionsModel()->getCoinControlFeatures())
ctrl = *CoinControlDialog::coinControl;
- if (ui->radioSmartFee->isChecked())
- ctrl.nConfirmTarget = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
- else
- ctrl.nConfirmTarget = 0;
- ctrl.signalRbf = ui->optInRBF->isChecked();
+ updateCoinControlState(ctrl);
- prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
+ prepareStatus = model->prepareTransaction(currentTransaction, ctrl);
// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(prepareStatus,
@@ -636,18 +626,6 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
}
-void SendCoinsDialog::updateGlobalFeeVariables()
-{
- if (ui->radioSmartFee->isChecked())
- {
- payTxFee = CFeeRate(0);
- }
- else
- {
- payTxFee = CFeeRate(ui->customFee->value());
- }
-}
-
void SendCoinsDialog::updateFeeMinimizedLabel()
{
if(!model || !model->getOptionsModel())
@@ -669,19 +647,32 @@ void SendCoinsDialog::updateMinFeeLabel()
);
}
+void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
+{
+ if (ui->radioCustomFee->isChecked()) {
+ ctrl.m_feerate = CFeeRate(ui->customFee->value());
+ } else {
+ ctrl.m_feerate.reset();
+ }
+ // Avoid using global defaults when sending money from the GUI
+ // Either custom fee will be used or if not selected, the confirmation target from dropdown box
+ ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
+ ctrl.signalRbf = ui->optInRBF->isChecked();
+}
+
void SendCoinsDialog::updateSmartFeeLabel()
{
if(!model || !model->getOptionsModel())
return;
-
- int nBlocksToConfirm = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
+ CCoinControl coin_control;
+ updateCoinControlState(coin_control);
+ coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
FeeCalculation feeCalc;
- bool conservative_estimate = CalculateEstimateType(FeeEstimateMode::UNSET, ui->optInRBF->isChecked());
- CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool, conservative_estimate);
- if (feeRate <= CFeeRate(0)) // not enough data => minfee
- {
- ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
- std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
+ CFeeRate feeRate = CFeeRate(CWallet::GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc));
+
+ ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
+
+ if (feeCalc.reason == FeeReason::FALLBACK) {
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
ui->labelFeeEstimation->setText("");
ui->fallbackFeeWarningLabel->setVisible(true);
@@ -692,8 +683,6 @@ void SendCoinsDialog::updateSmartFeeLabel()
}
else
{
- ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
- std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
ui->labelSmartFee2->hide();
ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget));
ui->fallbackFeeWarningLabel->setVisible(false);
@@ -752,8 +741,6 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
if (!checked && model) // coin control features disabled
CoinControlDialog::coinControl->SetNull();
- // make sure we set back the confirmation target
- updateGlobalFeeVariables();
coinControlUpdateLabels();
}
@@ -844,15 +831,11 @@ void SendCoinsDialog::coinControlUpdateLabels()
if (!model || !model->getOptionsModel())
return;
+ updateCoinControlState(*CoinControlDialog::coinControl);
+
// set pay amounts
CoinControlDialog::payAmounts.clear();
CoinControlDialog::fSubtractFeeFromAmount = false;
- if (ui->radioSmartFee->isChecked()) {
- CoinControlDialog::coinControl->nConfirmTarget = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
- } else {
- CoinControlDialog::coinControl->nConfirmTarget = model->getDefaultConfirmTarget();
- }
- CoinControlDialog::coinControl->signalRbf = ui->optInRBF->isChecked();
for(int i = 0; i < ui->entries->count(); ++i)
{
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index ff7040ac5b..70b4aa5a03 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -68,6 +68,8 @@ private:
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString());
void minimizeFeeSection(bool fMinimize);
void updateFeeMinimizedLabel();
+ // Update the passed in CCoinControl with state from the GUI
+ void updateCoinControlState(CCoinControl& ctrl);
private Q_SLOTS:
void on_sendButton_clicked();
@@ -91,7 +93,6 @@ private Q_SLOTS:
void updateFeeSectionControls();
void updateMinFeeLabel();
void updateSmartFeeLabel();
- void updateGlobalFeeVariables();
Q_SIGNALS:
// Fired when a message should be reported to the user
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 60b55da3e7..ba0e1da0c7 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -24,6 +24,7 @@
#include "sync.h"
#include "ui_interface.h"
#include "util.h" // for GetBoolArg
+#include "wallet/coincontrol.h"
#include "wallet/feebumper.h"
#include "wallet/wallet.h"
#include "wallet/walletdb.h" // for BackupWallet
@@ -191,7 +192,7 @@ bool WalletModel::validateAddress(const QString &address)
return addressParsed.IsValid();
}
-WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl)
+WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl)
{
CAmount total = 0;
bool fSubtractFeeFromAmount = false;
@@ -258,7 +259,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
return DuplicateAddress;
}
- CAmount nBalance = getBalance(coinControl);
+ CAmount nBalance = getBalance(&coinControl);
if(total > nBalance)
{
@@ -667,8 +668,10 @@ bool WalletModel::bumpFee(uint256 hash)
{
std::unique_ptr<CFeeBumper> feeBump;
{
+ CCoinControl coin_control;
+ coin_control.signalRbf = true;
LOCK2(cs_main, wallet->cs_wallet);
- feeBump.reset(new CFeeBumper(wallet, hash, nTxConfirmTarget, false, 0, true, FeeEstimateMode::UNSET));
+ feeBump.reset(new CFeeBumper(wallet, hash, coin_control, 0));
}
if (feeBump->getResult() != BumpFeeResult::OK)
{
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 16b0caed4e..5258dc6699 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -154,7 +154,7 @@ public:
};
// prepare transaction for getting txfee before sending coins
- SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl = NULL);
+ SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl);
// Send coins to a list of recipients
SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
diff --git a/src/random.cpp b/src/random.cpp
index 67efc7d945..3226abb69e 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -36,6 +36,10 @@
#include <mutex>
+#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
+#include <cpuid.h>
+#endif
+
#include <openssl/err.h>
#include <openssl/rand.h>
@@ -72,18 +76,9 @@ static bool rdrand_supported = false;
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
static void RDRandInit()
{
- uint32_t eax, ecx, edx;
-#if defined(__i386__) && ( defined(__PIC__) || defined(__PIE__))
- // Avoid clobbering ebx, as that is used for PIC on x86.
- uint32_t tmp;
- __asm__ ("mov %%ebx, %1; cpuid; mov %1, %%ebx": "=a"(eax), "=g"(tmp), "=c"(ecx), "=d"(edx) : "a"(1));
-#else
- uint32_t ebx;
- __asm__ ("cpuid": "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1));
-#endif
- //! When calling cpuid function #1, ecx register will have this set if RDRAND is available.
- if (ecx & CPUID_F1_ECX_RDRAND) {
- LogPrintf("Using RdRand as entropy source\n");
+ uint32_t eax, ebx, ecx, edx;
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) {
+ LogPrintf("Using RdRand as an additional entropy source\n");
rdrand_supported = true;
}
hwrand_initialized.store(true);
@@ -191,6 +186,7 @@ void GetDevURandom(unsigned char *ent32)
do {
ssize_t n = read(f, ent32 + have, NUM_OS_RANDOM_BYTES - have);
if (n <= 0 || n + have > NUM_OS_RANDOM_BYTES) {
+ close(f);
RandFailure();
}
have += n;
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 10bb341e54..b8c94d32ec 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -30,6 +30,16 @@
#include <univalue.h>
+unsigned int ParseConfirmTarget(const UniValue& value)
+{
+ int target = value.get_int();
+ unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ if (target < 1 || (unsigned int)target > max_target) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target));
+ }
+ return (unsigned int)target;
+}
+
/**
* Return average network hashes per second based on the last 'lookup' blocks,
* or from the last difficulty change if 'lookup' is nonpositive.
@@ -815,7 +825,6 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
"\n"
"A negative value is returned if not enough transactions and blocks\n"
"have been observed to make an estimate for any number of blocks.\n"
- "However it will not return a value below the mempool reject fee.\n"
"\nExample:\n"
+ HelpExampleCli("estimatesmartfee", "6")
);
@@ -831,7 +840,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
FeeCalculation feeCalc;
- CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, ::mempool, conservative);
+ CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, conservative);
result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
result.push_back(Pair("blocks", feeCalc.returnedTarget));
return result;
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
index a148d851da..868d7002b5 100644
--- a/src/rpc/mining.h
+++ b/src/rpc/mining.h
@@ -12,4 +12,7 @@
/** Generate blocks (mine) */
UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGenerate, uint64_t nMaxTries, bool keepScript);
+/** Check bounds on a command line confirm target */
+unsigned int ParseConfirmTarget(const UniValue& value);
+
#endif
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 093509e61c..6ed6e7744e 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -24,8 +24,7 @@ BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(dbwrapper)
{
// Perform tests both obfuscated and non-obfuscated.
- for (int i = 0; i < 2; i++) {
- bool obfuscate = (bool)i;
+ for (bool obfuscate : {false, true}) {
fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
char key = 'k';
@@ -45,8 +44,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper)
BOOST_AUTO_TEST_CASE(dbwrapper_batch)
{
// Perform tests both obfuscated and non-obfuscated.
- for (int i = 0; i < 2; i++) {
- bool obfuscate = (bool)i;
+ for (bool obfuscate : {false, true}) {
fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
@@ -82,8 +80,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch)
BOOST_AUTO_TEST_CASE(dbwrapper_iterator)
{
// Perform tests both obfuscated and non-obfuscated.
- for (int i = 0; i < 2; i++) {
- bool obfuscate = (bool)i;
+ for (bool obfuscate : {false, true}) {
fs::path ph = fs::temp_directory_path() / fs::unique_path();
CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate);
@@ -211,12 +208,7 @@ BOOST_AUTO_TEST_CASE(iterator_ordering)
}
std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());
- for (int c=0; c<2; ++c) {
- int seek_start;
- if (c == 0)
- seek_start = 0x00;
- else
- seek_start = 0x80;
+ for (int seek_start : {0x00, 0x80}) {
it->Seek((uint8_t)seek_start);
for (int x=seek_start; x<256; ++x) {
uint8_t key;
@@ -287,12 +279,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
}
std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator());
- for (int c=0; c<2; ++c) {
- int seek_start;
- if (c == 0)
- seek_start = 0;
- else
- seek_start = 5;
+ for (int seek_start : {0, 5}) {
snprintf(buf, sizeof(buf), "%d", seek_start);
StringContentsSerializer seek_key(buf);
it->Seek(seek_key);
diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp
index 8cdd392109..fd8f7191f4 100644
--- a/src/test/policyestimator_tests.cpp
+++ b/src/test/policyestimator_tests.cpp
@@ -177,16 +177,6 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
for (int i = 2; i < 9; i++) { // At 9, the original estimate was already at the bottom (b/c scale = 2)
BOOST_CHECK(feeEst.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
}
-
- // Test that if the mempool is limited, estimateSmartFee won't return a value below the mempool min fee
- mpool.addUnchecked(tx.GetHash(), entry.Fee(feeV[5]).Time(GetTime()).Height(blocknum).FromTx(tx));
- // evict that transaction which should set a mempool min fee of minRelayTxFee + feeV[5]
- mpool.TrimToSize(1);
- BOOST_CHECK(mpool.GetMinFee(1).GetFeePerK() > feeV[5]);
- for (int i = 1; i < 10; i++) {
- BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool, true).GetFeePerK() >= feeEst.estimateRawFee(i, 0.85, FeeEstimateHorizon::MED_HALFLIFE).GetFeePerK());
- BOOST_CHECK(feeEst.estimateSmartFee(i, NULL, mpool, true).GetFeePerK() >= mpool.GetMinFee(1).GetFeePerK());
- }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index a74f40251a..f609cb1af4 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -196,8 +196,8 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
// Test that invalidity under a set of flags doesn't preclude validity
// under other (eg consensus) flags.
// spend_tx is invalid according to DERSIG
- CValidationState state;
{
+ CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);
BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
diff --git a/src/util.h b/src/util.h
index 824ad51ac4..e1bdfb1988 100644
--- a/src/util.h
+++ b/src/util.h
@@ -215,7 +215,7 @@ public:
* Return string argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
- * @param default (e.g. "1")
+ * @param strDefault (e.g. "1")
* @return command-line argument or default value
*/
std::string GetArg(const std::string& strArg, const std::string& strDefault);
@@ -224,7 +224,7 @@ public:
* Return integer argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
- * @param default (e.g. 1)
+ * @param nDefault (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
int64_t GetArg(const std::string& strArg, int64_t nDefault);
@@ -233,7 +233,7 @@ public:
* Return boolean argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
- * @param default (true or false)
+ * @param fDefault (true or false)
* @return command-line argument or default value
*/
bool GetBoolArg(const std::string& strArg, bool fDefault);
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 568da66df2..d6da2bc1fd 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -86,7 +86,6 @@ public:
void TransactionAddedToMempool(const CTransactionRef &);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
- void UpdatedTransaction(const uint256 &);
void SetBestChain(const CBlockLocator &);
void Inventory(const uint256 &);
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index bdd01bec12..fc0e7c519e 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -10,6 +10,8 @@
#include "primitives/transaction.h"
#include "wallet/wallet.h"
+#include <boost/optional.hpp>
+
/** Coin Control Features. */
class CCoinControl
{
@@ -19,12 +21,12 @@ public:
bool fAllowOtherInputs;
//! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria
bool fAllowWatchOnly;
- //! Override estimated feerate
+ //! Override automatic min/max checks on fee, m_feerate must be set if true
bool fOverrideFeeRate;
- //! Feerate to use if overrideFeeRate is true
- CFeeRate nFeeRate;
- //! Override the default confirmation target, 0 = use default
- int nConfirmTarget;
+ //! Override the default payTxFee if set
+ boost::optional<CFeeRate> m_feerate;
+ //! Override the default confirmation target if set
+ boost::optional<unsigned int> m_confirm_target;
//! Signal BIP-125 replace by fee.
bool signalRbf;
//! Fee estimation mode to control arguments to estimateSmartFee
@@ -41,9 +43,9 @@ public:
fAllowOtherInputs = false;
fAllowWatchOnly = false;
setSelected.clear();
- nFeeRate = CFeeRate(0);
+ m_feerate.reset();
fOverrideFeeRate = false;
- nConfirmTarget = 0;
+ m_confirm_target.reset();
signalRbf = fWalletRbf;
m_fee_mode = FeeEstimateMode::UNSET;
}
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 607ecf4182..4bfd8726a5 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "consensus/validation.h"
+#include "wallet/coincontrol.h"
#include "wallet/feebumper.h"
#include "wallet/wallet.h"
#include "policy/fees.h"
@@ -66,7 +67,7 @@ bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx
return true;
}
-CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable, FeeEstimateMode fee_mode)
+CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee)
:
txid(std::move(txidIn)),
nOldFee(0),
@@ -165,8 +166,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
nNewFee = totalFee;
nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
} else {
- bool conservative_estimate = CalculateEstimateType(fee_mode, newTxReplaceable);
- nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, ::feeEstimator, nullptr /* FeeCalculation */, ignoreGlobalPayTxFee, conservative_estimate);
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
// New fee rate must be at least old rate + minimum incremental relay rate
@@ -221,7 +221,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
}
// Mark new tx not replaceable, if requested.
- if (!newTxReplaceable) {
+ if (!coin_control.signalRbf) {
for (auto& input : mtx.vin) {
if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
}
diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h
index 11e2f5f953..3d64e53c15 100644
--- a/src/wallet/feebumper.h
+++ b/src/wallet/feebumper.h
@@ -10,6 +10,7 @@
class CWallet;
class CWalletTx;
class uint256;
+class CCoinControl;
enum class FeeEstimateMode;
enum class BumpFeeResult
@@ -25,7 +26,7 @@ enum class BumpFeeResult
class CFeeBumper
{
public:
- CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, int newConfirmTarget, bool ignoreGlobalPayTxFee, CAmount totalFee, bool newTxReplaceable, FeeEstimateMode fee_mode);
+ CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee);
BumpFeeResult getResult() const { return currentResult; }
const std::vector<std::string>& getErrors() const { return vErrors; }
CAmount getOldFee() const { return nOldFee; }
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 873542a966..ee8c7548fc 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -356,7 +356,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
return ret;
}
-static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, CCoinControl *coin_control = nullptr)
+static void SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, const CCoinControl& coin_control)
{
CAmount curBalance = pwallet->GetBalance();
@@ -460,7 +460,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
}
if (request.params.size() > 6 && !request.params[6].isNull()) {
- coin_control.nConfirmTarget = request.params[6].get_int();
+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
}
if (request.params.size() > 7 && !request.params[7].isNull()) {
@@ -472,7 +472,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
EnsureWalletIsUnlocked(pwallet);
- SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, &coin_control);
+ SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control);
return wtx.GetHash().GetHex();
}
@@ -898,7 +898,8 @@ UniValue sendfrom(const JSONRPCRequest& request)
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
- SendMoney(pwallet, address.Get(), nAmount, false, wtx);
+ CCoinControl no_coin_control; // This is a deprecated API
+ SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control);
return wtx.GetHash().GetHex();
}
@@ -980,7 +981,7 @@ UniValue sendmany(const JSONRPCRequest& request)
}
if (request.params.size() > 6 && !request.params[6].isNull()) {
- coin_control.nConfirmTarget = request.params[6].get_int();
+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
}
if (request.params.size() > 7 && !request.params[7].isNull()) {
@@ -1033,7 +1034,7 @@ UniValue sendmany(const JSONRPCRequest& request)
CAmount nFeeRequired = 0;
int nChangePosRet = -1;
std::string strFailReason;
- bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, &coin_control);
+ bool fCreated = pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason, coin_control);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
CValidationState state;
@@ -2729,13 +2730,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VSTR});
CCoinControl coinControl;
- coinControl.destChange = CNoDestination();
int changePosition = -1;
- coinControl.fAllowWatchOnly = false; // include watching
bool lockUnspents = false;
bool reserveChangeKey = true;
- coinControl.nFeeRate = CFeeRate(0);
- coinControl.fOverrideFeeRate = false;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
@@ -2787,7 +2784,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (options.exists("feeRate"))
{
- coinControl.nFeeRate = CFeeRate(AmountFromValue(options["feeRate"]));
+ coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
coinControl.fOverrideFeeRate = true;
}
@@ -2798,7 +2795,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
coinControl.signalRbf = options["replaceable"].get_bool();
}
if (options.exists("conf_target")) {
- coinControl.nConfirmTarget = options["conf_target"].get_int();
+ coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"]);
}
if (options.exists("estimate_mode")) {
if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
@@ -2904,11 +2901,9 @@ UniValue bumpfee(const JSONRPCRequest& request)
hash.SetHex(request.params[0].get_str());
// optional parameters
- bool ignoreGlobalPayTxFee = false;
- int newConfirmTarget = nTxConfirmTarget;
CAmount totalFee = 0;
- bool replaceable = true;
- FeeEstimateMode fee_mode = FeeEstimateMode::UNSET;
+ CCoinControl coin_control;
+ coin_control.signalRbf = true;
if (request.params.size() > 1) {
UniValue options = request.params[1];
RPCTypeCheckObj(options,
@@ -2922,15 +2917,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("confTarget") && options.exists("totalFee")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction.");
- } else if (options.exists("confTarget")) {
- // If the user has explicitly set a confTarget in this rpc call,
- // then override the default logic that uses the global payTxFee
- // instead of the confirmation target.
- ignoreGlobalPayTxFee = true;
- newConfirmTarget = options["confTarget"].get_int();
- if (newConfirmTarget <= 0) { // upper-bound will be checked by estimatefee/smartfee
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid confTarget (cannot be <= 0)");
- }
+ } else if (options.exists("confTarget")) { // TODO: alias this to conf_target
+ coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"]);
} else if (options.exists("totalFee")) {
totalFee = options["totalFee"].get_int64();
if (totalFee <= 0) {
@@ -2939,10 +2927,10 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
if (options.exists("replaceable")) {
- replaceable = options["replaceable"].get_bool();
+ coin_control.signalRbf = options["replaceable"].get_bool();
}
if (options.exists("estimate_mode")) {
- if (!FeeModeFromString(options["estimate_mode"].get_str(), fee_mode)) {
+ if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
}
}
@@ -2951,7 +2939,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
- CFeeBumper feeBump(pwallet, hash, newConfirmTarget, ignoreGlobalPayTxFee, totalFee, replaceable, fee_mode);
+ CFeeBumper feeBump(pwallet, hash, coin_control, totalFee);
BumpFeeResult res = feeBump.getResult();
if (res != BumpFeeResult::OK)
{
diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h
index bd5dad18ca..31e2f6a699 100644
--- a/src/wallet/rpcwallet.h
+++ b/src/wallet/rpcwallet.h
@@ -16,7 +16,7 @@ void RegisterWalletRPCCommands(CRPCTable &t);
* @param[in] request JSONRPCRequest that wishes to access a wallet
* @return NULL if no wallet should be used, or a pointer to the CWallet
*/
-CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest&);
+CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request);
std::string HelpRequiringPassphrase(CWallet *);
void EnsureWalletIsUnlocked(CWallet *);
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 96a1b14b60..8176a0017c 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -13,6 +13,7 @@
#include "rpc/server.h"
#include "test/test_bitcoin.h"
#include "validation.h"
+#include "wallet/coincontrol.h"
#include "wallet/test/wallet_test_fixture.h"
#include <boost/test/unit_test.hpp>
@@ -617,7 +618,8 @@ public:
CAmount fee;
int changePos = -1;
std::string error;
- BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error));
+ CCoinControl dummy;
+ BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, changePos, error, dummy));
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state));
auto it = wallet->mapWallet.find(wtx.GetHash());
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 5db13ef02f..6f1894d430 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2490,9 +2490,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
CReserveKey reservekey(this);
CWalletTx wtx;
- if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false))
+ if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, coinControl, false)) {
return false;
-
+ }
if (nChangePosInOut != -1)
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
@@ -2523,7 +2523,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
}
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
- int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
+ int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
@@ -2588,7 +2588,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
- AvailableCoins(vAvailableCoins, true, coinControl);
+ AvailableCoins(vAvailableCoins, true, &coin_control);
// Create change script that will be used if we need change
// TODO: pass in scriptChange instead of reservekey so
@@ -2596,12 +2596,9 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
CScript scriptChange;
// coin control: send change to custom address
- if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))
- scriptChange = GetScriptForDestination(coinControl->destChange);
-
- // no coin control: send change to newly generated address
- else
- {
+ if (!boost::get<CNoDestination>(&coin_control.destChange)) {
+ scriptChange = GetScriptForDestination(coin_control.destChange);
+ } else { // no coin control: send change to newly generated address
// Note: We use a new key here to keep it from being obvious which side is the change.
// The drawback is that by not reusing a previous key, the change may be lost if a
// backup is restored, if the backup doesn't have the new private key for the change.
@@ -2675,7 +2672,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
- if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))
+ if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, &coin_control))
{
strFailReason = _("Insufficient funds");
return false;
@@ -2726,8 +2723,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// to avoid conflicting with other possible uses of nSequence,
// and in the spirit of "smallest possible change from prior
// behavior."
- bool rbf = coinControl ? coinControl->signalRbf : fWalletRbf;
- const uint32_t nSequence = rbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1);
+ const uint32_t nSequence = coin_control.signalRbf ? MAX_BIP125_RBF_SEQUENCE : (std::numeric_limits<unsigned int>::max() - 1);
for (const auto& coin : setCoins)
txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
nSequence));
@@ -2746,17 +2742,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
vin.scriptWitness.SetNull();
}
- // Allow to override the default confirmation target over the CoinControl instance
- int currentConfirmationTarget = nTxConfirmTarget;
- if (coinControl && coinControl->nConfirmTarget > 0)
- currentConfirmationTarget = coinControl->nConfirmTarget;
-
- // Allow to override the default fee estimate mode over the CoinControl instance
- bool conservative_estimate = CalculateEstimateType(coinControl ? coinControl->m_fee_mode : FeeEstimateMode::UNSET, rbf);
-
- CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc, false /* ignoreGlobalPayTxFee */, conservative_estimate);
- if (coinControl && coinControl->fOverrideFeeRate)
- nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);
+ CAmount nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
// because we must be at the maximum allowed fee.
@@ -2781,7 +2767,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// new inputs. We now know we only need the smaller fee
// (because of reduced tx size) and so we should add a
// change output. Only try this once.
- CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, currentConfirmationTarget, ::mempool, ::feeEstimator, nullptr, false /* ignoreGlobalPayTxFee */, conservative_estimate);
+ CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, ::dustRelayFee);
CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change;
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
@@ -2957,33 +2943,61 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
}
-CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate)
+CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc)
{
- // payTxFee is the user-set global for desired feerate
- CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
- // User didn't set: use -txconfirmtarget to estimate...
- if (nFeeNeeded == 0 || ignoreGlobalPayTxFee) {
- nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, conservative_estimate).GetFee(nTxBytes);
- // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
- if (nFeeNeeded == 0) {
- nFeeNeeded = fallbackFee.GetFee(nTxBytes);
+ /* User control of how to calculate fee uses the following parameter precedence:
+ 1. coin_control.m_feerate
+ 2. coin_control.m_confirm_target
+ 3. payTxFee (user-set global variable)
+ 4. nTxConfirmTarget (user-set global variable)
+ The first parameter that is set is used.
+ */
+ CAmount fee_needed;
+ if (coin_control.m_feerate) { // 1.
+ fee_needed = coin_control.m_feerate->GetFee(nTxBytes);
+ if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
+ // Allow to override automatic min/max check over coin control instance
+ if (coin_control.fOverrideFeeRate) return fee_needed;
+ }
+ else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee
+ fee_needed = ::payTxFee.GetFee(nTxBytes);
+ if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
+ }
+ else { // 2. or 4.
+ // We will use smart fee estimation
+ unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget;
+ // By default estimates are economical iff we are signaling opt-in-RBF
+ bool conservative_estimate = !coin_control.signalRbf;
+ // Allow to override the default fee estimate mode over the CoinControl instance
+ if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true;
+ else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false;
+
+ fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes);
+ if (fee_needed == 0) {
+ // if we don't have enough data for estimateSmartFee, then use fallbackFee
+ fee_needed = fallbackFee.GetFee(nTxBytes);
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
}
- } else {
- if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
+ // Obey mempool min fee when using smart fee estimation
+ CAmount min_mempool_fee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes);
+ if (fee_needed < min_mempool_fee) {
+ fee_needed = min_mempool_fee;
+ if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
+ }
}
+
// prevent user from paying a fee below minRelayTxFee or minTxFee
- CAmount requiredFee = GetRequiredFee(nTxBytes);
- if (requiredFee > nFeeNeeded) {
- nFeeNeeded = requiredFee;
+ CAmount required_fee = GetRequiredFee(nTxBytes);
+ if (required_fee > fee_needed) {
+ fee_needed = required_fee;
if (feeCalc) feeCalc->reason = FeeReason::REQUIRED;
}
// But always obey the maximum
- if (nFeeNeeded > maxTxFee) {
- nFeeNeeded = maxTxFee;
+ if (fee_needed > maxTxFee) {
+ fee_needed = maxTxFee;
if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE;
}
- return nFeeNeeded;
+ return fee_needed;
}
@@ -4073,13 +4087,19 @@ bool CWallet::ParameterInteraction()
}
}
- // -zapwallettx implies a rescan
- if (GetBoolArg("-zapwallettxes", false)) {
+ int zapwallettxes = GetArg("-zapwallettxes", 0);
+ // -zapwallettxes implies dropping the mempool on startup
+ if (zapwallettxes != 0 && 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 (SoftSetBoolArg("-rescan", true)) {
- LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\n", __func__);
+ LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes);
}
}
@@ -4219,15 +4239,3 @@ bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
{
return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}
-
-bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf) {
- switch (mode) {
- case FeeEstimateMode::UNSET:
- return !opt_in_rbf; // Allow for lower fees if RBF is an option
- case FeeEstimateMode::CONSERVATIVE:
- return true;
- case FeeEstimateMode::ECONOMICAL:
- return false;
- }
- return true;
-}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 9e687c4bfd..06937566b0 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -955,7 +955,7 @@ public:
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
- std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
+ std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
@@ -970,7 +970,7 @@ public:
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
- static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreGlobalPayTxFee, bool conservative_estimate);
+ static CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc);
/**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee
@@ -1221,6 +1221,4 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins
return true;
}
-bool CalculateEstimateType(FeeEstimateMode mode, bool opt_in_rbf);
-
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py
index e4d40520ef..af867d7a52 100755
--- a/test/functional/zapwallettxes.py
+++ b/test/functional/zapwallettxes.py
@@ -4,77 +4,73 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the zapwallettxes functionality.
-- start three bitcoind nodes
-- create four transactions on node 0 - two are confirmed and two are
- unconfirmed.
-- restart node 1 and verify that both the confirmed and the unconfirmed
+- start two bitcoind nodes
+- create two transactions on node 0 - one is confirmed and one is unconfirmed.
+- restart node 0 and verify that both the confirmed and the unconfirmed
transactions are still available.
-- restart node 0 and verify that the confirmed transactions are still
- available, but that the unconfirmed transaction has been zapped.
+- restart node 0 with zapwallettxes and persistmempool, and verify that both
+ the confirmed and the unconfirmed transactions are still available.
+- restart node 0 with just zapwallettxed and verify that the confirmed
+ transactions are still available, but that the unconfirmed transaction has
+ been zapped.
"""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import *
-
+from test_framework.util import (assert_equal,
+ assert_raises_jsonrpc,
+ )
class ZapWalletTXesTest (BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
- self.num_nodes = 3
-
- def setup_network(self):
- super().setup_network()
- connect_nodes_bi(self.nodes,0,2)
+ self.num_nodes = 2
- def run_test (self):
+ def run_test(self):
self.log.info("Mining blocks...")
self.nodes[0].generate(1)
self.sync_all()
- self.nodes[1].generate(101)
- self.sync_all()
-
- assert_equal(self.nodes[0].getbalance(), 50)
-
- txid0 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
- txid1 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
+ self.nodes[1].generate(100)
self.sync_all()
+
+ # This transaction will be confirmed
+ txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 10)
+
self.nodes[0].generate(1)
self.sync_all()
-
- txid2 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
- txid3 = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
-
- tx0 = self.nodes[0].gettransaction(txid0)
- assert_equal(tx0['txid'], txid0) #tx0 must be available (confirmed)
-
- tx1 = self.nodes[0].gettransaction(txid1)
- assert_equal(tx1['txid'], txid1) #tx1 must be available (confirmed)
-
- tx2 = self.nodes[0].gettransaction(txid2)
- assert_equal(tx2['txid'], txid2) #tx2 must be available (unconfirmed)
-
- tx3 = self.nodes[0].gettransaction(txid3)
- assert_equal(tx3['txid'], txid3) #tx3 must be available (unconfirmed)
-
- #restart bitcoind
+
+ # This transaction will not be confirmed
+ txid2 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 20)
+
+ # Confirmed and unconfirmed transactions are now in the wallet.
+ assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
+ assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
+
+ # Stop-start node0. Both confirmed and unconfirmed transactions remain in the wallet.
+ self.stop_node(0)
+ self.nodes[0] = self.start_node(0, self.options.tmpdir)
+
+ assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
+ assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
+
+ # Stop node0 and restart with zapwallettxes and persistmempool. The unconfirmed
+ # transaction is zapped from the wallet, but is re-added when the mempool is reloaded.
self.stop_node(0)
- self.nodes[0] = self.start_node(0,self.options.tmpdir)
-
- tx3 = self.nodes[0].gettransaction(txid3)
- assert_equal(tx3['txid'], txid3) #tx must be available (unconfirmed)
-
+ self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-persistmempool=1", "-zapwallettxes=2"])
+
+ assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
+ assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2)
+
+ # Stop node0 and restart with zapwallettxes, but not persistmempool.
+ # The unconfirmed transaction is zapped and is no longer in the wallet.
self.stop_node(0)
-
- #restart bitcoind with zapwallettxes
- self.nodes[0] = self.start_node(0,self.options.tmpdir, ["-zapwallettxes=1"])
-
- assert_raises(JSONRPCException, self.nodes[0].gettransaction, [txid3])
- #there must be an exception because the unconfirmed wallettx0 must be gone by now
+ self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-zapwallettxes=2"])
- tx0 = self.nodes[0].gettransaction(txid0)
- assert_equal(tx0['txid'], txid0) #tx0 (confirmed) must still be available because it was confirmed
+ # tx1 is still be available because it was confirmed
+ assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1)
+ # This will raise an exception because the unconfirmed transaction has been zapped
+ assert_raises_jsonrpc(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2)
if __name__ == '__main__':
- ZapWalletTXesTest ().main ()
+ ZapWalletTXesTest().main()