16 static const std::map<FeeEstimateHorizon, std::string> horizon_strings = {
21 auto horizon_string = horizon_strings.find(horizon);
23 if (horizon_string == horizon_strings.end())
return "unknown";
25 return horizon_string->second;
85 TxConfirmStats(
const std::vector<double>& defaultBuckets,
const std::map<double, unsigned int>& defaultBucketMap,
86 unsigned int maxPeriods,
double decay,
unsigned int scale);
97 void Record(
int blocksToConfirm,
double val);
100 unsigned int NewTx(
unsigned int nBlockHeight,
double val);
103 void removeTx(
unsigned int entryHeight,
unsigned int nBestSeenHeight,
104 unsigned int bucketIndex,
bool inBlock);
120 double minSuccess,
unsigned int nBlockHeight,
133 void Read(
CAutoFile& filein,
int nFileVersion,
size_t numBuckets);
138 const std::map<double, unsigned int>& defaultBucketMap,
139 unsigned int maxPeriods,
double _decay,
unsigned int _scale)
140 : buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
142 assert(_scale != 0 &&
"_scale must be non-zero");
145 for (
unsigned int i = 0; i < maxPeriods; i++) {
159 for (
unsigned int i = 0; i <
unconfTxs.size(); i++) {
168 for (
unsigned int j = 0; j <
buckets.size(); j++) {
178 if (blocksToConfirm < 1)
180 int periodsToConfirm = (blocksToConfirm +
scale - 1) /
scale;
181 unsigned int bucketindex =
bucketMap.lower_bound(feerate)->second;
182 for (
size_t i = periodsToConfirm; i <=
confAvg.size(); i++) {
192 for (
unsigned int j = 0; j <
buckets.size(); j++) {
193 for (
unsigned int i = 0; i <
confAvg.size(); i++) {
204 double successBreakPoint,
unsigned int nBlockHeight,
212 const int periodTarget = (confTarget +
scale - 1) /
scale;
213 const int maxbucketindex =
buckets.size() - 1;
220 unsigned int curNearBucket = maxbucketindex;
221 unsigned int bestNearBucket = maxbucketindex;
222 unsigned int curFarBucket = maxbucketindex;
223 unsigned int bestFarBucket = maxbucketindex;
225 bool foundAnswer =
false;
227 bool newBucketRange =
true;
233 for (
int bucket = maxbucketindex; bucket >= 0; --bucket) {
234 if (newBucketRange) {
235 curNearBucket = bucket;
236 newBucketRange =
false;
238 curFarBucket = bucket;
239 nConf +=
confAvg[periodTarget - 1][bucket];
241 failNum +=
failAvg[periodTarget - 1][bucket];
242 for (
unsigned int confct = confTarget; confct <
GetMaxConfirms(); confct++)
243 extraNum +=
unconfTxs[(nBlockHeight - confct) % bins][bucket];
249 if (totalNum >= sufficientTxVal / (1 -
decay)) {
250 double curPct = nConf / (totalNum + failNum + extraNum);
253 if (curPct < successBreakPoint) {
254 if (passing ==
true) {
256 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
257 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
258 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
282 bestNearBucket = curNearBucket;
283 bestFarBucket = curFarBucket;
284 newBucketRange =
true;
296 unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
297 unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
298 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
301 if (foundAnswer && txSum != 0) {
303 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
312 passBucket.
start = minBucket ?
buckets[minBucket-1] : 0;
317 if (passing && !newBucketRange) {
318 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
319 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
320 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
328 float passed_within_target_perc = 0.0;
329 float failed_within_target_perc = 0.0;
337 LogPrint(
BCLog::ESTIMATEFEE,
"FeeEst: %d > %.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",
338 confTarget, 100.0 * successBreakPoint,
decay,
339 median, passBucket.
start, passBucket.
end,
340 passed_within_target_perc,
343 failed_within_target_perc,
348 result->
pass = passBucket;
349 result->
fail = failBucket;
371 size_t maxConfirms, maxPeriods;
375 if (decay <= 0 || decay >= 1) {
376 throw std::runtime_error(
"Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
380 throw std::runtime_error(
"Corrupt estimates file. Scale must be non-zero");
385 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate average bucket count");
388 if (
txCtAvg.size() != numBuckets) {
389 throw std::runtime_error(
"Corrupt estimates file. Mismatch in tx count bucket count");
393 maxConfirms =
scale * maxPeriods;
395 if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) {
396 throw std::runtime_error(
"Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
398 for (
unsigned int i = 0; i < maxPeriods; i++) {
399 if (
confAvg[i].size() != numBuckets) {
400 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate conf average bucket count");
405 if (maxPeriods !=
failAvg.size()) {
406 throw std::runtime_error(
"Corrupt estimates file. Mismatch in confirms tracked for failures");
408 for (
unsigned int i = 0; i < maxPeriods; i++) {
409 if (
failAvg[i].size() != numBuckets) {
410 throw std::runtime_error(
"Corrupt estimates file. Mismatch in one of failure average bucket counts");
419 numBuckets, maxConfirms);
424 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
425 unsigned int blockIndex = nBlockHeight %
unconfTxs.size();
433 int blocksAgo = nBestSeenHeight - entryHeight;
434 if (nBestSeenHeight == 0)
441 if (blocksAgo >= (
int)
unconfTxs.size()) {
450 unsigned int blockIndex = entryHeight %
unconfTxs.size();
451 if (
unconfTxs[blockIndex][bucketindex] > 0) {
455 blockIndex, bucketindex);
458 if (!inBlock && (
unsigned int)blocksAgo >=
scale) {
460 unsigned int periodsAgo = blocksAgo /
scale;
461 for (
size_t i = 0; i < periodsAgo && i <
failAvg.size(); i++) {
475 std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
476 if (pos != mapMemPoolTxs.end()) {
477 feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
478 shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
479 longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
480 mapMemPoolTxs.erase(hash);
488 : nBestSeenHeight(0), firstRecordedHeight(0), historicalFirst(0), historicalBest(0), trackedTxs(0), untrackedTxs(0)
491 size_t bucketIndex = 0;
493 buckets.push_back(bucketBoundary);
494 bucketMap[bucketBoundary] = bucketIndex;
498 assert(bucketMap.size() == buckets.size());
512 unsigned int txHeight = entry.
GetHeight();
514 if (mapMemPoolTxs.count(hash)) {
520 if (txHeight != nBestSeenHeight) {
530 if (!validFeeEstimate) {
539 mapMemPoolTxs[hash].blockHeight = txHeight;
540 unsigned int bucketIndex = feeStats->NewTx(txHeight, (
double)feeRate.GetFeePerK());
541 mapMemPoolTxs[hash].bucketIndex = bucketIndex;
542 unsigned int bucketIndex2 = shortStats->NewTx(txHeight, (
double)feeRate.GetFeePerK());
543 assert(bucketIndex == bucketIndex2);
544 unsigned int bucketIndex3 = longStats->NewTx(txHeight, (
double)feeRate.GetFeePerK());
545 assert(bucketIndex == bucketIndex3);
558 int blocksToConfirm = nBlockHeight - entry->
GetHeight();
559 if (blocksToConfirm <= 0) {
569 feeStats->Record(blocksToConfirm, (
double)feeRate.GetFeePerK());
570 shortStats->Record(blocksToConfirm, (
double)feeRate.GetFeePerK());
571 longStats->Record(blocksToConfirm, (
double)feeRate.GetFeePerK());
576 std::vector<const CTxMemPoolEntry*>& entries)
579 if (nBlockHeight <= nBestSeenHeight) {
591 nBestSeenHeight = nBlockHeight;
594 feeStats->ClearCurrent(nBlockHeight);
595 shortStats->ClearCurrent(nBlockHeight);
596 longStats->ClearCurrent(nBlockHeight);
599 feeStats->UpdateMovingAverages();
600 shortStats->UpdateMovingAverages();
601 longStats->UpdateMovingAverages();
603 unsigned int countedTxs = 0;
605 for (
const auto& entry : entries) {
610 if (firstRecordedHeight == 0 && countedTxs > 0) {
611 firstRecordedHeight = nBestSeenHeight;
616 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",
617 countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
639 stats = shortStats.get();
644 stats = feeStats.get();
648 stats = longStats.get();
652 throw std::out_of_range(
"CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon");
658 if (confTarget <= 0 || (
unsigned int)confTarget > stats->
GetMaxConfirms())
660 if (successThreshold > 1)
663 double median = stats->
EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
676 return shortStats->GetMaxConfirms();
679 return feeStats->GetMaxConfirms();
682 return longStats->GetMaxConfirms();
685 throw std::out_of_range(
"CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon");
692 if (firstRecordedHeight == 0)
return 0;
693 assert(nBestSeenHeight >= firstRecordedHeight);
695 return nBestSeenHeight - firstRecordedHeight;
700 if (historicalFirst == 0)
return 0;
701 assert(historicalBest >= historicalFirst);
705 return historicalBest - historicalFirst;
720 double estimate = -1;
721 if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
723 if (confTarget <= shortStats->GetMaxConfirms()) {
724 estimate = shortStats->EstimateMedianVal(confTarget,
SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
726 else if (confTarget <= feeStats->GetMaxConfirms()) {
727 estimate = feeStats->EstimateMedianVal(confTarget,
SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
730 estimate = longStats->EstimateMedianVal(confTarget,
SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
732 if (checkShorterHorizon) {
735 if (confTarget > feeStats->GetMaxConfirms()) {
736 double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(),
SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
737 if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
739 if (result) *result = tempResult;
742 if (confTarget > shortStats->GetMaxConfirms()) {
743 double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(),
SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
744 if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
746 if (result) *result = tempResult;
759 double estimate = -1;
761 if (doubleTarget <= shortStats->GetMaxConfirms()) {
764 if (doubleTarget <= feeStats->GetMaxConfirms()) {
766 if (longEstimate > estimate) {
767 estimate = longEstimate;
768 if (result) *result = tempResult;
794 if (confTarget <= 0 || (
unsigned int)confTarget > longStats->GetMaxConfirms()) {
799 if (confTarget == 1) confTarget = 2;
802 if ((
unsigned int)confTarget > maxUsableEstimate) {
803 confTarget = maxUsableEstimate;
807 if (confTarget <= 1)
return CFeeRate(0);
809 assert(confTarget > 0);
822 feeCalc->
est = tempResult;
827 if (actualEst > median) {
830 feeCalc->
est = tempResult;
835 if (doubleEst > median) {
838 feeCalc->
est = tempResult;
843 if (conservative || median == -1) {
845 if (consEst > median) {
848 feeCalc->
est = tempResult;
866 fileout << nBestSeenHeight;
868 fileout << firstRecordedHeight << nBestSeenHeight;
871 fileout << historicalFirst << historicalBest;
874 feeStats->Write(fileout);
875 shortStats->Write(fileout);
876 longStats->Write(fileout);
878 catch (
const std::exception&) {
879 LogPrintf(
"CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
889 int nVersionRequired, nVersionThatWrote;
890 filein >> nVersionRequired >> nVersionThatWrote;
892 return error(
"CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
896 unsigned int nFileBestSeenHeight;
897 filein >> nFileBestSeenHeight;
899 if (nVersionRequired < 149900) {
900 LogPrintf(
"%s: incompatible old fee estimation data (non-fatal). Version: %d\n", __func__, nVersionRequired);
902 unsigned int nFileHistoricalFirst, nFileHistoricalBest;
903 filein >> nFileHistoricalFirst >> nFileHistoricalBest;
904 if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
905 throw std::runtime_error(
"Corrupt estimates file. Historical block range for estimates is invalid");
907 std::vector<double> fileBuckets;
908 filein >> fileBuckets;
909 size_t numBuckets = fileBuckets.size();
910 if (numBuckets <= 1 || numBuckets > 1000)
911 throw std::runtime_error(
"Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
916 fileFeeStats->Read(filein, nVersionThatWrote, numBuckets);
917 fileShortStats->Read(filein, nVersionThatWrote, numBuckets);
918 fileLongStats->Read(filein, nVersionThatWrote, numBuckets);
922 buckets = fileBuckets;
924 for (
unsigned int i = 0; i < buckets.size(); i++) {
925 bucketMap[buckets[i]] = i;
929 feeStats = std::move(fileFeeStats);
930 shortStats = std::move(fileShortStats);
931 longStats = std::move(fileLongStats);
933 nBestSeenHeight = nFileBestSeenHeight;
934 historicalFirst = nFileHistoricalFirst;
935 historicalBest = nFileHistoricalBest;
938 catch (
const std::exception& e) {
939 LogPrintf(
"CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal): %s\n",e.what());
948 size_t num_entries = mapMemPoolTxs.size();
950 while (!mapMemPoolTxs.empty()) {
951 auto mi = mapMemPoolTxs.begin();
955 LogPrint(
BCLog::ESTIMATEFEE,
"Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001);
963 feeset.insert(bucketBoundary);
969 std::set<double>::iterator
it =
feeset.lower_bound(currentMinFee);
973 return static_cast<CAmount>(*it);
static constexpr double MED_DECAY
Decay of .9952 is a half-life of 144 blocks or about 1 day.
std::vector< std::vector< double > > failAvg
std::deque< CInv >::iterator it
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry *entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Process a transaction confirmed in a block.
#define LogPrint(category,...)
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.
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.
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Helper for estimateSmartFee.
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...
unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Number of blocks of recorded fee estimate data represented in saved data file.
static constexpr double MAX_FILTER_FEERATE
We will instantiate an instance of this class to track transactions that were included in a block...
static void LogPrintf(const char *fmt, const Args &... args)
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
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 MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Calculation of highest target that reasonable estimate can be provided for.
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)
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)
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...
double EstimateMedianVal(int confTarget, double sufficientTxVal, double minSuccess, unsigned int nBlockHeight, EstimationResult *result=nullptr) const
Calculate a feerate estimate.
const std::map< double, unsigned int > & bucketMap
static constexpr unsigned int SHORT_SCALE
static constexpr double LONG_DECAY
Decay of .99931 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
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.
const std::vector< double > & buckets
unsigned int BlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Number of blocks of data recorded while fee estimates have been running.
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Helper for estimateSmartFee.
std::string ToString() const
FeeFilterRounder(const CFeeRate &minIncrementalFee)
Create new FeeFilterRounder.
FastRandomContext insecure_rand
CFeeRate estimateFee(int confTarget) const
DEPRECATED.
static constexpr unsigned int LONG_SCALE
uint32_t rand32() noexcept
Generate a random 32-bit integer.
int64_t GetTimeMicros()
Returns the system time (not mockable)
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 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.
RecursiveMutex m_cs_fee_estimator
static constexpr unsigned int SHORT_BLOCK_PERIODS
Track confirm delays up to 12 blocks for short horizon.
static constexpr double INF_FEERATE
static constexpr double SUFFICIENT_FEETXS
Require an avg of 0.1 tx in the combined feerate bucket per block to have stat significance.
std::vector< double > m_feerate_avg
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.
void Write(CAutoFile &fileout) const
Write state of estimation data to a file.
std::set< double > feeset
std::vector< double > txCtAvg
void UpdateMovingAverages()
Update our estimates by decaying our historical moving average and updating with the data gathered fr...
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
bool error(const char *fmt, const Args &... args)
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.