@@ -53,13 +53,14 @@ class TxConfirmStats
53
53
54
54
double decay;
55
55
56
+ // Resolution (# of blocks) with which confirmations are tracked
56
57
unsigned int scale;
57
58
58
59
// Mempool counts of outstanding transactions
59
60
// For each bucket X, track the number of transactions in the mempool
60
61
// that are unconfirmed for each possible confirmation value Y
61
62
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
63
64
std::vector<int > oldUnconfTxs;
64
65
65
66
void resizeInMemoryCounters (size_t newbuckets);
@@ -73,7 +74,7 @@ class TxConfirmStats
73
74
* @param decay how much to decay the historical moving average per block
74
75
*/
75
76
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 );
77
78
78
79
/* * Roll the circular buffer for unconfirmed txs*/
79
80
void ClearCurrent (unsigned int nBlockHeight);
@@ -113,7 +114,7 @@ class TxConfirmStats
113
114
EstimationResult *result = nullptr ) const ;
114
115
115
116
/* * 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 (); }
117
118
118
119
/* * Write state of estimation data to a file*/
119
120
void Write (CAutoFile& fileout) const ;
@@ -128,17 +129,17 @@ class TxConfirmStats
128
129
129
130
TxConfirmStats::TxConfirmStats (const std::vector<double >& defaultBuckets,
130
131
const std::map<double , unsigned int >& defaultBucketMap,
131
- unsigned int maxConfirms , double _decay)
132
+ unsigned int maxPeriods , double _decay, unsigned int _scale )
132
133
: buckets(defaultBuckets), bucketMap(defaultBucketMap)
133
134
{
134
135
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++) {
138
139
confAvg[i].resize (buckets.size ());
139
140
}
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++) {
142
143
failAvg[i].resize (buckets.size ());
143
144
}
144
145
@@ -172,8 +173,9 @@ void TxConfirmStats::Record(int blocksToConfirm, double val)
172
173
// blocksToConfirm is 1-based
173
174
if (blocksToConfirm < 1 )
174
175
return ;
176
+ int periodsToConfirm = (blocksToConfirm + scale - 1 )/scale;
175
177
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++) {
177
179
confAvg[i - 1 ][bucketindex]++;
178
180
}
179
181
txCtAvg[bucketindex]++;
@@ -202,6 +204,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
202
204
double totalNum = 0 ; // Total number of tx's that were ever confirmed
203
205
int extraNum = 0 ; // Number of tx's still in mempool for confTarget or longer
204
206
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;
205
208
206
209
int maxbucketindex = buckets.size () - 1 ;
207
210
@@ -236,9 +239,9 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
236
239
newBucketRange = false ;
237
240
}
238
241
curFarBucket = bucket;
239
- nConf += confAvg[confTarget - 1 ][bucket];
242
+ nConf += confAvg[periodTarget - 1 ][bucket];
240
243
totalNum += txCtAvg[bucket];
241
- failNum += failAvg[confTarget - 1 ][bucket];
244
+ failNum += failAvg[periodTarget - 1 ][bucket];
242
245
for (unsigned int confct = confTarget; confct < GetMaxConfirms (); confct++)
243
246
extraNum += unconfTxs[(nBlockHeight - confct)%bins][bucket];
244
247
extraNum += oldUnconfTxs[bucket];
@@ -339,6 +342,7 @@ double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
339
342
result->pass = passBucket;
340
343
result->fail = failBucket;
341
344
result->decay = decay;
345
+ result->scale = scale;
342
346
}
343
347
return median;
344
348
}
@@ -358,15 +362,15 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
358
362
// Read data file and do some very basic sanity checking
359
363
// buckets and bucketMap are not updated yet, so don't access them
360
364
// If there is a read failure, we'll just discard this entire object anyway
361
- size_t maxConfirms;
365
+ size_t maxConfirms, maxPeriods ;
362
366
363
367
// The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
364
368
if (nFileVersion >= 149900 ) {
365
369
filein >> decay;
366
370
if (decay <= 0 || decay >= 1 ) {
367
371
throw std::runtime_error (" Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)" );
368
372
}
369
- filein >> scale; // Unused for now
373
+ filein >> scale;
370
374
}
371
375
372
376
filein >> avg;
@@ -378,22 +382,24 @@ void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets
378
382
throw std::runtime_error (" Corrupt estimates file. Mismatch in tx count bucket count" );
379
383
}
380
384
filein >> confAvg;
381
- maxConfirms = confAvg.size ();
385
+ maxPeriods = confAvg.size ();
386
+ maxConfirms = scale * maxPeriods;
387
+
382
388
if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7 ) { // one week
383
389
throw std::runtime_error (" Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms" );
384
390
}
385
- for (unsigned int i = 0 ; i < maxConfirms ; i++) {
391
+ for (unsigned int i = 0 ; i < maxPeriods ; i++) {
386
392
if (confAvg[i].size () != numBuckets) {
387
393
throw std::runtime_error (" Corrupt estimates file. Mismatch in feerate conf average bucket count" );
388
394
}
389
395
}
390
396
391
397
if (nFileVersion >= 149900 ) {
392
398
filein >> failAvg;
393
- if (maxConfirms != failAvg.size ()) {
399
+ if (maxPeriods != failAvg.size ()) {
394
400
throw std::runtime_error (" Corrupt estimates file. Mismatch in confirms tracked for failures" );
395
401
}
396
- for (unsigned int i = 0 ; i < maxConfirms ; i++) {
402
+ for (unsigned int i = 0 ; i < maxPeriods ; i++) {
397
403
if (failAvg[i].size () != numBuckets) {
398
404
throw std::runtime_error (" Corrupt estimates file. Mismatch in one of failure average bucket counts" );
399
405
}
@@ -449,8 +455,9 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
449
455
blockIndex, bucketindex);
450
456
}
451
457
}
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++) {
454
461
failAvg[i][bucketindex]++;
455
462
}
456
463
}
@@ -490,9 +497,9 @@ CBlockPolicyEstimator::CBlockPolicyEstimator()
490
497
bucketMap[INF_FEERATE] = bucketIndex;
491
498
assert (bucketMap.size () == buckets.size ());
492
499
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 );
496
503
}
497
504
498
505
CBlockPolicyEstimator::~CBlockPolicyEstimator ()
@@ -864,7 +871,7 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
864
871
865
872
std::map<double , unsigned int > tempMap;
866
873
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 ));
868
875
tempFeeStats->Read (filein, nVersionThatWrote, tempNum);
869
876
// if nVersionThatWrote < 139900 then another TxConfirmStats (for priority) follows but can be ignored.
870
877
@@ -884,9 +891,9 @@ bool CBlockPolicyEstimator::Read(CAutoFile& filein)
884
891
if (numBuckets <= 1 || numBuckets > 1000 )
885
892
throw std::runtime_error (" Corrupt estimates file. Must have between 2 and 1000 feerate buckets" );
886
893
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 ));
890
897
fileFeeStats->Read (filein, nVersionThatWrote, numBuckets);
891
898
fileShortStats->Read (filein, nVersionThatWrote, numBuckets);
892
899
fileLongStats->Read (filein, nVersionThatWrote, numBuckets);
0 commit comments