18 static constexpr
double INF_FEERATE = 1e99;
21 static const std::map<FeeEstimateHorizon, std::string> horizon_strings = {
26 auto horizon_string = horizon_strings.find(horizon);
28 if (horizon_string == horizon_strings.end())
return "unknown";
30 return horizon_string->second;
34 static const std::map<FeeReason, std::string> fee_reason_strings = {
46 auto reason_string = fee_reason_strings.find(reason);
48 if (reason_string == fee_reason_strings.end())
return "Unknown";
50 return reason_string->second;
54 static const std::map<std::string, FeeEstimateMode> fee_modes = {
59 auto mode = fee_modes.find(mode_string);
61 if (mode == fee_modes.end())
return false;
63 fee_estimate_mode = mode->second;
97 std::vector<double>
avg;
124 TxConfirmStats(
const std::vector<double>& defaultBuckets,
const std::map<double, unsigned int>& defaultBucketMap,
125 unsigned int maxPeriods,
double decay,
unsigned int scale);
136 void Record(
int blocksToConfirm,
double val);
139 unsigned int NewTx(
unsigned int nBlockHeight,
double val);
142 void removeTx(
unsigned int entryHeight,
unsigned int nBestSeenHeight,
143 unsigned int bucketIndex,
bool inBlock);
161 double minSuccess,
bool requireGreater,
unsigned int nBlockHeight,
174 void Read(
CAutoFile& filein,
int nFileVersion,
size_t numBuckets);
179 const std::map<double, unsigned int>& defaultBucketMap,
180 unsigned int maxPeriods,
double _decay,
unsigned int _scale)
184 assert(_scale != 0 &&
"_scale must be non-zero");
187 for (
unsigned int i = 0; i < maxPeriods; i++) {
191 for (
unsigned int i = 0; i < maxPeriods; i++) {
204 for (
unsigned int i = 0; i <
unconfTxs.size(); i++) {
213 for (
unsigned int j = 0; j <
buckets.size(); j++) {
223 if (blocksToConfirm < 1)
225 int periodsToConfirm = (blocksToConfirm +
scale - 1)/
scale;
226 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
227 for (
size_t i = periodsToConfirm; i <=
confAvg.size(); i++) {
231 avg[bucketindex] += val;
236 for (
unsigned int j = 0; j <
buckets.size(); j++) {
237 for (
unsigned int i = 0; i <
confAvg.size(); i++)
239 for (
unsigned int i = 0; i <
failAvg.size(); i++)
248 double successBreakPoint,
bool requireGreater,
256 int periodTarget = (confTarget +
scale - 1)/
scale;
258 int maxbucketindex =
buckets.size() - 1;
264 unsigned int startbucket = requireGreater ? maxbucketindex : 0;
265 int step = requireGreater ? -1 : 1;
272 unsigned int curNearBucket = startbucket;
273 unsigned int bestNearBucket = startbucket;
274 unsigned int curFarBucket = startbucket;
275 unsigned int bestFarBucket = startbucket;
277 bool foundAnswer =
false;
279 bool newBucketRange =
true;
285 for (
int bucket = startbucket; bucket >= 0 && bucket <= maxbucketindex; bucket += step) {
286 if (newBucketRange) {
287 curNearBucket = bucket;
288 newBucketRange =
false;
290 curFarBucket = bucket;
291 nConf +=
confAvg[periodTarget - 1][bucket];
293 failNum +=
failAvg[periodTarget - 1][bucket];
294 for (
unsigned int confct = confTarget; confct <
GetMaxConfirms(); confct++)
295 extraNum +=
unconfTxs[(nBlockHeight - confct)%bins][bucket];
301 if (totalNum >= sufficientTxVal / (1 -
decay)) {
302 double curPct = nConf / (totalNum + failNum + extraNum);
305 if ((requireGreater && curPct < successBreakPoint) || (!requireGreater && curPct > successBreakPoint)) {
306 if (passing ==
true) {
308 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
309 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
310 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
334 bestNearBucket = curNearBucket;
335 bestFarBucket = curFarBucket;
336 newBucketRange =
true;
348 unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
349 unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
350 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
353 if (foundAnswer && txSum != 0) {
355 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
364 passBucket.
start = minBucket ?
buckets[minBucket-1] : 0;
369 if (passing && !newBucketRange) {
370 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
371 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
372 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
380 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",
381 confTarget, requireGreater ?
">" :
"<", 100.0 * successBreakPoint,
decay,
382 median, passBucket.
start, passBucket.
end,
391 result->
pass = passBucket;
392 result->
fail = failBucket;
414 size_t maxConfirms, maxPeriods;
417 if (nFileVersion >= 149900) {
419 if (decay <= 0 || decay >= 1) {
420 throw std::runtime_error(
"Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
424 throw std::runtime_error(
"Corrupt estimates file. Scale must be non-zero");
429 if (avg.size() != numBuckets) {
430 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate average bucket count");
433 if (txCtAvg.size() != numBuckets) {
434 throw std::runtime_error(
"Corrupt estimates file. Mismatch in tx count bucket count");
437 maxPeriods = confAvg.size();
438 maxConfirms =
scale * maxPeriods;
440 if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) {
441 throw std::runtime_error(
"Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
443 for (
unsigned int i = 0; i < maxPeriods; i++) {
444 if (confAvg[i].size() != numBuckets) {
445 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate conf average bucket count");
449 if (nFileVersion >= 149900) {
451 if (maxPeriods != failAvg.size()) {
452 throw std::runtime_error(
"Corrupt estimates file. Mismatch in confirms tracked for failures");
454 for (
unsigned int i = 0; i < maxPeriods; i++) {
455 if (failAvg[i].size() != numBuckets) {
456 throw std::runtime_error(
"Corrupt estimates file. Mismatch in one of failure average bucket counts");
460 failAvg.resize(confAvg.size());
461 for (
unsigned int i = 0; i <
failAvg.size(); i++) {
471 numBuckets, maxConfirms);
476 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
477 unsigned int blockIndex = nBlockHeight %
unconfTxs.size();
485 int blocksAgo = nBestSeenHeight - entryHeight;
486 if (nBestSeenHeight == 0)
493 if (blocksAgo >= (
int)
unconfTxs.size()) {
502 unsigned int blockIndex = entryHeight %
unconfTxs.size();
503 if (
unconfTxs[blockIndex][bucketindex] > 0) {
507 blockIndex, bucketindex);
510 if (!inBlock && (
unsigned int)blocksAgo >=
scale) {
512 unsigned int periodsAgo = blocksAgo /
scale;
513 for (
size_t i = 0; i < periodsAgo && i <
failAvg.size(); i++) {
526 LOCK(cs_feeEstimator);
527 std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
528 if (pos != mapMemPoolTxs.end()) {
529 feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
530 shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
531 longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
532 mapMemPoolTxs.erase(hash);
540 : nBestSeenHeight(0), firstRecordedHeight(0), historicalFirst(0), historicalBest(0), trackedTxs(0), untrackedTxs(0)
543 size_t bucketIndex = 0;
545 buckets.push_back(bucketBoundary);
548 buckets.push_back(INF_FEERATE);
567 unsigned int txHeight = entry.
GetHeight();
585 if (!validFeeEstimate) {
595 unsigned int bucketIndex =
feeStats->
NewTx(txHeight, (
double)feeRate.GetFeePerK());
597 unsigned int bucketIndex2 =
shortStats->
NewTx(txHeight, (
double)feeRate.GetFeePerK());
598 assert(bucketIndex == bucketIndex2);
599 unsigned int bucketIndex3 =
longStats->
NewTx(txHeight, (
double)feeRate.GetFeePerK());
600 assert(bucketIndex == bucketIndex3);
613 int blocksToConfirm = nBlockHeight - entry->
GetHeight();
614 if (blocksToConfirm <= 0) {
631 std::vector<const CTxMemPoolEntry*>& entries)
658 unsigned int countedTxs = 0;
660 for (
const auto& entry : entries) {
671 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",
707 throw std::out_of_range(
"CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon");
713 if (confTarget <= 0 || (
unsigned int)confTarget > stats->
GetMaxConfirms())
715 if (successThreshold > 1)
739 throw std::out_of_range(
"CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon");
774 double estimate = -1;
775 if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
777 if (confTarget <= shortStats->GetMaxConfirms()) {
780 else if (confTarget <= feeStats->GetMaxConfirms()) {
786 if (checkShorterHorizon) {
791 if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
793 if (result) *result = tempResult;
798 if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
800 if (result) *result = tempResult;
813 double estimate = -1;
815 if (doubleTarget <= shortStats->GetMaxConfirms()) {
818 if (doubleTarget <= feeStats->GetMaxConfirms()) {
820 if (longEstimate > estimate) {
821 estimate = longEstimate;
822 if (result) *result = tempResult;
853 if (confTarget == 1) confTarget = 2;
856 if ((
unsigned int)confTarget > maxUsableEstimate) {
857 confTarget = maxUsableEstimate;
861 if (confTarget <= 1)
return CFeeRate(0);
863 assert(confTarget > 0);
876 feeCalc->
est = tempResult;
881 if (actualEst > median) {
884 feeCalc->
est = tempResult;
889 if (doubleEst > median) {
892 feeCalc->
est = tempResult;
897 if (conservative || median == -1) {
899 if (consEst > median) {
902 feeCalc->
est = tempResult;
919 fileout << CLIENT_VERSION;
932 catch (
const std::exception&) {
933 LogPrintf(
"CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
943 int nVersionRequired, nVersionThatWrote;
944 filein >> nVersionRequired >> nVersionThatWrote;
945 if (nVersionRequired > CLIENT_VERSION)
946 return error(
"CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
950 unsigned int nFileBestSeenHeight;
951 filein >> nFileBestSeenHeight;
953 if (nVersionThatWrote < 149900) {
958 if (tempDecay <= 0 || tempDecay >= 1)
959 throw std::runtime_error(
"Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
961 std::vector<double> tempBuckets;
962 filein >> tempBuckets;
963 size_t tempNum = tempBuckets.size();
964 if (tempNum <= 1 || tempNum > 1000)
965 throw std::runtime_error(
"Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
967 std::map<double, unsigned int> tempMap;
970 tempFeeStats->Read(filein, nVersionThatWrote, tempNum);
974 for (
unsigned int i = 0; i < tempBuckets.size(); i++) {
975 tempMap[tempBuckets[i]] = i;
979 unsigned int nFileHistoricalFirst, nFileHistoricalBest;
980 filein >> nFileHistoricalFirst >> nFileHistoricalBest;
981 if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
982 throw std::runtime_error(
"Corrupt estimates file. Historical block range for estimates is invalid");
984 std::vector<double> fileBuckets;
985 filein >> fileBuckets;
986 size_t numBuckets = fileBuckets.size();
987 if (numBuckets <= 1 || numBuckets > 1000)
988 throw std::runtime_error(
"Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
993 fileFeeStats->Read(filein, nVersionThatWrote, numBuckets);
994 fileShortStats->Read(filein, nVersionThatWrote, numBuckets);
995 fileLongStats->Read(filein, nVersionThatWrote, numBuckets);
1001 for (
unsigned int i = 0; i <
buckets.size(); i++) {
1018 catch (
const std::exception& e) {
1019 LogPrintf(
"CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal): %s\n",e.what());
1027 std::vector<uint256> txids;
1030 for (
auto& txid : txids) {
1034 LogPrint(
BCLog::ESTIMATEFEE,
"Recorded %u unconfirmed txs from mempool in %gs\n",txids.size(), (endclear - startclear)*0.000001);
1041 for (
double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FILTER_FEERATE; bucketBoundary *= FEE_FILTER_SPACING) {
1042 feeset.insert(bucketBoundary);
1048 std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
1049 if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
1052 return static_cast<CAmount>(*it);
1057 if (index+1 > static_cast<int>(confTargets.size())) {
1058 return confTargets.back();
1061 return confTargets[0];
1063 return confTargets[index];
1067 for (
unsigned int i = 0; i < confTargets.size(); i++) {
1068 if (confTargets[i] >= target) {
1072 return confTargets.size() - 1;
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.
std::vector< double > avg
We will instantiate an instance of this class to track transactions that were included in a block...
std::map< double, unsigned int > bucketMap
std::string StringForFeeReason(FeeReason reason)
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.
void queryHashes(std::vector< uint256 > &vtxid)
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...
TxConfirmStats * longStats
int64_t CAmount
Amount in corbies (Can be negative)
Force estimateSmartFee to use non-conservative estimates.
int getConfTargetForIndex(int index)
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...
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...
TxConfirmStats * feeStats
Classes to track historical data on transaction confirmations.
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
void FlushUnconfirmed(CTxMemPool &pool)
Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool...
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
Helper for estimateSmartFee.
const std::vector< double > & buckets
unsigned int BlockSpan() const
Number of blocks of data recorded while fee estimates have been running.
std::string ToString() const
int getIndexForConfTarget(int target)
FeeFilterRounder(const CFeeRate &minIncrementalFee)
Create new FeeFilterRounder.
std::map< uint256, TxStatsInfo > mapMemPoolTxs
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.
#define LogPrint(category,...)
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.
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
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.
Use default settings based on other criteria.
TxConfirmStats * shortStats
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::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.