diff options
Diffstat (limited to 'src/policy')
-rw-r--r-- | src/policy/feerate.h | 2 | ||||
-rw-r--r-- | src/policy/fees.cpp | 164 | ||||
-rw-r--r-- | src/policy/fees.h | 18 | ||||
-rw-r--r-- | src/policy/policy.cpp | 12 | ||||
-rw-r--r-- | src/policy/policy.h | 4 |
5 files changed, 125 insertions, 75 deletions
diff --git a/src/policy/feerate.h b/src/policy/feerate.h index e82268b095..7e519e3efa 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -26,7 +26,6 @@ public: explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/ CFeeRate(const CAmount& nFeePaid, size_t nBytes); - CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } /** * Return the fee in satoshis for the given size in bytes. */ @@ -40,6 +39,7 @@ public: friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; } friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; } friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; } + friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; } CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; } std::string ToString() const; diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 771491770e..b9476407cf 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -16,6 +16,19 @@ static constexpr double INF_FEERATE = 1e99; +std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) { + static const std::map<FeeEstimateHorizon, std::string> horizon_strings = { + {FeeEstimateHorizon::SHORT_HALFLIFE, "short"}, + {FeeEstimateHorizon::MED_HALFLIFE, "medium"}, + {FeeEstimateHorizon::LONG_HALFLIFE, "long"}, + }; + auto horizon_string = horizon_strings.find(horizon); + + if (horizon_string == horizon_strings.end()) return "unknown"; + + return horizon_string->second; +} + std::string StringForFeeReason(FeeReason reason) { static const std::map<FeeReason, std::string> fee_reason_strings = { {FeeReason::NONE, "None"}, @@ -36,6 +49,20 @@ std::string StringForFeeReason(FeeReason reason) { return reason_string->second; } +bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) { + static const std::map<std::string, FeeEstimateMode> fee_modes = { + {"UNSET", FeeEstimateMode::UNSET}, + {"ECONOMICAL", FeeEstimateMode::ECONOMICAL}, + {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE}, + }; + auto mode = fee_modes.find(mode_string); + + if (mode == fee_modes.end()) return false; + + fee_estimate_mode = mode->second; + return true; +} + /** * We will instantiate an instance of this class to track transactions that were * included in a block. We will lump transactions into a bucket according to their @@ -90,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, @@ -671,7 +698,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr break; } default: { - return CFeeRate(0); + throw std::out_of_range("CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon"); } } @@ -690,6 +717,24 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr return CFeeRate(median); } +unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const +{ + switch (horizon) { + case FeeEstimateHorizon::SHORT_HALFLIFE: { + return shortStats->GetMaxConfirms(); + } + case FeeEstimateHorizon::MED_HALFLIFE: { + return feeStats->GetMaxConfirms(); + } + case FeeEstimateHorizon::LONG_HALFLIFE: { + return longStats->GetMaxConfirms(); + } + default: { + throw std::out_of_range("CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon"); + } + } +} + unsigned int CBlockPolicyEstimator::BlockSpan() const { if (firstRecordedHeight == 0) return 0; @@ -781,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; @@ -790,82 +837,69 @@ 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); - - // It's not possible to get reasonable estimates for confTarget of 1 - if (confTarget == 1) - confTarget = 2; + // Return failure if trying to analyze a target we're not tracking + if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) { + return CFeeRate(0); // error condition + } - unsigned int maxUsableEstimate = MaxUsableEstimate(); - if (maxUsableEstimate <= 1) - return CFeeRate(0); + // It's not possible to get reasonable estimates for confTarget of 1 + if (confTarget == 1) confTarget = 2; - if ((unsigned int)confTarget > maxUsableEstimate) { - confTarget = maxUsableEstimate; - } + unsigned int maxUsableEstimate = MaxUsableEstimate(); + if ((unsigned int)confTarget > maxUsableEstimate) { + confTarget = maxUsableEstimate; + } + if (feeCalc) feeCalc->returnedTarget = confTarget; - 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 (confTarget <= 1) return CFeeRate(0); // error condition + + 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); + if (median < 0) return CFeeRate(0); // error condition return CFeeRate(median); } diff --git a/src/policy/fees.h b/src/policy/fees.h index 2029ce3744..6edaf28714 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -74,6 +74,8 @@ enum FeeEstimateHorizon { LONG_HALFLIFE = 2 }; +std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon); + /* Enumeration of reason for returned fee estimate */ enum class FeeReason { NONE, @@ -90,6 +92,15 @@ enum class FeeReason { std::string StringForFeeReason(FeeReason reason); +/* Used to determine type of fee estimation requested */ +enum class FeeEstimateMode { + UNSET, //! Use default settings based on other criteria + ECONOMICAL, //! Force estimateSmartFee to use non-conservative estimates + CONSERVATIVE, //! Force estimateSmartFee to use conservative estimates +}; + +bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode); + /* Used to return detailed information about a feerate bucket */ struct EstimatorBucket { @@ -197,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 = true) 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 @@ -214,6 +225,9 @@ public: /** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */ void FlushUnconfirmed(CTxMemPool& pool); + /** Calculation of highest target that estimates are tracked for */ + unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const; + private: unsigned int nBestSeenHeight; unsigned int firstRecordedHeight; @@ -270,7 +284,7 @@ private: public: /** Create new FeeFilterRounder */ - FeeFilterRounder(const CFeeRate& minIncrementalFee); + explicit FeeFilterRounder(const CFeeRate& minIncrementalFee); /** Quantize a minimum fee for privacy purpose before broadcast **/ CAmount round(CAmount currentMinFee); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 68b5d48fbf..56912d0375 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -7,28 +7,30 @@ #include "policy/policy.h" +#include "consensus/validation.h" #include "validation.h" #include "coins.h" #include "tinyformat.h" #include "util.h" #include "utilstrencodings.h" -#include <boost/foreach.hpp> CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { // "Dust" is defined in terms of dustRelayFee, // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees + // If you'd pay more in fees than the value of the output // to spend something, then we consider it dust. // A typical spendable non-segwit txout is 34 bytes big, and will // need a CTxIn of at least 148 bytes to spend: // so dust is a spendable txout less than - // 546*dustRelayFee/1000 (in satoshis). + // 182*dustRelayFee/1000 (in satoshis). + // 546 satoshis at the default rate of 3000 sat/kB. // A typical spendable segwit txout is 31 bytes big, and will // need a CTxIn of at least 67 bytes to spend: // so dust is a spendable txout less than - // 294*dustRelayFee/1000 (in satoshis). + // 98*dustRelayFee/1000 (in satoshis). + // 294 satoshis at the default rate of 3000 sat/kB. if (txout.scriptPubKey.IsUnspendable()) return 0; @@ -44,7 +46,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above } - return 3 * dustRelayFeeIn.GetFee(nSize); + return dustRelayFeeIn.GetFee(nSize); } bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) diff --git a/src/policy/policy.h b/src/policy/policy.h index 2c2ea9d5b8..c06820f84e 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -40,12 +40,12 @@ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100; static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80; /** The maximum size of a standard witnessScript */ static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600; -/** Min feerate for defining dust. Historically this has been the same as the +/** Min feerate for defining dust. Historically this has been based on the * minRelayTxFee, however changing the dust limit changes which transactions are * standard and should be done with care and ideally rarely. It makes sense to * only increase the dust limit after prior releases were already not creating * outputs below the new threshold */ -static const unsigned int DUST_RELAY_TX_FEE = 1000; +static const unsigned int DUST_RELAY_TX_FEE = 3000; /** * Standard script verification flags that standard transactions will comply * with. However scripts violating these flags may still be present in valid |