diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 03fe11a0d8..45f9765231 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -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/rpc/mining.cpp b/src/rpc/mining.cpp index 10bb341e54..5dc468e111 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -815,7 +815,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 +830,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/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/wallet/wallet.cpp b/src/wallet/wallet.cpp index f7f296bd52..8c4f637306 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2951,12 +2951,18 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_c 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, pool, conservative_estimate).GetFee(nTxBytes); + 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; } + // 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