77#include " policy/policy.h"
88
99#include " amount.h"
10+ #include " clientversion.h"
1011#include " primitives/transaction.h"
1112#include " streams.h"
1213#include " txmempool.h"
1314#include " util.h"
1415
15- void TxConfirmStats::Initialize (std::vector<double >& defaultBuckets,
16- unsigned int maxConfirms, double _decay)
16+ /* *
17+ * We will instantiate an instance of this class to track transactions that were
18+ * included in a block. We will lump transactions into a bucket according to their
19+ * approximate feerate and then track how long it took for those txs to be included in a block
20+ *
21+ * The tracking of unconfirmed (mempool) transactions is completely independent of the
22+ * historical tracking of transactions that have been confirmed in a block.
23+ */
24+ class TxConfirmStats
25+ {
26+ private:
27+ // Define the buckets we will group transactions into
28+ std::vector<double > buckets; // The upper-bound of the range for the bucket (inclusive)
29+ std::map<double , unsigned int > bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
30+
31+ // For each bucket X:
32+ // Count the total # of txs in each bucket
33+ // Track the historical moving average of this total over blocks
34+ std::vector<double > txCtAvg;
35+ // and calculate the total for the current block to update the moving average
36+ std::vector<int > curBlockTxCt;
37+
38+ // Count the total # of txs confirmed within Y blocks in each bucket
39+ // Track the historical moving average of theses totals over blocks
40+ std::vector<std::vector<double > > confAvg; // confAvg[Y][X]
41+ // and calculate the totals for the current block to update the moving averages
42+ std::vector<std::vector<int > > curBlockConf; // curBlockConf[Y][X]
43+
44+ // Sum the total feerate of all tx's in each bucket
45+ // Track the historical moving average of this total over blocks
46+ std::vector<double > avg;
47+ // and calculate the total for the current block to update the moving average
48+ std::vector<double > curBlockVal;
49+
50+ // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
51+ // Combine the total value with the tx counts to calculate the avg feerate per bucket
52+
53+ double decay;
54+
55+ // Mempool counts of outstanding transactions
56+ // For each bucket X, track the number of transactions in the mempool
57+ // that are unconfirmed for each possible confirmation value Y
58+ std::vector<std::vector<int > > unconfTxs; // unconfTxs[Y][X]
59+ // transactions still unconfirmed after MAX_CONFIRMS for each bucket
60+ std::vector<int > oldUnconfTxs;
61+
62+ public:
63+ /* *
64+ * Create new TxConfirmStats. This is called by BlockPolicyEstimator's
65+ * constructor with default values.
66+ * @param defaultBuckets contains the upper limits for the bucket boundaries
67+ * @param maxConfirms max number of confirms to track
68+ * @param decay how much to decay the historical moving average per block
69+ */
70+ TxConfirmStats (const std::vector<double >& defaultBuckets, unsigned int maxConfirms, double decay);
71+
72+ /* * Clear the state of the curBlock variables to start counting for the new block */
73+ void ClearCurrent (unsigned int nBlockHeight);
74+
75+ /* *
76+ * Record a new transaction data point in the current block stats
77+ * @param blocksToConfirm the number of blocks it took this transaction to confirm
78+ * @param val the feerate of the transaction
79+ * @warning blocksToConfirm is 1-based and has to be >= 1
80+ */
81+ void Record (int blocksToConfirm, double val);
82+
83+ /* * Record a new transaction entering the mempool*/
84+ unsigned int NewTx (unsigned int nBlockHeight, double val);
85+
86+ /* * Remove a transaction from mempool tracking stats*/
87+ void removeTx (unsigned int entryHeight, unsigned int nBestSeenHeight,
88+ unsigned int bucketIndex);
89+
90+ /* * Update our estimates by decaying our historical moving average and updating
91+ with the data gathered from the current block */
92+ void UpdateMovingAverages ();
93+
94+ /* *
95+ * Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
96+ * to make sure we have enough data points) whose transactions still have sufficient likelihood
97+ * of being confirmed within the target number of confirmations
98+ * @param confTarget target number of confirmations
99+ * @param sufficientTxVal required average number of transactions per block in a bucket range
100+ * @param minSuccess the success probability we require
101+ * @param requireGreater return the lowest feerate such that all higher values pass minSuccess OR
102+ * return the highest feerate such that all lower values fail minSuccess
103+ * @param nBlockHeight the current block height
104+ */
105+ double EstimateMedianVal (int confTarget, double sufficientTxVal,
106+ double minSuccess, bool requireGreater, unsigned int nBlockHeight) const ;
107+
108+ /* * Return the max number of confirms we're tracking */
109+ unsigned int GetMaxConfirms () const { return confAvg.size (); }
110+
111+ /* * Write state of estimation data to a file*/
112+ void Write (CAutoFile& fileout) const ;
113+
114+ /* *
115+ * Read saved state of estimation data from a file and replace all internal data structures and
116+ * variables with this state.
117+ */
118+ void Read (CAutoFile& filein);
119+ };
120+
121+
122+ TxConfirmStats::TxConfirmStats (const std::vector<double >& defaultBuckets,
123+ unsigned int maxConfirms, double _decay)
17124{
18125 decay = _decay;
19126 for (unsigned int i = 0 ; i < defaultBuckets.size (); i++) {
@@ -76,7 +183,7 @@ void TxConfirmStats::UpdateMovingAverages()
76183// returns -1 on error conditions
77184double TxConfirmStats::EstimateMedianVal (int confTarget, double sufficientTxVal,
78185 double successBreakPoint, bool requireGreater,
79- unsigned int nBlockHeight)
186+ unsigned int nBlockHeight) const
80187{
81188 // Counters for a bucket (or range of buckets)
82189 double nConf = 0 ; // Number of tx's confirmed within the confTarget
@@ -172,7 +279,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
172279 return median;
173280}
174281
175- void TxConfirmStats::Write (CAutoFile& fileout)
282+ void TxConfirmStats::Write (CAutoFile& fileout) const
176283{
177284 fileout << decay;
178285 fileout << buckets;
@@ -289,9 +396,10 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
289396// of no harm to try to remove them again.
290397bool CBlockPolicyEstimator::removeTx (uint256 hash)
291398{
399+ LOCK (cs_feeEstimator);
292400 std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find (hash);
293401 if (pos != mapMemPoolTxs.end ()) {
294- feeStats. removeTx (pos->second .blockHeight , nBestSeenHeight, pos->second .bucketIndex );
402+ feeStats-> removeTx (pos->second .blockHeight , nBestSeenHeight, pos->second .bucketIndex );
295403 mapMemPoolTxs.erase (hash);
296404 return true ;
297405 } else {
@@ -309,11 +417,17 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
309417 vfeelist.push_back (bucketBoundary);
310418 }
311419 vfeelist.push_back (INF_FEERATE);
312- feeStats.Initialize (vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
420+ feeStats = new TxConfirmStats (vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY);
421+ }
422+
423+ CBlockPolicyEstimator::~CBlockPolicyEstimator ()
424+ {
425+ delete feeStats;
313426}
314427
315428void CBlockPolicyEstimator::processTransaction (const CTxMemPoolEntry& entry, bool validFeeEstimate)
316429{
430+ LOCK (cs_feeEstimator);
317431 unsigned int txHeight = entry.GetHeight ();
318432 uint256 hash = entry.GetTx ().GetHash ();
319433 if (mapMemPoolTxs.count (hash)) {
@@ -341,7 +455,7 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
341455 CFeeRate feeRate (entry.GetFee (), entry.GetTxSize ());
342456
343457 mapMemPoolTxs[hash].blockHeight = txHeight;
344- mapMemPoolTxs[hash].bucketIndex = feeStats. NewTx (txHeight, (double )feeRate.GetFeePerK ());
458+ mapMemPoolTxs[hash].bucketIndex = feeStats-> NewTx (txHeight, (double )feeRate.GetFeePerK ());
345459}
346460
347461bool CBlockPolicyEstimator::processBlockTx (unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
@@ -365,13 +479,14 @@ bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxM
365479 // Feerates are stored and reported as BTC-per-kb:
366480 CFeeRate feeRate (entry->GetFee (), entry->GetTxSize ());
367481
368- feeStats. Record (blocksToConfirm, (double )feeRate.GetFeePerK ());
482+ feeStats-> Record (blocksToConfirm, (double )feeRate.GetFeePerK ());
369483 return true ;
370484}
371485
372486void CBlockPolicyEstimator::processBlock (unsigned int nBlockHeight,
373487 std::vector<const CTxMemPoolEntry*>& entries)
374488{
489+ LOCK (cs_feeEstimator);
375490 if (nBlockHeight <= nBestSeenHeight) {
376491 // Ignore side chains and re-orgs; assuming they are random
377492 // they don't affect the estimate.
@@ -387,7 +502,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
387502 nBestSeenHeight = nBlockHeight;
388503
389504 // Clear the current block state and update unconfirmed circular buffer
390- feeStats. ClearCurrent (nBlockHeight);
505+ feeStats-> ClearCurrent (nBlockHeight);
391506
392507 unsigned int countedTxs = 0 ;
393508 // Repopulate the current block states
@@ -397,7 +512,7 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
397512 }
398513
399514 // Update all exponential averages with the current block state
400- feeStats. UpdateMovingAverages ();
515+ feeStats-> UpdateMovingAverages ();
401516
402517 LogPrint (BCLog::ESTIMATEFEE, " Blockpolicy after updating estimates for %u of %u txs in block, since last block %u of %u tracked, new mempool map size %u\n " ,
403518 countedTxs, entries.size (), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size ());
@@ -406,37 +521,44 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
406521 untrackedTxs = 0 ;
407522}
408523
409- CFeeRate CBlockPolicyEstimator::estimateFee (int confTarget)
524+ CFeeRate CBlockPolicyEstimator::estimateFee (int confTarget) const
410525{
526+ LOCK (cs_feeEstimator);
411527 // Return failure if trying to analyze a target we're not tracking
412528 // It's not possible to get reasonable estimates for confTarget of 1
413- if (confTarget <= 1 || (unsigned int )confTarget > feeStats. GetMaxConfirms ())
529+ if (confTarget <= 1 || (unsigned int )confTarget > feeStats-> GetMaxConfirms ())
414530 return CFeeRate (0 );
415531
416- double median = feeStats. EstimateMedianVal (confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true , nBestSeenHeight);
532+ double median = feeStats-> EstimateMedianVal (confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true , nBestSeenHeight);
417533
418534 if (median < 0 )
419535 return CFeeRate (0 );
420536
421537 return CFeeRate (median);
422538}
423539
424- CFeeRate CBlockPolicyEstimator::estimateSmartFee (int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool)
540+ CFeeRate CBlockPolicyEstimator::estimateSmartFee (int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool) const
425541{
426542 if (answerFoundAtTarget)
427543 *answerFoundAtTarget = confTarget;
428- // Return failure if trying to analyze a target we're not tracking
429- if (confTarget <= 0 || (unsigned int )confTarget > feeStats.GetMaxConfirms ())
430- return CFeeRate (0 );
431-
432- // It's not possible to get reasonable estimates for confTarget of 1
433- if (confTarget == 1 )
434- confTarget = 2 ;
435544
436545 double median = -1 ;
437- while (median < 0 && (unsigned int )confTarget <= feeStats.GetMaxConfirms ()) {
438- median = feeStats.EstimateMedianVal (confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true , nBestSeenHeight);
439- }
546+
547+ {
548+ LOCK (cs_feeEstimator);
549+
550+ // Return failure if trying to analyze a target we're not tracking
551+ if (confTarget <= 0 || (unsigned int )confTarget > feeStats->GetMaxConfirms ())
552+ return CFeeRate (0 );
553+
554+ // It's not possible to get reasonable estimates for confTarget of 1
555+ if (confTarget == 1 )
556+ confTarget = 2 ;
557+
558+ while (median < 0 && (unsigned int )confTarget <= feeStats->GetMaxConfirms ()) {
559+ median = feeStats->EstimateMedianVal (confTarget++, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true , nBestSeenHeight);
560+ }
561+ } // Must unlock cs_feeEstimator before taking mempool locks
440562
441563 if (answerFoundAtTarget)
442564 *answerFoundAtTarget = confTarget - 1 ;
@@ -452,18 +574,39 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
452574 return CFeeRate (median);
453575}
454576
455- void CBlockPolicyEstimator::Write (CAutoFile& fileout)
577+ bool CBlockPolicyEstimator::Write (CAutoFile& fileout) const
456578{
457- fileout << nBestSeenHeight;
458- feeStats.Write (fileout);
579+ try {
580+ LOCK (cs_feeEstimator);
581+ fileout << 139900 ; // version required to read: 0.13.99 or later
582+ fileout << CLIENT_VERSION; // version that wrote the file
583+ fileout << nBestSeenHeight;
584+ feeStats->Write (fileout);
585+ }
586+ catch (const std::exception&) {
587+ LogPrintf (" CBlockPolicyEstimator::Write(): unable to read policy estimator data (non-fatal)\n " );
588+ return false ;
589+ }
590+ return true ;
459591}
460592
461- void CBlockPolicyEstimator::Read (CAutoFile& filein, int nFileVersion )
593+ bool CBlockPolicyEstimator::Read (CAutoFile& filein)
462594{
463- int nFileBestSeenHeight;
464- filein >> nFileBestSeenHeight;
465- feeStats.Read (filein);
466- nBestSeenHeight = nFileBestSeenHeight;
467- // if nVersionThatWrote < 120300 then another TxConfirmStats (for priority) follows but can be ignored.
595+ try {
596+ LOCK (cs_feeEstimator);
597+ int nVersionRequired, nVersionThatWrote, nFileBestSeenHeight;
598+ filein >> nVersionRequired >> nVersionThatWrote;
599+ if (nVersionRequired > CLIENT_VERSION)
600+ return error (" CBlockPolicyEstimator::Read(): up-version (%d) fee estimate file" , nVersionRequired);
601+ filein >> nFileBestSeenHeight;
602+ feeStats->Read (filein);
603+ nBestSeenHeight = nFileBestSeenHeight;
604+ // if nVersionThatWrote < 120300 then another TxConfirmStats (for priority) follows but can be ignored.
605+ }
606+ catch (const std::exception&) {
607+ LogPrintf (" CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal)\n " );
608+ return false ;
609+ }
610+ return true ;
468611}
469612
0 commit comments