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)
186 for (
unsigned int i = 0; i < maxPeriods; i++) {
190 for (
unsigned int i = 0; i < maxPeriods; i++) {
203 for (
unsigned int i = 0; i <
unconfTxs.size(); i++) {
212 for (
unsigned int j = 0; j <
buckets.size(); j++) {
222 if (blocksToConfirm < 1)
224 int periodsToConfirm = (blocksToConfirm +
scale - 1)/
scale;
225 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
226 for (
size_t i = periodsToConfirm; i <=
confAvg.size(); i++) {
230 avg[bucketindex] += val;
235 for (
unsigned int j = 0; j <
buckets.size(); j++) {
236 for (
unsigned int i = 0; i <
confAvg.size(); i++)
238 for (
unsigned int i = 0; i <
failAvg.size(); i++)
247 double successBreakPoint,
bool requireGreater,
255 int periodTarget = (confTarget +
scale - 1)/
scale;
257 int maxbucketindex =
buckets.size() - 1;
263 unsigned int startbucket = requireGreater ? maxbucketindex : 0;
264 int step = requireGreater ? -1 : 1;
271 unsigned int curNearBucket = startbucket;
272 unsigned int bestNearBucket = startbucket;
273 unsigned int curFarBucket = startbucket;
274 unsigned int bestFarBucket = startbucket;
276 bool foundAnswer =
false;
278 bool newBucketRange =
true;
285 if (newBucketRange) {
287 newBucketRange =
false;
293 for (
unsigned int confct = confTarget; confct <
GetMaxConfirms(); confct++)
300 if (totalNum >= sufficientTxVal / (1 -
decay)) {
301 double curPct = nConf / (totalNum + failNum + extraNum);
304 if ((requireGreater && curPct < successBreakPoint) || (!requireGreater && curPct > successBreakPoint)) {
305 if (passing ==
true) {
307 unsigned int failMinBucket =
std::min(curNearBucket, curFarBucket);
308 unsigned int failMaxBucket =
std::max(curNearBucket, curFarBucket);
309 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
333 bestNearBucket = curNearBucket;
334 bestFarBucket = curFarBucket;
335 newBucketRange =
true;
347 unsigned int minBucket =
std::min(bestNearBucket, bestFarBucket);
348 unsigned int maxBucket =
std::max(bestNearBucket, bestFarBucket);
349 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
352 if (foundAnswer && txSum != 0) {
354 for (
unsigned int j = minBucket; j <= maxBucket; j++) {
363 passBucket.
start = minBucket ?
buckets[minBucket-1] : 0;
368 if (passing && !newBucketRange) {
369 unsigned int failMinBucket =
std::min(curNearBucket, curFarBucket);
370 unsigned int failMaxBucket =
std::max(curNearBucket, curFarBucket);
371 failBucket.
start = failMinBucket ?
buckets[failMinBucket - 1] : 0;
379 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",
380 confTarget, requireGreater ?
">" :
"<", 100.0 * successBreakPoint,
decay,
381 median, passBucket.
start, passBucket.
end,
390 result->
pass = passBucket;
391 result->
fail = failBucket;
413 size_t maxConfirms, maxPeriods;
416 if (nFileVersion >= 149900) {
418 if (decay <= 0 || decay >= 1) {
419 throw std::runtime_error(
"Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
425 if (avg.size() != numBuckets) {
426 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate average bucket count");
429 if (txCtAvg.size() != numBuckets) {
430 throw std::runtime_error(
"Corrupt estimates file. Mismatch in tx count bucket count");
433 maxPeriods = confAvg.size();
434 maxConfirms =
scale * maxPeriods;
436 if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) {
437 throw std::runtime_error(
"Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
439 for (
unsigned int i = 0; i < maxPeriods; i++) {
440 if (confAvg[i].
size() != numBuckets) {
441 throw std::runtime_error(
"Corrupt estimates file. Mismatch in feerate conf average bucket count");
445 if (nFileVersion >= 149900) {
447 if (maxPeriods != failAvg.size()) {
448 throw std::runtime_error(
"Corrupt estimates file. Mismatch in confirms tracked for failures");
450 for (
unsigned int i = 0; i < maxPeriods; i++) {
451 if (failAvg[i].
size() != numBuckets) {
452 throw std::runtime_error(
"Corrupt estimates file. Mismatch in one of failure average bucket counts");
456 failAvg.resize(confAvg.size());
457 for (
unsigned int i = 0; i <
failAvg.size(); i++) {
467 numBuckets, maxConfirms);
472 unsigned int bucketindex =
bucketMap.lower_bound(val)->second;
473 unsigned int blockIndex = nBlockHeight %
unconfTxs.size();
481 int blocksAgo = nBestSeenHeight - entryHeight;
482 if (nBestSeenHeight == 0)
489 if (blocksAgo >= (
int)
unconfTxs.size()) {
498 unsigned int blockIndex = entryHeight %
unconfTxs.size();
499 if (
unconfTxs[blockIndex][bucketindex] > 0) {
503 blockIndex, bucketindex);
506 if (!inBlock && (
unsigned int)blocksAgo >=
scale) {
507 unsigned int periodsAgo = blocksAgo /
scale;
508 for (
size_t i = 0; i < periodsAgo && i <
failAvg.size(); i++) {
521 LOCK(cs_feeEstimator);
522 std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
523 if (pos != mapMemPoolTxs.end()) {
524 feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
525 shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
526 longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
527 mapMemPoolTxs.erase(hash);
535 : nBestSeenHeight(0), firstRecordedHeight(0), historicalFirst(0), historicalBest(0), trackedTxs(0), untrackedTxs(0)
538 size_t bucketIndex = 0;
540 buckets.push_back(bucketBoundary);
543 buckets.push_back(INF_FEERATE);
556 size_t bucketIndex = 0;
558 buckets.push_back(bucketBoundary);
561 buckets.push_back(INF_FEERATE);
578 unsigned int txHeight = entry.
GetHeight();
596 if (!validFeeEstimate) {
606 unsigned int bucketIndex =
feeStats->
NewTx(txHeight, (
double)feeRate.GetFeePerK());
608 unsigned int bucketIndex2 =
shortStats->
NewTx(txHeight, (
double)feeRate.GetFeePerK());
609 assert(bucketIndex == bucketIndex2);
610 unsigned int bucketIndex3 =
longStats->
NewTx(txHeight, (
double)feeRate.GetFeePerK());
611 assert(bucketIndex == bucketIndex3);
624 int blocksToConfirm = nBlockHeight - entry->
GetHeight();
625 if (blocksToConfirm <= 0) {
642 std::vector<const CTxMemPoolEntry*>& entries)
669 unsigned int countedTxs = 0;
671 for (
const auto& entry : entries) {
682 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",
718 throw std::out_of_range(
"CBlockPolicyEstimator::estimateRawFee unknown FeeEstimateHorizon");
724 if (confTarget <= 0 || (
unsigned int)confTarget > stats->
GetMaxConfirms())
726 if (successThreshold > 1)
738 if (answerFoundAtTarget)
739 *answerFoundAtTarget = confTarget;
753 if (answerFoundAtTarget)
754 *answerFoundAtTarget = confTarget - 1;
758 if (minPoolFee > 0 && minPoolFee > median)
784 throw std::out_of_range(
"CBlockPolicyEstimator::HighestTargetTracked unknown FeeEstimateHorizon");
819 double estimate = -1;
820 if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
822 if (confTarget <= shortStats->GetMaxConfirms()) {
825 else if (confTarget <= feeStats->GetMaxConfirms()) {
831 if (checkShorterHorizon) {
836 if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
838 if (result) *result = tempResult;
843 if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
845 if (result) *result = tempResult;
858 double estimate = -1;
860 if (doubleTarget <= shortStats->GetMaxConfirms()) {
863 if (doubleTarget <= feeStats->GetMaxConfirms()) {
865 if (longEstimate > estimate) {
866 estimate = longEstimate;
867 if (result) *result = tempResult;
898 if (confTarget == 1) confTarget = 2;
901 if ((
unsigned int)confTarget > maxUsableEstimate) {
902 confTarget = maxUsableEstimate;
906 if (confTarget <= 1)
return CFeeRate(0);
921 feeCalc->
est = tempResult;
926 if (actualEst > median) {
929 feeCalc->
est = tempResult;
934 if (doubleEst > median) {
937 feeCalc->
est = tempResult;
942 if (conservative || median == -1) {
944 if (consEst > median) {
947 feeCalc->
est = tempResult;
959 if (answerFoundAtTarget)
960 *answerFoundAtTarget = confTarget;
974 fileout << CLIENT_VERSION;
987 catch (
const std::exception&) {
988 LogPrintf(
"CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
998 int nVersionRequired, nVersionThatWrote;
999 filein >> nVersionRequired >> nVersionThatWrote;
1000 if (nVersionRequired > CLIENT_VERSION)
1001 return error(
"CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file", nVersionRequired);
1005 unsigned int nFileBestSeenHeight;
1006 filein >> nFileBestSeenHeight;
1008 if (nVersionThatWrote < 149900) {
1012 filein >> tempDecay;
1013 if (tempDecay <= 0 || tempDecay >= 1)
1014 throw std::runtime_error(
"Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
1016 std::vector<double> tempBuckets;
1017 filein >> tempBuckets;
1018 size_t tempNum = tempBuckets.size();
1019 if (tempNum <= 1 || tempNum > 1000)
1020 throw std::runtime_error(
"Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
1022 std::map<double, unsigned int> tempMap;
1025 tempFeeStats->Read(filein, nVersionThatWrote, tempNum);
1029 for (
unsigned int i = 0; i < tempBuckets.size(); i++) {
1030 tempMap[tempBuckets[i]] = i;
1034 unsigned int nFileHistoricalFirst, nFileHistoricalBest;
1035 filein >> nFileHistoricalFirst >> nFileHistoricalBest;
1036 if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
1037 throw std::runtime_error(
"Corrupt estimates file. Historical block range for estimates is invalid");
1039 std::vector<double> fileBuckets;
1040 filein >> fileBuckets;
1041 size_t numBuckets = fileBuckets.size();
1042 if (numBuckets <= 1 || numBuckets > 1000)
1043 throw std::runtime_error(
"Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
1048 fileFeeStats->Read(filein, nVersionThatWrote, numBuckets);
1049 fileShortStats->Read(filein, nVersionThatWrote, numBuckets);
1050 fileLongStats->Read(filein, nVersionThatWrote, numBuckets);
1056 for (
unsigned int i = 0; i <
buckets.size(); i++) {
1073 catch (
const std::exception&
e) {
1074 LogPrintf(
"CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal): %s\n",e.what());
1082 std::vector<uint256> txids;
1085 for (
auto& txid : txids) {
1089 LogPrint(
BCLog::ESTIMATEFEE,
"Recorded %u unconfirmed txs from mempool in %gs\n",txids.size(), (endclear - startclear)*0.000001);
1096 for (
double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FILTER_FEERATE; bucketBoundary *= FEE_FILTER_SPACING) {
1097 feeset.insert(bucketBoundary);
1103 std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
1104 if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
static constexpr double MED_DECAY
Decay of .998 is a half-life of 144 blocks or about 1 day.
CAmount GetFeePerK() const
Return the fee in liu for a size of 1000 bytes.
CFeeRate GetMinFee(size_t sizelimit) const
The minimum fee to get into the mempool, which may itself not be enough for larger-sized transactions...
std::vector< std::vector< double > > failAvg
bool error(const char *fmt, const Args &...args)
unsigned int GetHeight() const
unsigned int GetMaxConfirms() const
Return the max number of confirms we're tracking.
Force estimateSmartFee to use non-conservative estimates.
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.
CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool &pool)
Estimate feerate needed to get be included in a block within confTarget 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.
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
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.
CFeeRate estimateFee(int confTarget) const
DEPRECATED.
assert(len-trim+(2 *lenIndices)<=WIDTH)
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...
double estimatePriority(int confTarget)
Return a priority estimate.
void Write(CAutoFile &fileout) const
Write state of estimation data to a file.
void resizeInMemoryCounters(size_t newbuckets)
void ClearCurrent(unsigned int nBlockHeight)
Roll the circular buffer for unconfirmed txs.
bool FeeModeFromString(const std::string &mode_string, FeeEstimateMode &fee_estimate_mode)
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
Helper for estimateSmartFee.
static constexpr double SUFFICIENT_TXS_SHORT
Require an avg of 0.5 tx when using short decay since there are fewer blocks considered.
std::string ToString() const
CTxMemPoolEntry stores data about the corresponding transaction, as well as data about all in-mempool...
TxConfirmStats * longStats
int64_t CAmount
Amount in lius (Can be negative)
std::string StringForFeeReason(FeeReason reason)
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 CAmount & GetFee() const
const std::map< double, unsigned int > & bucketMap
static constexpr unsigned int SHORT_SCALE
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
static constexpr double LONG_DECAY
Decay of .9995 is a half-life of 1008 blocks or about 1 week.
unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const
Calculation of highest target that estimates are tracked for.
std::vector< std::vector< double > > confAvg
unsigned int NewTx(unsigned int nBlockHeight, double val)
Record a new transaction entering the mempool.
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.
unsigned int historicalFirst
void FlushUnconfirmed(CTxMemPool &pool)
Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool...
const std::vector< double > & buckets
FeeFilterRounder(const CFeeRate &minIncrementalFee)
Create new FeeFilterRounder.
std::map< uint256, TxStatsInfo > mapMemPoolTxs
static constexpr unsigned int LONG_SCALE
bool Write(CAutoFile &fileout) const
Write estimation data to a file.
#define LogPrint(category,...)
std::vector< std::vector< int > > unconfTxs
unsigned int HistoricalBlockSpan() const
Number of blocks of recorded fee estimate data represented in saved data file.
static constexpr unsigned int MED_SCALE
static const unsigned int OLDEST_ESTIMATE_HISTORY
Historical estimates that are older than this aren't valid.
const CTransaction & GetTx() const
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
std::vector< int > oldUnconfTxs
uint8_t const size_t const size
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
bool Read(CAutoFile &filein)
Read estimation data from a file.
unsigned int historicalBest
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
Helper for estimateSmartFee.
static constexpr unsigned int LONG_BLOCK_PERIODS
Track confirm delays up to 1008 blocks for long horizon.
Fee rate in liu per kilobyte: CAmount / kB.
std::vector< double > buckets
static constexpr unsigned int SHORT_BLOCK_PERIODS
Track confirm delays up to 12 blocks for short horizon.
const uint256 & GetHash() const
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.
dev::WithExisting max(dev::WithExisting _a, dev::WithExisting _b)
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.
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...
std::vector< double > txCtAvg
void processBlock(unsigned int nBlockHeight, std::vector< const CTxMemPoolEntry * > &entries)
Process all the transactions that have been included in a block.
unsigned int untrackedTxs
void UpdateMovingAverages()
Update our estimates by decaying our historical moving average and updating with the data gathered fr...
double EstimateMedianVal(int confTarget, double sufficientTxVal, double minSuccess, bool requireGreater, unsigned int nBlockHeight, EstimationResult *result=nullptr) const
Calculate a feerate estimate.
Non-refcounted RAII wrapper for FILE*.
unsigned int BlockSpan() const
Number of blocks of data recorded while fee estimates have been running.
void processTransaction(const CTxMemPoolEntry &entry, bool validFeeEstimate)
Process a transaction accepted to the mempool.
unsigned int MaxUsableEstimate() const
Calculation of highest target that reasonable estimate can be provided for.
double estimateSmartPriority(int confTarget, int *answerFoundAtTarget, const CTxMemPool &pool)
Estimate priority needed to get be included in a block within confTarget blocks.