15 static constexpr
double INF_FEERATE = 1e99;
18 static const std::map<FeeEstimateHorizon, std::string> horizon_strings = {
23 auto horizon_string = horizon_strings.find(horizon);
25 if (horizon_string == horizon_strings.end())
return "unknown";
27 return horizon_string->second;
31 static const std::map<FeeReason, std::string> fee_reason_strings = {
43 auto reason_string = fee_reason_strings.find(reason);
45 if (reason_string == fee_reason_strings.end())
return "Unknown";
47 return reason_string->second;
51 static const std::map<std::string, FeeEstimateMode> fee_modes = {
56 auto mode = fee_modes.find(mode_string);
58 if (mode == fee_modes.end())
return false;
60 fee_estimate_mode = mode->second;
94 std::vector<double>
avg;
121 TxConfirmStats(
const std::vector<double>& defaultBuckets,
const std::map<double, unsigned int>& defaultBucketMap,
122 unsigned int maxPeriods,
double decay,
unsigned int scale);
133 void Record(
int blocksToConfirm,
double val);
136 unsigned int NewTx(
unsigned int nBlockHeight,
double val);
139 void removeTx(
unsigned int entryHeight,
unsigned int nBestSeenHeight,
140 unsigned int bucketIndex,
bool inBlock);
158 double minSuccess,
bool requireGreater,
unsigned int nBlockHeight,
171 void Read(
CAutoFile& filein,
int nFileVersion,
size_t numBuckets);
176 const std::map<double, unsigned int>& defaultBucketMap,
177 unsigned int maxPeriods,
double _decay,
unsigned int _scale)
178 : buckets(defaultBuckets), bucketMap(defaultBucketMap)
181 assert(_scale != 0 &&
"_scale must be non-zero");
184 for (
unsigned int i = 0; i < maxPeriods; i++) {
188 for (
unsigned int i = 0; i < maxPeriods; i++) {
201 for (
unsigned int i = 0; i <
unconfTxs.size(); i++) {
210 for (
unsigned int j = 0; j <
buckets.size(); j++) {
220 if (blocksToConfirm < 1)
222 int periodsToConfirm = (blocksToConfirm +
scale - 1)/
scale;
223 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
224 for (
size_t i = periodsToConfirm; i <=
confAvg.size(); i++) {
228 avg[bucketindex] += val;
233 for (
unsigned int j = 0; j <
buckets.size(); j++) {
234 for (
unsigned int i = 0; i <
confAvg.size(); i++)
236 for (
unsigned int i = 0; i <
failAvg.size(); i++)
245 double successBreakPoint,
bool requireGreater,
253 int periodTarget = (confTarget +
scale - 1)/
scale;
255 int maxbucketindex =
buckets.size() - 1;
261 unsigned int startbucket = requireGreater ? maxbucketindex : 0;
262 int step = requireGreater ? -1 : 1;
269 unsigned int curNearBucket = startbucket;
270 unsigned int bestNearBucket = startbucket;
271 unsigned int curFarBucket = startbucket;
272 unsigned int bestFarBucket = startbucket;
274 bool foundAnswer =
false;
276 bool newBucketRange =
true;
282 for (
int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) {
283 if (newBucketRange) {
284 curNearBucket = bucket;
285 newBucketRange =
false;
287 curFarBucket = bucket;
288 nConf +=
confAvg[periodTarget - 1][bucket];
290 failNum +=
failAvg[periodTarget - 1][bucket];
291 for (
unsigned int confct = confTarget; confct <
GetMaxConfirms(); confct++)
292 extraNum +=
unconfTxs[(nBlockHeight - confct)%bins][bucket];
298 if (totalNum >= sufficientTxVal / (1 -
decay)) {
299 double curPct = nConf / (totalNum + failNum + extraNum);
302 if ((requireGreater && curPct < successBreakPoint) || (!requireGreater && curPct > successBreakPoint)) {
303 if (passing ==
true) {
305 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
306 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
307 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
331 bestNearBucket = curNearBucket;
332 bestFarBucket = curFarBucket;
333 newBucketRange =
true;
345 unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
346 unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
347 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
350 if (foundAnswer && txSum != 0) {
352 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
361 passBucket.
start = minBucket ?
buckets[minBucket-1] : 0;
366 if (passing && !newBucketRange) {
367 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
368 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
369 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
377 LogPrint(
BCLog::ESTIMATEFEE,
"FeeEst: %d %s%.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
378 confTarget, requireGreater ?
">" :
"<", 100.0 * successBreakPoint,
decay,
379 median, passBucket.
start, passBucket.
end,
388 result->
pass = passBucket;
389 result->
fail = failBucket;
411 size_t maxConfirms, maxPeriods;
415 if (decay <= 0 || decay >= 1) {
416 throw std::runtime_error(
"Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
420 throw std::runtime_error(
"Corrupt estimates file. Scale must be non-zero");
424 if (
avg.size() != numBuckets) {
425 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate average bucket count");
428 if (
txCtAvg.size() != numBuckets) {
429 throw std::runtime_error(
"Corrupt estimates file. Mismatch in tx count bucket count");
433 maxConfirms =
scale * maxPeriods;
435 if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) {
436 throw std::runtime_error(
"Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
438 for (
unsigned int i = 0; i < maxPeriods; i++) {
439 if (
confAvg[i].size() != numBuckets) {
440 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate conf average bucket count");
445 if (maxPeriods !=
failAvg.size()) {
446 throw std::runtime_error(
"Corrupt estimates file. Mismatch in confirms tracked for failures");
448 for (
unsigned int i = 0; i < maxPeriods; i++) {
449 if (
failAvg[i].size() != numBuckets) {
450 throw std::runtime_error(
"Corrupt estimates file. Mismatch in one of failure average bucket counts");
458 LogPrint(
BCLog::ESTIMATEFEE,
"Reading estimates: %u buckets counting confirms up to %u blocks\n",
459 numBuckets, maxConfirms);
464 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
465 unsigned int blockIndex = nBlockHeight %
unconfTxs.size();
473 int blocksAgo = nBestSeenHeight - entryHeight;
474 if (nBestSeenHeight == 0)
477 LogPrint(
BCLog::ESTIMATEFEE,
"Blockpolicy error, blocks ago is negative for mempool tx\n");
481 if (blocksAgo >= (
int)
unconfTxs.size()) {
485 LogPrint(
BCLog::ESTIMATEFEE,
"Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n",
490 unsigned int blockIndex = entryHeight %
unconfTxs.size();
491 if (
unconfTxs[blockIndex][bucketindex] > 0) {
494 LogPrint(
BCLog::ESTIMATEFEE,
"Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n",
495 blockIndex, bucketindex);
498 if (!inBlock && (
unsigned int)blocksAgo >=
scale) {
500 unsigned int periodsAgo = blocksAgo /
scale;
501 for (
size_t i = 0; i < periodsAgo && i <
failAvg.size(); i++) {
515 std::map<uint256, TxStatsInfo>::iterator pos =
mapMemPoolTxs.find(hash);
528 : nBestSeenHeight(0), firstRecordedHeight(0), historicalFirst(0), historicalBest(0), trackedTxs(0), untrackedTxs(0)
531 size_t bucketIndex = 0;
533 buckets.push_back(bucketBoundary);
536 buckets.push_back(INF_FEERATE);
552 unsigned int txHeight = entry.
GetHeight();
555 LogPrint(
BCLog::ESTIMATEFEE,
"Blockpolicy error mempool tx %s already being tracked\n",
570 if (!validFeeEstimate) {
580 unsigned int bucketIndex =
feeStats->NewTx(txHeight, (
double)feeRate.GetFeePerK());
582 unsigned int bucketIndex2 =
shortStats->NewTx(txHeight, (
double)feeRate.GetFeePerK());
583 assert(bucketIndex == bucketIndex2);
584 unsigned int bucketIndex3 =
longStats->NewTx(txHeight, (
double)feeRate.GetFeePerK());
585 assert(bucketIndex == bucketIndex3);
598 int blocksToConfirm = nBlockHeight - entry->
GetHeight();
599 if (blocksToConfirm <= 0) {
602 LogPrint(
BCLog::ESTIMATEFEE,
"Blockpolicy error Transaction had negative blocksToConfirm\n");
609 feeStats->Record(blocksToConfirm, (
double)feeRate.GetFeePerK());
610 shortStats->Record(blocksToConfirm, (
double)feeRate.GetFeePerK());
611 longStats->Record(blocksToConfirm, (
double)feeRate.GetFeePerK());
616 std::vector<const CTxMemPoolEntry*>& entries)
634 feeStats->ClearCurrent(nBlockHeight);
643 unsigned int countedTxs = 0;
645 for (
const auto& entry : entries) {
656 LogPrint(
BCLog::ESTIMATEFEE,
"Blockpolicy estimates updated by %u of %u block txs, since last block %u of %u tracked, mempool map size %u, max target %u from %s\n",
692 throw std::out_of_range(
"CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon");
698 if (confTarget <= 0 || (
unsigned int)confTarget > stats->
GetMaxConfirms())
700 if (successThreshold > 1)
724 throw std::out_of_range(
"CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon");
759 double estimate = -1;
760 if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
762 if (confTarget <= shortStats->GetMaxConfirms()) {
765 else if (confTarget <= feeStats->GetMaxConfirms()) {
771 if (checkShorterHorizon) {
774 if (confTarget >
feeStats->GetMaxConfirms()) {
776 if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
778 if (result) *result = tempResult;
781 if (confTarget >
shortStats->GetMaxConfirms()) {
783 if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
785 if (result) *result = tempResult;
798 double estimate = -1;
800 if (doubleTarget <= shortStats->GetMaxConfirms()) {
803 if (doubleTarget <= feeStats->GetMaxConfirms()) {
805 if (longEstimate > estimate) {
806 estimate = longEstimate;
807 if (result) *result = tempResult;
833 if (confTarget <= 0 || (
unsigned int)confTarget >
longStats->GetMaxConfirms()) {
838 if (confTarget == 1) confTarget = 2;
841 if ((
unsigned int)confTarget > maxUsableEstimate) {
842 confTarget = maxUsableEstimate;
846 if (confTarget <= 1)
return CFeeRate(0);
848 assert(confTarget > 0);
861 feeCalc->
est = tempResult;
866 if (actualEst > median) {
869 feeCalc->
est = tempResult;
874 if (doubleEst > median) {
877 feeCalc->
est = tempResult;
882 if (conservative || median == -1) {
884 if (consEst > median) {
887 feeCalc->
est = tempResult;
904 fileout << CLIENT_VERSION;
917 catch (
const std::exception&) {
918 LogPrintf(
"CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
928 int nVersionRequired, nVersionThatWrote;
929 filein >> nVersionRequired >> nVersionThatWrote;
930 if (nVersionRequired > CLIENT_VERSION)
931 return error(
"CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
935 unsigned int nFileBestSeenHeight;
936 filein >> nFileBestSeenHeight;
938 if (nVersionRequired < 149900) {
939 LogPrintf(
"%s: incompatible old fee estimation data (non-fatal). Version: %d\n", __func__, nVersionRequired);
941 unsigned int nFileHistoricalFirst, nFileHistoricalBest;
942 filein >> nFileHistoricalFirst >> nFileHistoricalBest;
943 if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
944 throw std::runtime_error(
"Corrupt estimates file. Historical block range for estimates is invalid");
946 std::vector<double> fileBuckets;
947 filein >> fileBuckets;
948 size_t numBuckets = fileBuckets.size();
949 if (numBuckets <= 1 || numBuckets > 1000)
950 throw std::runtime_error(
"Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
955 fileFeeStats->Read(filein, nVersionThatWrote, numBuckets);
956 fileShortStats->Read(filein, nVersionThatWrote, numBuckets);
957 fileLongStats->Read(filein, nVersionThatWrote, numBuckets);
963 for (
unsigned int i = 0; i <
buckets.size(); i++) {
977 catch (
const std::exception& e) {
978 LogPrintf(
"CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal): %s\n",e.what());
994 LogPrint(
BCLog::ESTIMATEFEE,
"Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001);
1002 feeset.insert(bucketBoundary);
1008 std::set<double>::iterator it =
feeset.lower_bound(currentMinFee);
1012 return static_cast<CAmount>(*it);
static constexpr double MED_DECAY
Decay of .998 is a half-life of 144 blocks or about 1 day.
std::vector< std::vector< double > > failAvg
bool FeeModeFromString(const std::string &mode_string, FeeEstimateMode &fee_estimate_mode)
CCriticalSection cs_feeEstimator
static constexpr double MAX_BUCKET_FEERATE
static constexpr double HALF_SUCCESS_PCT
Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks.
unsigned int firstRecordedHeight
static constexpr unsigned int MED_BLOCK_PERIODS
Track confirm delays up to 48 blocks for medium horizon.
bool removeTx(uint256 hash, bool inBlock)
Remove a transaction from the mempool tracking stats.
CBlockPolicyEstimator()
Create new BlockPolicyEstimator and initialize stats tracking classes with default values...
TxConfirmStats(const std::vector< double > &defaultBuckets, const std::map< double, unsigned int > &defaultBucketMap, unsigned int maxPeriods, double decay, unsigned int scale)
Create new TxConfirmStats.
bool Write(CAutoFile &fileout) const
Write estimation data to a file.
void FlushUnconfirmed()
Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool...
std::vector< double > avg
static constexpr double MAX_FILTER_FEERATE
We will instantiate an instance of this class to track transactions that were included in a block...
std::map< double, unsigned int > bucketMap
std::unique_ptr< TxConfirmStats > shortStats
std::string StringForFeeReason(FeeReason reason)
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
std::unique_ptr< TxConfirmStats > longStats
static constexpr double DOUBLE_SUCCESS_PCT
Require greater than 95% of X feerate transactions to be confirmed within 2 * Y blocks.
static constexpr double FEE_SPACING
Spacing of FeeRate buckets We have to lump transactions into buckets based on feerate, but we want to be able to give accurate estimates over a large range of potential feerates Therefore it makes sense to exponentially space the buckets.
unsigned int nBestSeenHeight
void Record(int blocksToConfirm, double val)
Record a new transaction data point in the current block stats.
static constexpr double MIN_BUCKET_FEERATE
Minimum and Maximum values for tracking feerates The MIN_BUCKET_FEERATE should just be set to the low...
void resizeInMemoryCounters(size_t newbuckets)
unsigned int MaxUsableEstimate() const
Calculation of highest target that reasonable estimate can be provided for.
void ClearCurrent(unsigned int nBlockHeight)
Roll the circular buffer for unconfirmed txs.
static constexpr double SUFFICIENT_TXS_SHORT
Require an avg of 0.5 tx when using short decay since there are fewer blocks considered.
CTxMemPoolEntry stores data about the corresponding transaction, as well as data about all in-mempool...
static constexpr double FEE_FILTER_SPACING
FEE_FILTER_SPACING is just used to provide some quantization of fee filter results.
int64_t CAmount
Amount in satoshis (Can be negative)
Force estimateSmartFee to use conservative estimates.
static constexpr double SUCCESS_PCT
Require greater than 85% of X feerate transactions to be confirmed within Y blocks.
void Read(CAutoFile &filein, int nFileVersion, size_t numBuckets)
Read saved state of estimation data from a file and replace all internal data structures and variable...
std::unique_ptr< TxConfirmStats > feeStats
Classes to track historical data on transaction confirmations.
const std::map< double, unsigned int > & bucketMap
static constexpr unsigned int SHORT_SCALE
static constexpr double LONG_DECAY
Decay of .9995 is a half-life of 1008 blocks or about 1 week.
std::vector< std::vector< double > > confAvg
unsigned int NewTx(unsigned int nBlockHeight, double val)
Record a new transaction entering the mempool.
unsigned int GetHeight() const
unsigned int HistoricalBlockSpan() const
Number of blocks of recorded fee estimate data represented in saved data file.
CFeeRate estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult *result=nullptr) const
Return a specific fee estimate calculation with a given success threshold and time horizon...
void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketIndex, bool inBlock)
Remove a transaction from mempool tracking stats.
const uint256 & GetHash() const
const CAmount & GetFee() const
CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
Estimate feerate needed to get be included in a block within confTarget blocks.
unsigned int historicalFirst
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
Helper for estimateSmartFee.
const std::vector< double > & buckets
Use default settings based on other criteria.
uint32_t rand32()
Generate a random 32-bit integer.
unsigned int BlockSpan() const
Number of blocks of data recorded while fee estimates have been running.
std::string ToString() const
FeeFilterRounder(const CFeeRate &minIncrementalFee)
Create new FeeFilterRounder.
std::map< uint256, TxStatsInfo > mapMemPoolTxs
FastRandomContext insecure_rand
CFeeRate estimateFee(int confTarget) const
DEPRECATED.
static constexpr unsigned int LONG_SCALE
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
Helper for estimateSmartFee.
std::vector< std::vector< int > > unconfTxs
unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const
Calculation of highest target that estimates are tracked for.
static constexpr unsigned int MED_SCALE
static const unsigned int OLDEST_ESTIMATE_HISTORY
Historical estimates that are older than this aren't valid.
std::vector< int > oldUnconfTxs
void processBlock(unsigned int nBlockHeight, std::vector< const CTxMemPoolEntry *> &entries)
Process all the transactions that have been included in a block.
const CTransaction & GetTx() const
bool Read(CAutoFile &filein)
Read estimation data from a file.
unsigned int historicalBest
unsigned int GetMaxConfirms() const
Return the max number of confirms we're tracking.
static constexpr unsigned int LONG_BLOCK_PERIODS
Track confirm delays up to 1008 blocks for long horizon.
Fee rate in satoshis per kilobyte: CAmount / kB.
bool error(const char *fmt, const Args &... args)
std::vector< double > buckets
static constexpr unsigned int SHORT_BLOCK_PERIODS
Track confirm delays up to 12 blocks for short horizon.
static constexpr double SUFFICIENT_FEETXS
Require an avg of 0.1 tx in the combined feerate bucket per block to have stat significance.
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry *entry)
Process a transaction confirmed in a block.
Force estimateSmartFee to use non-conservative estimates.
CAmount round(CAmount currentMinFee)
Quantize a minimum fee for privacy purpose before broadcast.
static constexpr double SHORT_DECAY
Decay of .962 is a half-life of 18 blocks or about 3 hours.
double EstimateMedianVal(int confTarget, double sufficientTxVal, double minSuccess, bool requireGreater, unsigned int nBlockHeight, EstimationResult *result=nullptr) const
Calculate a feerate estimate.
void Write(CAutoFile &fileout) const
Write state of estimation data to a file.
std::set< double > feeset
std::vector< double > txCtAvg
unsigned int untrackedTxs
void UpdateMovingAverages()
Update our estimates by decaying our historical moving average and updating with the data gathered fr...
CAmount GetFeePerK() const
Return the fee in satoshis for a size of 1000 bytes.
Non-refcounted RAII wrapper for FILE*.
void processTransaction(const CTxMemPoolEntry &entry, bool validFeeEstimate)
Process a transaction accepted to the mempool.