Bitcoin Core 31.99.0
P2P Digital Currency
block_policy_estimator.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-present The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
7
8#include <common/system.h>
9#include <consensus/amount.h>
11#include <policy/feerate.h>
13#include <random.h>
14#include <serialize.h>
15#include <streams.h>
16#include <sync.h>
17#include <tinyformat.h>
18#include <uint256.h>
19#include <util/fs.h>
20#include <util/log.h>
21#include <util/serfloat.h>
22#include <util/syserror.h>
23#include <util/time.h>
24
25#include <algorithm>
26#include <cassert>
27#include <chrono>
28#include <cmath>
29#include <cstddef>
30#include <cstdint>
31#include <exception>
32#include <stdexcept>
33#include <system_error>
34#include <utility>
35
36// The current format written, and the version required to read. Must be
37// increased to at least 309900+1 on the next breaking change.
38constexpr int CURRENT_FEES_FILE_VERSION{309900};
39
40static constexpr double INF_FEERATE = 1e99;
41
43{
44 switch (horizon) {
45 case FeeEstimateHorizon::SHORT_HALFLIFE: return "short";
46 case FeeEstimateHorizon::MED_HALFLIFE: return "medium";
47 case FeeEstimateHorizon::LONG_HALFLIFE: return "long";
48 } // no default case, so the compiler can warn about missing cases
49 assert(false);
50}
51
53{
54 switch (reason) {
56 return "None";
58 return "Half Target 60% Threshold";
60 return "Target 85% Threshold";
62 return "Double Target 95% Threshold";
64 return "Conservative Double Target longer horizon";
65 } // no default case, so the compiler can warn about missing cases
66 assert(false);
67}
68
69namespace {
70
71struct EncodedDoubleFormatter
72{
73 template<typename Stream> void Ser(Stream &s, double v)
74 {
75 s << EncodeDouble(v);
76 }
77
78 template<typename Stream> void Unser(Stream& s, double& v)
79 {
80 uint64_t encoded;
81 s >> encoded;
82 v = DecodeDouble(encoded);
83 }
84};
85
86} // namespace
87
97{
98private:
99 //Define the buckets we will group transactions into
100 const std::vector<double>& buckets; // The upper-bound of the range for the bucket (inclusive)
101 const std::map<double, unsigned int>& bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
102
103 // For each bucket X:
104 // Count the total # of txs in each bucket
105 // Track the historical moving average of this total over blocks
106 std::vector<double> txCtAvg;
107
108 // Count the total # of txs confirmed within Y blocks in each bucket
109 // Track the historical moving average of these totals over blocks
110 std::vector<std::vector<double>> confAvg; // confAvg[Y][X]
111
112 // Track moving avg of txs which have been evicted from the mempool
113 // after failing to be confirmed within Y blocks
114 std::vector<std::vector<double>> failAvg; // failAvg[Y][X]
115
116 // Sum the total feerate of all tx's in each bucket
117 // Track the historical moving average of this total over blocks
118 std::vector<double> m_feerate_avg;
119
120 // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
121 // Combine the total value with the tx counts to calculate the avg feerate per bucket
122
123 double decay;
124
125 // Resolution (# of blocks) with which confirmations are tracked
126 unsigned int scale;
127
128 // Mempool counts of outstanding transactions
129 // For each bucket X, track the number of transactions in the mempool
130 // that are unconfirmed for each possible confirmation value Y
131 std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
132 // transactions still unconfirmed after GetMaxConfirms for each bucket
133 std::vector<int> oldUnconfTxs;
134
135 void resizeInMemoryCounters(size_t newbuckets);
136
137public:
145 TxConfirmStats(const std::vector<double>& defaultBuckets, const std::map<double, unsigned int>& defaultBucketMap,
146 unsigned int maxPeriods, double decay, unsigned int scale);
147
149 void ClearCurrent(unsigned int nBlockHeight);
150
157 void Record(int blocksToConfirm, double val);
158
160 unsigned int NewTx(unsigned int nBlockHeight, double val);
161
163 void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
164 unsigned int bucketIndex, bool inBlock);
165
169
179 double EstimateMedianVal(int confTarget, double sufficientTxVal,
180 double minSuccess, unsigned int nBlockHeight,
181 EstimationResult *result = nullptr) const;
182
184 unsigned int GetMaxConfirms() const { return scale * confAvg.size(); }
185
187 void Write(AutoFile& fileout) const;
188
193 void Read(AutoFile& filein, size_t numBuckets);
194};
195
196
197TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
198 const std::map<double, unsigned int>& defaultBucketMap,
199 unsigned int maxPeriods, double _decay, unsigned int _scale)
200 : buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
201{
202 assert(_scale != 0 && "_scale must be non-zero");
203 confAvg.resize(maxPeriods);
204 failAvg.resize(maxPeriods);
205 for (unsigned int i = 0; i < maxPeriods; i++) {
206 confAvg[i].resize(buckets.size());
207 failAvg[i].resize(buckets.size());
208 }
209
210 txCtAvg.resize(buckets.size());
211 m_feerate_avg.resize(buckets.size());
212
214}
215
217 // newbuckets must be passed in because the buckets referred to during Read have not been updated yet.
218 unconfTxs.resize(GetMaxConfirms());
219 for (unsigned int i = 0; i < unconfTxs.size(); i++) {
220 unconfTxs[i].resize(newbuckets);
221 }
222 oldUnconfTxs.resize(newbuckets);
223}
224
225// Roll the unconfirmed txs circular buffer
226void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight)
227{
228 for (unsigned int j = 0; j < buckets.size(); j++) {
229 oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j];
230 unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0;
231 }
232}
233
234
235void TxConfirmStats::Record(int blocksToConfirm, double feerate)
236{
237 // blocksToConfirm is 1-based
238 if (blocksToConfirm < 1)
239 return;
240 int periodsToConfirm = (blocksToConfirm + scale - 1) / scale;
241 unsigned int bucketindex = bucketMap.lower_bound(feerate)->second;
242 for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) {
243 confAvg[i - 1][bucketindex]++;
244 }
245 txCtAvg[bucketindex]++;
246 m_feerate_avg[bucketindex] += feerate;
247}
248
250{
251 assert(confAvg.size() == failAvg.size());
252 for (unsigned int j = 0; j < buckets.size(); j++) {
253 for (unsigned int i = 0; i < confAvg.size(); i++) {
254 confAvg[i][j] *= decay;
255 failAvg[i][j] *= decay;
256 }
257 m_feerate_avg[j] *= decay;
258 txCtAvg[j] *= decay;
259 }
260}
261
262// returns -1 on error conditions
263double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
264 double successBreakPoint, unsigned int nBlockHeight,
265 EstimationResult *result) const
266{
267 // Counters for a bucket (or range of buckets)
268 double nConf = 0; // Number of tx's confirmed within the confTarget
269 double totalNum = 0; // Total number of tx's that were ever confirmed
270 int extraNum = 0; // Number of tx's still in mempool for confTarget or longer
271 double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget
272 const int periodTarget = (confTarget + scale - 1) / scale;
273 const int maxbucketindex = buckets.size() - 1;
274
275 // We'll combine buckets until we have enough samples.
276 // The near and far variables will define the range we've combined
277 // The best variables are the last range we saw which still had a high
278 // enough confirmation rate to count as success.
279 // The cur variables are the current range we're counting.
280 unsigned int curNearBucket = maxbucketindex;
281 unsigned int bestNearBucket = maxbucketindex;
282 unsigned int curFarBucket = maxbucketindex;
283 unsigned int bestFarBucket = maxbucketindex;
284
285 // We'll always group buckets into sets that meet sufficientTxVal --
286 // this ensures that we're using consistent groups between different
287 // confirmation targets.
288 double partialNum = 0;
289
290 bool foundAnswer = false;
291 unsigned int bins = unconfTxs.size();
292 bool newBucketRange = true;
293 bool passing = true;
294 EstimatorBucket passBucket;
295 EstimatorBucket failBucket;
296
297 // Start counting from highest feerate transactions
298 for (int bucket = maxbucketindex; bucket >= 0; --bucket) {
299 if (newBucketRange) {
300 curNearBucket = bucket;
301 newBucketRange = false;
302 }
303 curFarBucket = bucket;
304 nConf += confAvg[periodTarget - 1][bucket];
305 partialNum += txCtAvg[bucket];
306 totalNum += txCtAvg[bucket];
307 failNum += failAvg[periodTarget - 1][bucket];
308 for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)
309 extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];
310 extraNum += oldUnconfTxs[bucket];
311 // If we have enough transaction data points in this range of buckets,
312 // we can test for success
313 // (Only count the confirmed data points, so that each confirmation count
314 // will be looking at the same amount of data and same bucket breaks)
315
316 if (partialNum < sufficientTxVal / (1 - decay)) {
317 // the buckets we've added in this round aren't sufficient
318 // so keep adding
319 continue;
320 } else {
321 partialNum = 0; // reset for the next range we'll add
322
323 double curPct = nConf / (totalNum + failNum + extraNum);
324
325 // Check to see if we are no longer getting confirmed at the success rate
326 if (curPct < successBreakPoint) {
327 if (passing == true) {
328 // First time we hit a failure record the failed bucket
329 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
330 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
331 failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
332 failBucket.end = buckets[failMaxBucket];
333 failBucket.withinTarget = nConf;
334 failBucket.totalConfirmed = totalNum;
335 failBucket.inMempool = extraNum;
336 failBucket.leftMempool = failNum;
337 passing = false;
338 }
339 continue;
340 }
341 // Otherwise update the cumulative stats, and the bucket variables
342 // and reset the counters
343 else {
344 failBucket = EstimatorBucket(); // Reset any failed bucket, currently passing
345 foundAnswer = true;
346 passing = true;
347 passBucket.withinTarget = nConf;
348 nConf = 0;
349 passBucket.totalConfirmed = totalNum;
350 totalNum = 0;
351 passBucket.inMempool = extraNum;
352 passBucket.leftMempool = failNum;
353 failNum = 0;
354 extraNum = 0;
355 bestNearBucket = curNearBucket;
356 bestFarBucket = curFarBucket;
357 newBucketRange = true;
358 }
359 }
360 }
361
362 double median = -1;
363 double txSum = 0;
364
365 // Calculate the "average" feerate of the best bucket range that met success conditions
366 // Find the bucket with the median transaction and then report the average feerate from that bucket
367 // This is a compromise between finding the median which we can't since we don't save all tx's
368 // and reporting the average which is less accurate
369 unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
370 unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
371 for (unsigned int j = minBucket; j <= maxBucket; j++) {
372 txSum += txCtAvg[j];
373 }
374 if (foundAnswer && txSum != 0) {
375 txSum = txSum / 2;
376 for (unsigned int j = minBucket; j <= maxBucket; j++) {
377 if (txCtAvg[j] < txSum)
378 txSum -= txCtAvg[j];
379 else { // we're in the right bucket
380 median = m_feerate_avg[j] / txCtAvg[j];
381 break;
382 }
383 }
384
385 passBucket.start = minBucket ? buckets[minBucket-1] : 0;
386 passBucket.end = buckets[maxBucket];
387 }
388
389 // If we were passing until we reached last few buckets with insufficient data, then report those as failed
390 if (passing && !newBucketRange) {
391 unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
392 unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
393 failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
394 failBucket.end = buckets[failMaxBucket];
395 failBucket.withinTarget = nConf;
396 failBucket.totalConfirmed = totalNum;
397 failBucket.inMempool = extraNum;
398 failBucket.leftMempool = failNum;
399 }
400
401 float passed_within_target_perc = 0.0;
402 float failed_within_target_perc = 0.0;
403 if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {
404 passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);
405 }
406 if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {
407 failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);
408 }
409
410 LogDebug(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",
411 confTarget, 100.0 * successBreakPoint, decay,
412 median, passBucket.start, passBucket.end,
413 passed_within_target_perc,
414 passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,
415 failBucket.start, failBucket.end,
416 failed_within_target_perc,
417 failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);
418
419
420 if (result) {
421 result->pass = passBucket;
422 result->fail = failBucket;
423 result->decay = decay;
424 result->scale = scale;
425 }
426 return median;
427}
428
429void TxConfirmStats::Write(AutoFile& fileout) const
430{
431 fileout << Using<EncodedDoubleFormatter>(decay);
432 fileout << scale;
433 fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
434 fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
435 fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
436 fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
437}
438
439void TxConfirmStats::Read(AutoFile& filein, size_t numBuckets)
440{
441 // Read data file and do some very basic sanity checking
442 // buckets and bucketMap are not updated yet, so don't access them
443 // If there is a read failure, we'll just discard this entire object anyway
444 uint64_t maxConfirms, maxPeriods;
445
446 // The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
447 filein >> Using<EncodedDoubleFormatter>(decay);
448 if (decay <= 0 || decay >= 1) {
449 throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
450 }
451 filein >> scale;
452 if (scale == 0) {
453 throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
454 }
455
456 filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
457 if (m_feerate_avg.size() != numBuckets) {
458 throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
459 }
460 filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
461 if (txCtAvg.size() != numBuckets) {
462 throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
463 }
464 filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
465 maxPeriods = confAvg.size();
466 maxConfirms = scale * maxPeriods;
467
468 if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) { // one week
469 throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
470 }
471 for (unsigned int i = 0; i < maxPeriods; i++) {
472 if (confAvg[i].size() != numBuckets) {
473 throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count");
474 }
475 }
476
477 filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
478 if (maxPeriods != failAvg.size()) {
479 throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures");
480 }
481 for (unsigned int i = 0; i < maxPeriods; i++) {
482 if (failAvg[i].size() != numBuckets) {
483 throw std::runtime_error("Corrupt estimates file. Mismatch in one of failure average bucket counts");
484 }
485 }
486
487 // Resize the current block variables which aren't stored in the data file
488 // to match the number of confirms and buckets
489 resizeInMemoryCounters(numBuckets);
490
491 LogDebug(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n",
492 numBuckets, maxConfirms);
493}
494
495unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)
496{
497 unsigned int bucketindex = bucketMap.lower_bound(val)->second;
498 unsigned int blockIndex = nBlockHeight % unconfTxs.size();
499 unconfTxs[blockIndex][bucketindex]++;
500 return bucketindex;
501}
502
503void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketindex, bool inBlock)
504{
505 //nBestSeenHeight is not updated yet for the new block
506 int blocksAgo = nBestSeenHeight - entryHeight;
507 if (nBestSeenHeight == 0) // the BlockPolicyEstimator hasn't seen any blocks yet
508 blocksAgo = 0;
509 if (blocksAgo < 0) {
510 LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, blocks ago is negative for mempool tx\n");
511 return; //This can't happen because we call this with our best seen height, no entries can have higher
512 }
513
514 if (blocksAgo >= (int)unconfTxs.size()) {
515 if (oldUnconfTxs[bucketindex] > 0) {
516 oldUnconfTxs[bucketindex]--;
517 } else {
518 LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n",
519 bucketindex);
520 }
521 }
522 else {
523 unsigned int blockIndex = entryHeight % unconfTxs.size();
524 if (unconfTxs[blockIndex][bucketindex] > 0) {
525 unconfTxs[blockIndex][bucketindex]--;
526 } else {
527 LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n",
528 blockIndex, bucketindex);
529 }
530 }
531 if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
532 assert(scale != 0);
533 unsigned int periodsAgo = blocksAgo / scale;
534 for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) {
535 failAvg[i][bucketindex]++;
536 }
537 }
538}
539
541{
543 return _removeTx(hash, /*inBlock=*/false);
544}
545
546bool CBlockPolicyEstimator::_removeTx(const Txid& hash, bool inBlock)
547{
549 std::map<Txid, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
550 if (pos != mapMemPoolTxs.end()) {
551 feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
552 shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
553 longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
554 mapMemPoolTxs.erase(hash);
555 return true;
556 } else {
557 return false;
558 }
559}
560
561CBlockPolicyEstimator::CBlockPolicyEstimator(const fs::path& estimation_filepath, const bool read_stale_estimates)
562 : m_estimation_filepath{estimation_filepath}
563{
564 static_assert(MIN_BUCKET_FEERATE > 0, "Min feerate must be nonzero");
565 size_t bucketIndex = 0;
566
567 for (double bucketBoundary = MIN_BUCKET_FEERATE; bucketBoundary <= MAX_BUCKET_FEERATE; bucketBoundary *= FEE_SPACING, bucketIndex++) {
568 buckets.push_back(bucketBoundary);
569 bucketMap[bucketBoundary] = bucketIndex;
570 }
571 buckets.push_back(INF_FEERATE);
572 bucketMap[INF_FEERATE] = bucketIndex;
573 assert(bucketMap.size() == buckets.size());
574
575 feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
576 shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
577 longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
578
580
581 if (est_file.IsNull()) {
582 LogInfo("%s is not found. Continue anyway.", fs::PathToString(m_estimation_filepath));
583 return;
584 }
585
586 std::chrono::hours file_age = GetFeeEstimatorFileAge();
587 if (file_age > MAX_FILE_AGE && !read_stale_estimates) {
588 LogWarning("Fee estimation file %s too old (age=%lld > %lld hours) and will not be used to avoid serving stale estimates.", fs::PathToString(m_estimation_filepath), Ticks<std::chrono::hours>(file_age), Ticks<std::chrono::hours>(MAX_FILE_AGE));
589 return;
590 }
591
592 if (!Read(est_file)) {
593 LogWarning("Failed to read fee estimates from %s. Continue anyway.", fs::PathToString(m_estimation_filepath));
594 }
595}
596
598
600{
602 const unsigned int txHeight = tx.info.txHeight;
603 const auto& hash = tx.info.m_tx->GetHash();
604 if (mapMemPoolTxs.contains(hash)) {
605 LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n",
606 hash.ToString());
607 return;
608 }
609
610 if (txHeight != nBestSeenHeight) {
611 // Ignore side chains and re-orgs; assuming they are random they don't
612 // affect the estimate. We'll potentially double count transactions in 1-block reorgs.
613 // Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip().
614 // It will be synced next time a block is processed.
615 return;
616 }
617 // This transaction should only count for fee estimation if:
618 // - it's not being re-added during a reorg which bypasses typical mempool fee limits
619 // - the node is not behind
620 // - the transaction is not dependent on any other transactions in the mempool
621 // - it's not part of a package.
622 const bool validForFeeEstimation = !tx.m_mempool_limit_bypassed && !tx.m_submitted_in_package && tx.m_chainstate_is_current && tx.m_has_no_mempool_parents;
623
624 // Only want to be updating estimates when our blockchain is synced,
625 // otherwise we'll miscalculate how many blocks its taking to get included.
626 if (!validForFeeEstimation) {
627 untrackedTxs++;
628 return;
629 }
630 trackedTxs++;
631
632 // Feerates are stored and reported as BTC-per-kb:
633 const CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size);
634
635 mapMemPoolTxs[hash].blockHeight = txHeight;
636 unsigned int bucketIndex = feeStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
637 mapMemPoolTxs[hash].bucketIndex = bucketIndex;
638 unsigned int bucketIndex2 = shortStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
639 assert(bucketIndex == bucketIndex2);
640 unsigned int bucketIndex3 = longStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
641 assert(bucketIndex == bucketIndex3);
642}
643
645{
647 if (!_removeTx(tx.info.m_tx->GetHash(), true)) {
648 // This transaction wasn't being tracked for fee estimation
649 return false;
650 }
651
652 // How many blocks did it take for miners to include this transaction?
653 // blocksToConfirm is 1-based, so a transaction included in the earliest
654 // possible block has confirmation count of 1
655 int blocksToConfirm = nBlockHeight - tx.info.txHeight;
656 if (blocksToConfirm <= 0) {
657 // This can't happen because we don't process transactions from a block with a height
658 // lower than our greatest seen height
659 LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error Transaction had negative blocksToConfirm\n");
660 return false;
661 }
662
663 // Feerates are stored and reported as BTC-per-kb:
665
666 feeStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
667 shortStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
668 longStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
669 return true;
670}
671
672void CBlockPolicyEstimator::processBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
673 unsigned int nBlockHeight)
674{
676 if (nBlockHeight <= nBestSeenHeight) {
677 // Ignore side chains and re-orgs; assuming they are random
678 // they don't affect the estimate.
679 // And if an attacker can re-org the chain at will, then
680 // you've got much bigger problems than "attacker can influence
681 // transaction fees."
682 return;
683 }
684
685 // Must update nBestSeenHeight in sync with ClearCurrent so that
686 // calls to removeTx (via processBlockTx) correctly calculate age
687 // of unconfirmed txs to remove from tracking.
688 nBestSeenHeight = nBlockHeight;
689
690 // Update unconfirmed circular buffer
691 feeStats->ClearCurrent(nBlockHeight);
692 shortStats->ClearCurrent(nBlockHeight);
693 longStats->ClearCurrent(nBlockHeight);
694
695 // Decay all exponential averages
696 feeStats->UpdateMovingAverages();
697 shortStats->UpdateMovingAverages();
698 longStats->UpdateMovingAverages();
699
700 unsigned int countedTxs = 0;
701 // Update averages with data points from current block
702 for (const auto& tx : txs_removed_for_block) {
703 if (processBlockTx(nBlockHeight, tx))
704 countedTxs++;
705 }
706
707 if (firstRecordedHeight == 0 && countedTxs > 0) {
708 firstRecordedHeight = nBestSeenHeight;
709 LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy first recorded height %u\n", firstRecordedHeight);
710 }
711
712
713 LogDebug(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",
714 countedTxs, txs_removed_for_block.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
715 MaxUsableEstimate(), HistoricalBlockSpan() > BlockSpan() ? "historical" : "current");
716
717 trackedTxs = 0;
718 untrackedTxs = 0;
719}
720
722{
723 // It's not possible to get reasonable estimates for confTarget of 1
724 if (confTarget <= 1)
725 return CFeeRate(0);
726
728}
729
730CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult* result) const
731{
732 TxConfirmStats* stats = nullptr;
733 double sufficientTxs = SUFFICIENT_FEETXS;
734 switch (horizon) {
736 stats = shortStats.get();
737 sufficientTxs = SUFFICIENT_TXS_SHORT;
738 break;
739 }
741 stats = feeStats.get();
742 break;
743 }
745 stats = longStats.get();
746 break;
747 }
748 } // no default case, so the compiler can warn about missing cases
749 assert(stats);
750
752 // Return failure if trying to analyze a target we're not tracking
753 if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms())
754 return CFeeRate(0);
755 if (successThreshold > 1)
756 return CFeeRate(0);
757
758 double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
759
760 if (median < 0)
761 return CFeeRate(0);
762
763 return CFeeRate(llround(median));
764}
765
767{
769 switch (horizon) {
771 return shortStats->GetMaxConfirms();
772 }
774 return feeStats->GetMaxConfirms();
775 }
777 return longStats->GetMaxConfirms();
778 }
779 } // no default case, so the compiler can warn about missing cases
780 assert(false);
781}
782
784{
785 if (firstRecordedHeight == 0) return 0;
786 assert(nBestSeenHeight >= firstRecordedHeight);
787
788 return nBestSeenHeight - firstRecordedHeight;
789}
790
792{
793 if (historicalFirst == 0) return 0;
794 assert(historicalBest >= historicalFirst);
795
796 if (nBestSeenHeight - historicalBest > OLDEST_ESTIMATE_HISTORY) return 0;
797
798 return historicalBest - historicalFirst;
799}
800
802{
803 // Block spans are divided by 2 to make sure there are enough potential failing data points for the estimate
804 return std::min(longStats->GetMaxConfirms(), std::max(BlockSpan(), HistoricalBlockSpan()) / 2);
805}
806
811double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
812{
813 double estimate = -1;
814 if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
815 // Find estimate from shortest time horizon possible
816 if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
817 estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
818 }
819 else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
820 estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
821 }
822 else { // long horizon
823 estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
824 }
825 if (checkShorterHorizon) {
826 EstimationResult tempResult;
827 // If a lower confTarget from a more recent horizon returns a lower answer use it.
828 if (confTarget > feeStats->GetMaxConfirms()) {
829 double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
830 if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
831 estimate = medMax;
832 if (result) *result = tempResult;
833 }
834 }
835 if (confTarget > shortStats->GetMaxConfirms()) {
836 double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
837 if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
838 estimate = shortMax;
839 if (result) *result = tempResult;
840 }
841 }
842 }
843 }
844 return estimate;
845}
846
850double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
851{
852 double estimate = -1;
853 EstimationResult tempResult;
854 if (doubleTarget <= shortStats->GetMaxConfirms()) {
855 estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);
856 }
857 if (doubleTarget <= feeStats->GetMaxConfirms()) {
858 double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);
859 if (longEstimate > estimate) {
860 estimate = longEstimate;
861 if (result) *result = tempResult;
862 }
863 }
864 return estimate;
865}
866
874CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
875{
877
878 FeeCalculation temp_fee_calc;
879 if (!feeCalc) feeCalc = &temp_fee_calc;
880
881 feeCalc->desiredTarget = confTarget;
882 feeCalc->returnedTarget = confTarget;
883 feeCalc->best_height = nBestSeenHeight;
884
885 double median = -1;
886 EstimationResult tempResult;
887
888 // Return failure if trying to analyze a target we're not tracking
889 if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) {
890 return CFeeRate(0); // error condition
891 }
892
893 // It's not possible to get reasonable estimates for confTarget of 1
894 if (confTarget == 1) confTarget = 2;
895
896 unsigned int maxUsableEstimate = MaxUsableEstimate();
897 if ((unsigned int)confTarget > maxUsableEstimate) {
898 confTarget = maxUsableEstimate;
899 }
900 feeCalc->returnedTarget = confTarget;
901
902 if (confTarget <= 1) return CFeeRate(0); // error condition
903
904 assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
923 double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
924 feeCalc->est = tempResult;
926 median = halfEst;
927 double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
928 if (actualEst > median) {
929 median = actualEst;
930 feeCalc->est = tempResult;
932 }
933 double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
934 if (doubleEst > median) {
935 median = doubleEst;
936 feeCalc->est = tempResult;
938 }
939
940 if (conservative || median == -1) {
941 double consEst = estimateConservativeFee(2 * confTarget, &tempResult);
942 if (consEst > median) {
943 median = consEst;
944 feeCalc->est = tempResult;
946 }
947 }
948
949 if (median < 0) return CFeeRate(0); // error condition
950
951 LogDebug(BCLog::ESTIMATEFEE, "estimateSmartFee Selected feerate: %g Tgt: %d (requested %d) Reason: \"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)",
952 median, feeCalc->returnedTarget, feeCalc->desiredTarget, StringForBlockPolicyEstimateReason(feeCalc->reason), feeCalc->est.decay,
953 feeCalc->est.pass.start, feeCalc->est.pass.end,
954 (feeCalc->est.pass.totalConfirmed + feeCalc->est.pass.inMempool + feeCalc->est.pass.leftMempool) > 0.0 ? 100 * feeCalc->est.pass.withinTarget / (feeCalc->est.pass.totalConfirmed + feeCalc->est.pass.inMempool + feeCalc->est.pass.leftMempool) : 0.0,
955 feeCalc->est.pass.withinTarget, feeCalc->est.pass.totalConfirmed, feeCalc->est.pass.inMempool, feeCalc->est.pass.leftMempool,
956 feeCalc->est.fail.start, feeCalc->est.fail.end,
957 (feeCalc->est.fail.totalConfirmed + feeCalc->est.fail.inMempool + feeCalc->est.fail.leftMempool) > 0.0 ? 100 * feeCalc->est.fail.withinTarget / (feeCalc->est.fail.totalConfirmed + feeCalc->est.fail.inMempool + feeCalc->est.fail.leftMempool) : 0.0,
958 feeCalc->est.fail.withinTarget, feeCalc->est.fail.totalConfirmed, feeCalc->est.fail.inMempool, feeCalc->est.fail.leftMempool);
959
960 return CFeeRate(llround(median));
961}
962
964{
965 FeeCalculation fee_calc;
966 CFeeRate feerate{estimateSmartFee(target, &fee_calc, conservative)};
967 if (feerate == CFeeRate(0)) {
968 return EstimationError(FeeRateEstimatorType::BLOCK_POLICY, fee_calc.returnedTarget, "Insufficient data or no feerate found");
969 }
970 return FeeRateEstimation{FeeRateEstimatorType::BLOCK_POLICY, feerate.GetFeePerVSize(), fee_calc.returnedTarget};
971}
972
974{
976}
977
981}
982
984{
985 if (!m_estimation_filepath.parent_path().empty()) {
986 std::error_code error;
987 fs::create_directories(m_estimation_filepath.parent_path(), error);
988 if (error) {
989 LogWarning("Failed to create fee estimates directory %s: %s. Continue anyway.", fs::PathToString(m_estimation_filepath.parent_path()), error.message());
990 return;
991 }
992 }
993
995 if (est_file.IsNull() || !Write(est_file)) {
996 LogWarning("Failed to write fee estimates to %s. Continue anyway.", fs::PathToString(m_estimation_filepath));
997 (void)est_file.fclose();
998 return;
999 }
1000 if (est_file.fclose() != 0) {
1001 LogWarning("Failed to close fee estimates file %s: %s. Continuing anyway.", fs::PathToString(m_estimation_filepath), SysErrorString(errno));
1002 return;
1003 }
1004 LogDebug(BCLog::ESTIMATEFEE, "Flushed fee estimates to %s.", fs::PathToString(m_estimation_filepath));
1005}
1006
1008{
1009 try {
1011 fileout << CURRENT_FEES_FILE_VERSION;
1012 fileout << nBestSeenHeight;
1013 if (BlockSpan() > HistoricalBlockSpan()/2) {
1014 fileout << firstRecordedHeight << nBestSeenHeight;
1015 }
1016 else {
1017 fileout << historicalFirst << historicalBest;
1018 }
1019 fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets);
1020 feeStats->Write(fileout);
1021 shortStats->Write(fileout);
1022 longStats->Write(fileout);
1023 }
1024 catch (const std::exception&) {
1025 LogWarning("Unable to write policy estimator data (non-fatal)");
1026 return false;
1027 }
1028 return true;
1029}
1030
1032{
1033 try {
1035 int nVersionRequired;
1036 filein >> nVersionRequired;
1037 if (nVersionRequired > CURRENT_FEES_FILE_VERSION) {
1038 throw std::runtime_error{strprintf("File version (%d) too high to be read.", nVersionRequired)};
1039 }
1040 if (nVersionRequired < CURRENT_FEES_FILE_VERSION) {
1041 throw std::runtime_error{strprintf("File version (%d) incompatible: Too old to be read", nVersionRequired)};
1042 }
1043
1044 // Read fee estimates file into temporary variables so existing data
1045 // structures aren't corrupted if there is an exception.
1046 unsigned int nFileBestSeenHeight;
1047 filein >> nFileBestSeenHeight;
1048
1049 // nVersionRequired == CURRENT_FEES_FILE_VERSION
1050 unsigned int nFileHistoricalFirst, nFileHistoricalBest;
1051 filein >> nFileHistoricalFirst >> nFileHistoricalBest;
1052 if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
1053 throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
1054 }
1055 std::vector<double> fileBuckets;
1056 filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
1057 size_t numBuckets = fileBuckets.size();
1058 if (numBuckets <= 1 || numBuckets > 1000) {
1059 throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
1060 }
1061
1062 std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
1063 std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
1064 std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
1065 fileFeeStats->Read(filein, numBuckets);
1066 fileShortStats->Read(filein, numBuckets);
1067 fileLongStats->Read(filein, numBuckets);
1068
1069 // Fee estimates file parsed correctly
1070 // Copy buckets from file and refresh our bucketmap
1071 buckets = fileBuckets;
1072 bucketMap.clear();
1073 for (unsigned int i = 0; i < buckets.size(); i++) {
1074 bucketMap[buckets[i]] = i;
1075 }
1076
1077 // Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
1078 feeStats = std::move(fileFeeStats);
1079 shortStats = std::move(fileShortStats);
1080 longStats = std::move(fileLongStats);
1081
1082 nBestSeenHeight = nFileBestSeenHeight;
1083 historicalFirst = nFileHistoricalFirst;
1084 historicalBest = nFileHistoricalBest;
1085 }
1086 catch (const std::exception& e) {
1087 LogWarning("Unable to read policy estimator data (non-fatal): %s", e.what());
1088 return false;
1089 }
1090 return true;
1091}
1092
1094{
1095 const auto startclear{SteadyClock::now()};
1097 size_t num_entries = mapMemPoolTxs.size();
1098 // Remove every entry in mapMemPoolTxs
1099 while (!mapMemPoolTxs.empty()) {
1100 auto mi = mapMemPoolTxs.begin();
1101 _removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs
1102 }
1103 const auto endclear{SteadyClock::now()};
1104 LogDebug(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %.3fs\n", num_entries, Ticks<SecondsDouble>(endclear - startclear));
1105}
1106
1108{
1109 auto file_time{fs::last_write_time(m_estimation_filepath)};
1110 auto now{fs::file_time_type::clock::now()};
1111 return std::chrono::duration_cast<std::chrono::hours>(now - file_time);
1112}
1113
1114static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee,
1115 double max_filter_fee_rate,
1116 double fee_filter_spacing)
1117{
1118 std::set<double> fee_set;
1119
1120 const CAmount min_fee_limit{std::max(CAmount(1), min_incremental_fee.GetFeePerK() / 2)};
1121 fee_set.insert(0);
1122 for (double bucket_boundary = min_fee_limit;
1123 bucket_boundary <= max_filter_fee_rate;
1124 bucket_boundary *= fee_filter_spacing) {
1125
1126 fee_set.insert(bucket_boundary);
1127 }
1128
1129 return fee_set;
1130}
1131
1133 : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)},
1134 insecure_rand{rng}
1135{
1136}
1137
1139{
1141 std::set<double>::iterator it = m_fee_set.lower_bound(currentMinFee);
1142 if (it == m_fee_set.end() ||
1143 (it != m_fee_set.begin() &&
1144 WITH_LOCK(m_insecure_rand_mutex, return insecure_rand.rand32()) % 3 != 0)) {
1145 --it;
1146 }
1147 return static_cast<CAmount>(*it);
1148}
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
constexpr std::chrono::hours MAX_FILE_AGE
Block policy estimate files that are more than 60 hours (2.5 days) old will not be read,...
FeeEstimateHorizon
BlockPolicyEstimateReason
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:395
int64_t size()
Return the size of the file.
Definition: streams.cpp:60
void processTransaction(const NewMempoolTransactionInfo &tx) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Process a transaction accepted to the mempool.
static constexpr unsigned int LONG_SCALE
static constexpr double SUCCESS_PCT
Require greater than 85% of X feerate transactions to be confirmed within Y blocks.
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 estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Helper for estimateSmartFee.
static constexpr double FEE_SPACING
Spacing of FeeRate buckets We have to lump transactions into buckets based on feerate,...
void Flush() EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Drop still unconfirmed transactions and record current estimations, if the fee estimation file is pre...
static constexpr double SUFFICIENT_FEETXS
Require an avg of 0.1 tx in the combined feerate bucket per block to have stat significance.
bool removeTx(Txid hash) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Remove a transaction from the mempool tracking stats for non BLOCK removal reasons.
static constexpr double MAX_BUCKET_FEERATE
CBlockPolicyEstimator(const fs::path &estimation_filepath, bool read_stale_estimates)
Create new BlockPolicyEstimator and initialize stats tracking classes with default values.
void FlushFeeEstimates() EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Record current fee estimations.
virtual CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Estimate feerate needed to get be included in a block within confTarget blocks.
static constexpr unsigned int LONG_BLOCK_PERIODS
Track confirm delays up to 1008 blocks for long horizon.
bool Write(AutoFile &fileout) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Write estimation data to a file.
static constexpr double SHORT_DECAY
Decay of .962 is a half-life of 18 blocks or about 3 hours.
std::chrono::hours GetFeeEstimatorFileAge()
Calculates the age of the file, since last modified.
virtual unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Calculation of highest target that estimates are tracked for.
static constexpr double LONG_DECAY
Decay of .99931 is a half-life of 1008 blocks or about 1 week.
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Helper for estimateSmartFee.
static constexpr double HALF_SUCCESS_PCT
Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks.
static constexpr double MED_DECAY
Decay of .9952 is a half-life of 144 blocks or about 1 day.
CFeeRate estimateFee(int confTarget) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
DEPRECATED.
bool _removeTx(const Txid &hash, bool inBlock) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
A non-thread-safe helper for the removeTx function.
unsigned int MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Calculation of highest target that reasonable estimate can be provided for.
util::Expected< FeeRateEstimation, FeeRateEstimationError > EstimateFeeRate(int target, bool conservative) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Estimate the feerate needed to confirm within target blocks; wraps estimateSmartFee into a FeeRateEst...
static constexpr unsigned int SHORT_SCALE
unsigned int BlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Number of blocks of data recorded while fee estimates have been running.
bool Read(AutoFile &filein) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Read estimation data from a file.
static constexpr unsigned int SHORT_BLOCK_PERIODS
Track confirm delays up to 12 blocks for short horizon.
static constexpr double DOUBLE_SUCCESS_PCT
Require greater than 95% of X feerate transactions to be confirmed within 2 * Y blocks.
unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Number of blocks of recorded fee estimate data represented in saved data file.
void FlushUnconfirmed() EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool.
CFeeRate estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult *result=nullptr) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Return a specific fee estimate calculation with a given success threshold and time horizon,...
unsigned int MaximumTarget() const EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Return the highest confirmation target for which an estimate can be provided.
static constexpr unsigned int OLDEST_ESTIMATE_HISTORY
Historical estimates that are older than this aren't valid.
static constexpr double SUFFICIENT_TXS_SHORT
Require an avg of 0.5 tx when using short decay since there are fewer blocks considered.
bool processBlockTx(unsigned int nBlockHeight, const RemovedMempoolTransactionInfo &tx) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator)
Process a transaction confirmed in a block.
static constexpr unsigned int MED_SCALE
static constexpr unsigned int MED_BLOCK_PERIODS
Track confirm delays up to 48 blocks for medium horizon.
const fs::path m_estimation_filepath
virtual ~CBlockPolicyEstimator()
void processBlock(const std::vector< RemovedMempoolTransactionInfo > &txs_removed_for_block, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator)
Process all the transactions that have been included in a block.
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
Definition: feerate.h:32
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
Definition: feerate.h:71
Fast randomness source.
Definition: random.h:386
const std::set< double > m_fee_set
CAmount round(CAmount currentMinFee) EXCLUSIVE_LOCKS_REQUIRED(!m_insecure_rand_mutex)
Quantize a minimum fee for privacy purpose before broadcast.
FeeFilterRounder(const CFeeRate &min_incremental_fee, FastRandomContext &rng)
Create new FeeFilterRounder.
We will instantiate an instance of this class to track transactions that were included in a block.
void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketIndex, bool inBlock)
Remove a transaction from mempool tracking stats.
std::vector< std::vector< double > > failAvg
TxConfirmStats(const std::vector< double > &defaultBuckets, const std::map< double, unsigned int > &defaultBucketMap, unsigned int maxPeriods, double decay, unsigned int scale)
Create new TxConfirmStats.
unsigned int GetMaxConfirms() const
Return the max number of confirms we're tracking.
void ClearCurrent(unsigned int nBlockHeight)
Roll the circular buffer for unconfirmed txs.
void Record(int blocksToConfirm, double val)
Record a new transaction data point in the current block stats.
void resizeInMemoryCounters(size_t newbuckets)
std::vector< double > txCtAvg
std::vector< int > oldUnconfTxs
void UpdateMovingAverages()
Update our estimates by decaying our historical moving average and updating with the data gathered fr...
const std::map< double, unsigned int > & bucketMap
const std::vector< double > & buckets
void Read(AutoFile &filein, size_t numBuckets)
Read saved state of estimation data from a file and replace all internal data structures and variable...
std::vector< std::vector< double > > confAvg
void Write(AutoFile &fileout) const
Write state of estimation data to a file.
std::vector< std::vector< int > > unconfTxs
unsigned int NewTx(unsigned int nBlockHeight, double val)
Record a new transaction entering the mempool.
std::vector< double > m_feerate_avg
double EstimateMedianVal(int confTarget, double sufficientTxVal, double minSuccess, unsigned int nBlockHeight, EstimationResult *result=nullptr) const
Calculate a feerate estimate.
The util::Expected class provides a standard way for low-level functions to return either error value...
Definition: expected.h:44
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:162
#define LogWarning(...)
Definition: log.h:126
#define LogInfo(...)
Definition: log.h:125
#define LogDebug(category,...)
Definition: log.h:143
@ ESTIMATEFEE
Definition: categories.h:24
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:23
SocketId Stream
Definition: util.h:30
static constexpr double INF_FEERATE
static std::set< double > MakeFeeSet(const CFeeRate &min_incremental_fee, double max_filter_fee_rate, double fee_filter_spacing)
std::string StringForBlockPolicyEstimateReason(BlockPolicyEstimateReason reason)
constexpr int CURRENT_FEES_FILE_VERSION
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
uint64_t EncodeDouble(double f) noexcept
Definition: serfloat.cpp:37
double DecodeDouble(uint64_t v) noexcept
Definition: serfloat.cpp:10
BlockPolicyEstimateReason reason
unsigned int best_height
EstimationResult est
A successful fee rate estimate returned by a fee rate estimator.
Definition: fees.h:46
const bool m_has_no_mempool_parents
const bool m_chainstate_is_current
const bool m_mempool_limit_bypassed
const CAmount m_fee
const unsigned int txHeight
const CTransactionRef m_tx
const int64_t m_virtual_transaction_size
The virtual transaction size.
#define AssertLockNotHeld(cs)
Definition: sync.h:149
#define LOCK(cs)
Definition: sync.h:268
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:299
std::string SysErrorString(int err)
Return system error string from errno value.
Definition: syserror.cpp:18
const size_t num_entries
Definition: dbwrapper.cpp:375
FastRandomContext rng
Definition: dbwrapper.cpp:413
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
util::Unexpected< FeeRateEstimationError > EstimationError(FeeRateEstimatorType estimator, int returned_target, std::string error)
Build a fee rate estimation error result: a zero-value estimation identifying the estimator and targe...
Definition: fees.h:78
AssertLockHeld(pool.cs)
assert(!tx.IsCoinBase())