@@ -53,13 +53,14 @@ class TxConfirmStats
5353
5454 double decay;
5555
56+ // Resolution (# of blocks) with which confirmations are tracked
5657 unsigned int scale;
5758
5859 // Mempool counts of outstanding transactions
5960 // For each bucket X, track the number of transactions in the mempool
6061 // that are unconfirmed for each possible confirmation value Y
6162 std::vector<std::vector<int > > unconfTxs; // unconfTxs[Y][X]
62- // transactions still unconfirmed after MAX_CONFIRMS for each bucket
63+ // transactions still unconfirmed after GetMaxConfirms for each bucket
6364 std::vector<int > oldUnconfTxs;
6465
6566 void resizeInMemoryCounters (size_t newbuckets);
@@ -73,7 +74,7 @@ class TxConfirmStats
7374 * @param decay how much to decay the historical moving average per block
7475 */
7576 TxConfirmStats (const std::vector<double >& defaultBuckets, const std::map<double , unsigned int >& defaultBucketMap,
76- unsigned int maxConfirms , double decay);
77+ unsigned int maxPeriods , double decay, unsigned int scale );
7778
7879 /* * Roll the circular buffer for unconfirmed txs*/
7980 void ClearCurrent (unsigned int nBlockHeight);
@@ -113,7 +114,7 @@ class TxConfirmStats
113114 EstimationResult *result = nullptr ) const ;
114115
115116 /* * Return the max number of confirms we're tracking */
116- unsigned int GetMaxConfirms () const { return confAvg.size (); }
117+ unsigned int GetMaxConfirms () const { return scale * confAvg.size (); }
117118
118119 /* * Write state of estimation data to a file*/
119120 void Write (CAutoFile& fileout) const ;
@@ -128,17 +129,17 @@ class TxConfirmStats
128129
129130TxConfirmStats::TxConfirmStats (const std::vector<double >& defaultBuckets,
130131 const std::map<double , unsigned int >& defaultBucketMap,
131- unsigned int maxConfirms , double _decay)
132+ unsigned int maxPeriods , double _decay, unsigned int _scale )
132133 : buckets(defaultBuckets), bucketMap(defaultBucketMap)
133134{
134135 decay = _decay;
135- scale = 1 ;
136- confAvg.resize (maxConfirms );
137- for (unsigned int i = 0 ; i < maxConfirms ; i++) {
136+ scale = _scale ;
137+ confAvg.resize (maxPeriods );
138+ for (unsigned int i = 0 ; i < maxPeriods ; i++) {
138139 confAvg[i].resize (buckets.size ());
139140 }
140- failAvg.resize (maxConfirms );
141- for (unsigned int i = 0 ; i < maxConfirms ; i++) {
141+ failAvg.resize (maxPeriods );
142+ for (unsigned int i = 0 ; i < maxPeriods ; i++) {
142143 failAvg[i].resize (buckets.size ());
143144 }
144145
@@ -172,8 +173,9 @@ void TxConfirmStats::Record(int blocksToConfirm, double val)
172173 // blocksToConfirm is 1-based
173174 if (blocksToConfirm < 1 )
174175 return ;
176+ int periodsToConfirm = (blocksToConfirm + scale - 1 )/scale;
175177 unsigned int bucketindex = bucketMap.lower_bound (val)->second ;
176- for (size_t i = blocksToConfirm ; i <= confAvg.size (); i++) {
178+ for (size_t i = periodsToConfirm ; i <= confAvg.size (); i++) {
177179 confAvg[i - 1 ][bucketindex]++;
178180 }
179181 txCtAvg[bucketindex]++;
@@ -202,6 +204,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
202204 double totalNum = 0 ; // Total number of tx's that were ever confirmed
203205 int extraNum = 0 ; // Number of tx's still in mempool for confTarget or longer
204206 double failNum = 0 ; // Number of tx's that were never confirmed but removed from the mempool after confTarget
207+ int periodTarget = (confTarget + scale - 1 )/scale;
205208
206209 int maxbucketindex = buckets.size () - 1 ;
207210
@@ -236,9 +239,9 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
236239 newBucketRange = false ;
237240 }
238241 curFarBucket = bucket;
239- nConf += confAvg[confTarget - 1 ][bucket];
242+ nConf += confAvg[periodTarget - 1 ][bucket];
240243 totalNum += txCtAvg[bucket];
241- failNum += failAvg[confTarget - 1 ][bucket];
244+ failNum += failAvg[periodTarget - 1 ][bucket];
242245 for (unsigned int confct = confTarget; confct < GetMaxConfirms (); confct++)
243246 extraNum += unconfTxs[(nBlockHeight - confct)%bins][bucket];
244247 extraNum += oldUnconfTxs[bucket];
@@ -339,6 +342,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
339342 result->pass = passBucket;
340343 result->fail = failBucket;
341344 result->decay = decay;
345+ result->scale = scale;
342346 }
343347 return median;
344348}
@@ -358,15 +362,15 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
358362 // Read data file and do some very basic sanity checking
359363 // buckets and bucketMap are not updated yet, so don't access them
360364 // If there is a read failure, we'll just discard this entire object anyway
361- size_t maxConfirms;
365+ size_t maxConfirms, maxPeriods ;
362366
363367 // The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
364368 if (nFileVersion >= 149900 ) {
365369 filein >> decay;
366370 if (decay <= 0 || decay >= 1 ) {
367371 throw std::runtime_error (" Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)" );
368372 }
369- filein >> scale; // Unused for now
373+ filein >> scale;
370374 }
371375
372376 filein >> avg;
@@ -378,22 +382,24 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
378382 throw std::runtime_error (" Corrupt estimates file. Mismatch in tx count bucket count" );
379383 }
380384 filein >> confAvg;
381- maxConfirms = confAvg.size ();
385+ maxPeriods = confAvg.size ();
386+ maxConfirms = scale * maxPeriods;
387+
382388 if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7 ) { // one week
383389 throw std::runtime_error (" Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms" );
384390 }
385- for (unsigned int i = 0 ; i < maxConfirms ; i++) {
391+ for (unsigned int i = 0 ; i < maxPeriods ; i++) {
386392 if (confAvg[i].size () != numBuckets) {
387393 throw std::runtime_error (" Corrupt estimates file. Mismatch in feerate conf average bucket count" );
388394 }
389395 }
390396
391397 if (nFileVersion >= 149900 ) {
392398 filein >> failAvg;
393- if (maxConfirms != failAvg.size ()) {
399+ if (maxPeriods != failAvg.size ()) {
394400 throw std::runtime_error (" Corrupt estimates file. Mismatch in confirms tracked for failures" );
395401 }
396- for (unsigned int i = 0 ; i < maxConfirms ; i++) {
402+ for (unsigned int i = 0 ; i < maxPeriods ; i++) {
397403 if (failAvg[i].size () != numBuckets) {
398404 throw std::runtime_error (" Corrupt estimates file. Mismatch in one of failure average bucket counts" );
399405 }
@@ -449,8 +455,9 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
449455 blockIndex, bucketindex);
450456 }
451457 }
452- if (!inBlock && blocksAgo >= 1 ) {
453- for (size_t i = 0 ; i < blocksAgo && i < failAvg.size (); i++) {
458+ if (!inBlock && (unsigned int )blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
459+ unsigned int periodsAgo = blocksAgo / scale;
460+ for (size_t i = 0 ; i < periodsAgo && i < failAvg.size (); i++) {
454461 failAvg[i][bucketindex]++;
455462 }
456463 }
@@ -490,9 +497,9 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
490497 bucketMap[INF_FEERATE] = bucketIndex;
491498 assert (bucketMap.size () == buckets.size ());
492499
493- feeStats = new TxConfirmStats (buckets, bucketMap, MED_BLOCK_CONFIRMS , MED_DECAY);
494- shortStats = new TxConfirmStats (buckets, bucketMap, SHORT_BLOCK_CONFIRMS , SHORT_DECAY);
495- longStats = new TxConfirmStats (buckets, bucketMap, LONG_BLOCK_CONFIRMS , LONG_DECAY);
500+ feeStats = new TxConfirmStats (buckets, bucketMap, MED_BLOCK_PERIODS , MED_DECAY, MED_SCALE );
501+ shortStats = new TxConfirmStats (buckets, bucketMap, SHORT_BLOCK_PERIODS , SHORT_DECAY, SHORT_SCALE );
502+ longStats = new TxConfirmStats (buckets, bucketMap, LONG_BLOCK_PERIODS , LONG_DECAY, LONG_SCALE );
496503}
497504
498505CBlockPolicyEstimator::~CBlockPolicyEstimator ()
@@ -864,7 +871,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
864871
865872 std::map<double , unsigned int > tempMap;
866873
867- std::unique_ptr<TxConfirmStats> tempFeeStats (new TxConfirmStats (tempBuckets, tempMap, MED_BLOCK_CONFIRMS , tempDecay));
874+ std::unique_ptr<TxConfirmStats> tempFeeStats (new TxConfirmStats (tempBuckets, tempMap, MED_BLOCK_PERIODS , tempDecay, 1 ));
868875 tempFeeStats->Read (filein, nVersionThatWrote, tempNum);
869876 // if nVersionThatWrote < 139900 then another TxConfirmStats (for priority) follows but can be ignored.
870877
@@ -884,9 +891,9 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
884891 if (numBuckets <= 1 || numBuckets > 1000 )
885892 throw std::runtime_error (" Corrupt estimates file. Must have between 2 and 1000 feerate buckets" );
886893
887- std::unique_ptr<TxConfirmStats> fileFeeStats (new TxConfirmStats (buckets, bucketMap, MED_BLOCK_CONFIRMS , MED_DECAY));
888- std::unique_ptr<TxConfirmStats> fileShortStats (new TxConfirmStats (buckets, bucketMap, SHORT_BLOCK_CONFIRMS , SHORT_DECAY));
889- std::unique_ptr<TxConfirmStats> fileLongStats (new TxConfirmStats (buckets, bucketMap, LONG_BLOCK_CONFIRMS , LONG_DECAY));
894+ std::unique_ptr<TxConfirmStats> fileFeeStats (new TxConfirmStats (buckets, bucketMap, MED_BLOCK_PERIODS , MED_DECAY, MED_SCALE ));
895+ std::unique_ptr<TxConfirmStats> fileShortStats (new TxConfirmStats (buckets, bucketMap, SHORT_BLOCK_PERIODS , SHORT_DECAY, SHORT_SCALE ));
896+ std::unique_ptr<TxConfirmStats> fileLongStats (new TxConfirmStats (buckets, bucketMap, LONG_BLOCK_PERIODS , LONG_DECAY, LONG_SCALE ));
890897 fileFeeStats->Read (filein, nVersionThatWrote, numBuckets);
891898 fileShortStats->Read (filein, nVersionThatWrote, numBuckets);
892899 fileLongStats->Read (filein, nVersionThatWrote, numBuckets);
0 commit comments