Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/policy/fees/block_policy_estimator.cpp
Line
Count
Source
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
6
#include <policy/fees/block_policy_estimator.h>
7
8
#include <common/system.h>
9
#include <consensus/amount.h>
10
#include <kernel/mempool_entry.h>
11
#include <logging.h>
12
#include <policy/feerate.h>
13
#include <primitives/transaction.h>
14
#include <random.h>
15
#include <serialize.h>
16
#include <streams.h>
17
#include <sync.h>
18
#include <tinyformat.h>
19
#include <uint256.h>
20
#include <util/fs.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 <utility>
34
35
// The current format written, and the version required to read. Must be
36
// increased to at least 309900+1 on the next breaking change.
37
constexpr int CURRENT_FEES_FILE_VERSION{309900};
38
39
static constexpr double INF_FEERATE = 1e99;
40
41
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
42
319
{
43
319
    switch (horizon) {
44
63
    case FeeEstimateHorizon::SHORT_HALFLIFE: return "short";
45
128
    case FeeEstimateHorizon::MED_HALFLIFE: return "medium";
46
128
    case FeeEstimateHorizon::LONG_HALFLIFE: return "long";
47
319
    } // no default case, so the compiler can warn about missing cases
48
319
    assert(false);
49
0
}
50
51
namespace {
52
53
struct EncodedDoubleFormatter
54
{
55
    template<typename Stream> void Ser(Stream &s, double v)
56
40.2M
    {
57
40.2M
        s << EncodeDouble(v);
58
40.2M
    }
59
60
    template<typename Stream> void Unser(Stream& s, double& v)
61
21.5M
    {
62
21.5M
        uint64_t encoded;
63
21.5M
        s >> encoded;
64
21.5M
        v = DecodeDouble(encoded);
65
21.5M
    }
66
};
67
68
} // namespace
69
70
/**
71
 * We will instantiate an instance of this class to track transactions that were
72
 * included in a block. We will lump transactions into a bucket according to their
73
 * approximate feerate and then track how long it took for those txs to be included in a block
74
 *
75
 * The tracking of unconfirmed (mempool) transactions is completely independent of the
76
 * historical tracking of transactions that have been confirmed in a block.
77
 */
78
class TxConfirmStats
79
{
80
private:
81
    //Define the buckets we will group transactions into
82
    const std::vector<double>& buckets;              // The upper-bound of the range for the bucket (inclusive)
83
    const std::map<double, unsigned int>& bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
84
85
    // For each bucket X:
86
    // Count the total # of txs in each bucket
87
    // Track the historical moving average of this total over blocks
88
    std::vector<double> txCtAvg;
89
90
    // Count the total # of txs confirmed within Y blocks in each bucket
91
    // Track the historical moving average of these totals over blocks
92
    std::vector<std::vector<double>> confAvg; // confAvg[Y][X]
93
94
    // Track moving avg of txs which have been evicted from the mempool
95
    // after failing to be confirmed within Y blocks
96
    std::vector<std::vector<double>> failAvg; // failAvg[Y][X]
97
98
    // Sum the total feerate of all tx's in each bucket
99
    // Track the historical moving average of this total over blocks
100
    std::vector<double> m_feerate_avg;
101
102
    // Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
103
    // Combine the total value with the tx counts to calculate the avg feerate per bucket
104
105
    double decay;
106
107
    // Resolution (# of blocks) with which confirmations are tracked
108
    unsigned int scale;
109
110
    // Mempool counts of outstanding transactions
111
    // For each bucket X, track the number of transactions in the mempool
112
    // that are unconfirmed for each possible confirmation value Y
113
    std::vector<std::vector<int> > unconfTxs;  //unconfTxs[Y][X]
114
    // transactions still unconfirmed after GetMaxConfirms for each bucket
115
    std::vector<int> oldUnconfTxs;
116
117
    void resizeInMemoryCounters(size_t newbuckets);
118
119
public:
120
    /**
121
     * Create new TxConfirmStats. This is called by BlockPolicyEstimator's
122
     * constructor with default values.
123
     * @param defaultBuckets contains the upper limits for the bucket boundaries
124
     * @param maxPeriods max number of periods to track
125
     * @param decay how much to decay the historical moving average per block
126
     */
127
    TxConfirmStats(const std::vector<double>& defaultBuckets, const std::map<double, unsigned int>& defaultBucketMap,
128
                   unsigned int maxPeriods, double decay, unsigned int scale);
129
130
    /** Roll the circular buffer for unconfirmed txs*/
131
    void ClearCurrent(unsigned int nBlockHeight);
132
133
    /**
134
     * Record a new transaction data point in the current block stats
135
     * @param blocksToConfirm the number of blocks it took this transaction to confirm
136
     * @param val the feerate of the transaction
137
     * @warning blocksToConfirm is 1-based and has to be >= 1
138
     */
139
    void Record(int blocksToConfirm, double val);
140
141
    /** Record a new transaction entering the mempool*/
142
    unsigned int NewTx(unsigned int nBlockHeight, double val);
143
144
    /** Remove a transaction from mempool tracking stats*/
145
    void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
146
                  unsigned int bucketIndex, bool inBlock);
147
148
    /** Update our estimates by decaying our historical moving average and updating
149
        with the data gathered from the current block */
150
    void UpdateMovingAverages();
151
152
    /**
153
     * Calculate a feerate estimate.  Find the lowest value bucket (or range of buckets
154
     * to make sure we have enough data points) whose transactions still have sufficient likelihood
155
     * of being confirmed within the target number of confirmations
156
     * @param confTarget target number of confirmations
157
     * @param sufficientTxVal required average number of transactions per block in a bucket range
158
     * @param minSuccess the success probability we require
159
     * @param nBlockHeight the current block height
160
     */
161
    double EstimateMedianVal(int confTarget, double sufficientTxVal,
162
                             double minSuccess, unsigned int nBlockHeight,
163
                             EstimationResult *result = nullptr) const;
164
165
    /** Return the max number of confirms we're tracking */
166
730M
    unsigned int GetMaxConfirms() const { return scale * confAvg.size(); }
167
168
    /** Write state of estimation data to a file*/
169
    void Write(AutoFile& fileout) const;
170
171
    /**
172
     * Read saved state of estimation data from a file and replace all internal data structures and
173
     * variables with this state.
174
     */
175
    void Read(AutoFile& filein, size_t numBuckets);
176
};
177
178
179
TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
180
                                const std::map<double, unsigned int>& defaultBucketMap,
181
                               unsigned int maxPeriods, double _decay, unsigned int _scale)
182
4.79k
    : buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
183
4.79k
{
184
4.79k
    assert(_scale != 0 && "_scale must be non-zero");
185
4.79k
    confAvg.resize(maxPeriods);
186
4.79k
    failAvg.resize(maxPeriods);
187
129k
    for (unsigned int i = 0; i < maxPeriods; i++) {
188
124k
        confAvg[i].resize(buckets.size());
189
124k
        failAvg[i].resize(buckets.size());
190
124k
    }
191
192
4.79k
    txCtAvg.resize(buckets.size());
193
4.79k
    m_feerate_avg.resize(buckets.size());
194
195
4.79k
    resizeInMemoryCounters(buckets.size());
196
4.79k
}
197
198
6.46k
void TxConfirmStats::resizeInMemoryCounters(size_t newbuckets) {
199
    // newbuckets must be passed in because the buckets referred to during Read have not been updated yet.
200
6.46k
    unconfTxs.resize(GetMaxConfirms());
201
2.30M
    for (unsigned int i = 0; i < unconfTxs.size(); i++) {
202
2.30M
        unconfTxs[i].resize(newbuckets);
203
2.30M
    }
204
6.46k
    oldUnconfTxs.resize(newbuckets);
205
6.46k
}
206
207
// Roll the unconfirmed txs circular buffer
208
void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight)
209
246k
{
210
58.6M
    for (unsigned int j = 0; j < buckets.size(); j++) {
211
58.3M
        oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j];
212
58.3M
        unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0;
213
58.3M
    }
214
246k
}
215
216
217
void TxConfirmStats::Record(int blocksToConfirm, double feerate)
218
130k
{
219
    // blocksToConfirm is 1-based
220
130k
    if (blocksToConfirm < 1)
221
0
        return;
222
130k
    int periodsToConfirm = (blocksToConfirm + scale - 1) / scale;
223
130k
    unsigned int bucketindex = bucketMap.lower_bound(feerate)->second;
224
3.38M
    for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) {
225
3.25M
        confAvg[i - 1][bucketindex]++;
226
3.25M
    }
227
130k
    txCtAvg[bucketindex]++;
228
130k
    m_feerate_avg[bucketindex] += feerate;
229
130k
}
230
231
void TxConfirmStats::UpdateMovingAverages()
232
246k
{
233
246k
    assert(confAvg.size() == failAvg.size());
234
58.6M
    for (unsigned int j = 0; j < buckets.size(); j++) {
235
1.57G
        for (unsigned int i = 0; i < confAvg.size(); i++) {
236
1.51G
            confAvg[i][j] *= decay;
237
1.51G
            failAvg[i][j] *= decay;
238
1.51G
        }
239
58.3M
        m_feerate_avg[j] *= decay;
240
58.3M
        txCtAvg[j] *= decay;
241
58.3M
    }
242
246k
}
243
244
// returns -1 on error conditions
245
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
246
                                         double successBreakPoint, unsigned int nBlockHeight,
247
                                         EstimationResult *result) const
248
22.0k
{
249
    // Counters for a bucket (or range of buckets)
250
22.0k
    double nConf = 0; // Number of tx's confirmed within the confTarget
251
22.0k
    double totalNum = 0; // Total number of tx's that were ever confirmed
252
22.0k
    int extraNum = 0;  // Number of tx's still in mempool for confTarget or longer
253
22.0k
    double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget
254
22.0k
    const int periodTarget = (confTarget + scale - 1) / scale;
255
22.0k
    const int maxbucketindex = buckets.size() - 1;
256
257
    // We'll combine buckets until we have enough samples.
258
    // The near and far variables will define the range we've combined
259
    // The best variables are the last range we saw which still had a high
260
    // enough confirmation rate to count as success.
261
    // The cur variables are the current range we're counting.
262
22.0k
    unsigned int curNearBucket = maxbucketindex;
263
22.0k
    unsigned int bestNearBucket = maxbucketindex;
264
22.0k
    unsigned int curFarBucket = maxbucketindex;
265
22.0k
    unsigned int bestFarBucket = maxbucketindex;
266
267
    // We'll always group buckets into sets that meet sufficientTxVal --
268
    // this ensures that we're using consistent groups between different
269
    // confirmation targets.
270
22.0k
    double partialNum = 0;
271
272
22.0k
    bool foundAnswer = false;
273
22.0k
    unsigned int bins = unconfTxs.size();
274
22.0k
    bool newBucketRange = true;
275
22.0k
    bool passing = true;
276
22.0k
    EstimatorBucket passBucket;
277
22.0k
    EstimatorBucket failBucket;
278
279
    // Start counting from highest feerate transactions
280
5.25M
    for (int bucket = maxbucketindex; bucket >= 0; --bucket) {
281
5.23M
        if (newBucketRange) {
282
58.6k
            curNearBucket = bucket;
283
58.6k
            newBucketRange = false;
284
58.6k
        }
285
5.23M
        curFarBucket = bucket;
286
5.23M
        nConf += confAvg[periodTarget - 1][bucket];
287
5.23M
        partialNum += txCtAvg[bucket];
288
5.23M
        totalNum += txCtAvg[bucket];
289
5.23M
        failNum += failAvg[periodTarget - 1][bucket];
290
729M
        for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)
291
724M
            extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];
292
5.23M
        extraNum += oldUnconfTxs[bucket];
293
        // If we have enough transaction data points in this range of buckets,
294
        // we can test for success
295
        // (Only count the confirmed data points, so that each confirmation count
296
        // will be looking at the same amount of data and same bucket breaks)
297
298
5.23M
        if (partialNum < sufficientTxVal / (1 - decay)) {
299
            // the buckets we've added in this round aren't sufficient
300
            // so keep adding
301
5.19M
            continue;
302
5.19M
        } else {
303
46.5k
            partialNum = 0; // reset for the next range we'll add
304
305
46.5k
            double curPct = nConf / (totalNum + failNum + extraNum);
306
307
            // Check to see if we are no longer getting confirmed at the success rate
308
46.5k
            if (curPct < successBreakPoint) {
309
9.98k
                if (passing == true) {
310
                    // First time we hit a failure record the failed bucket
311
841
                    unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
312
841
                    unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
313
841
                    failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
314
841
                    failBucket.end = buckets[failMaxBucket];
315
841
                    failBucket.withinTarget = nConf;
316
841
                    failBucket.totalConfirmed = totalNum;
317
841
                    failBucket.inMempool = extraNum;
318
841
                    failBucket.leftMempool = failNum;
319
841
                    passing = false;
320
841
                }
321
9.98k
                continue;
322
9.98k
            }
323
            // Otherwise update the cumulative stats, and the bucket variables
324
            // and reset the counters
325
36.5k
            else {
326
36.5k
                failBucket = EstimatorBucket(); // Reset any failed bucket, currently passing
327
36.5k
                foundAnswer = true;
328
36.5k
                passing = true;
329
36.5k
                passBucket.withinTarget = nConf;
330
36.5k
                nConf = 0;
331
36.5k
                passBucket.totalConfirmed = totalNum;
332
36.5k
                totalNum = 0;
333
36.5k
                passBucket.inMempool = extraNum;
334
36.5k
                passBucket.leftMempool = failNum;
335
36.5k
                failNum = 0;
336
36.5k
                extraNum = 0;
337
36.5k
                bestNearBucket = curNearBucket;
338
36.5k
                bestFarBucket = curFarBucket;
339
36.5k
                newBucketRange = true;
340
36.5k
            }
341
46.5k
        }
342
5.23M
    }
343
344
22.0k
    double median = -1;
345
22.0k
    double txSum = 0;
346
347
    // Calculate the "average" feerate of the best bucket range that met success conditions
348
    // Find the bucket with the median transaction and then report the average feerate from that bucket
349
    // This is a compromise between finding the median which we can't since we don't save all tx's
350
    // and reporting the average which is less accurate
351
22.0k
    unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
352
22.0k
    unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
353
1.46M
    for (unsigned int j = minBucket; j <= maxBucket; j++) {
354
1.44M
        txSum += txCtAvg[j];
355
1.44M
    }
356
22.0k
    if (foundAnswer && txSum != 0) {
357
12.8k
        txSum = txSum / 2;
358
34.7k
        for (unsigned int j = minBucket; j <= maxBucket; j++) {
359
34.7k
            if (txCtAvg[j] < txSum)
360
21.8k
                txSum -= txCtAvg[j];
361
12.8k
            else { // we're in the right bucket
362
12.8k
                median = m_feerate_avg[j] / txCtAvg[j];
363
12.8k
                break;
364
12.8k
            }
365
34.7k
        }
366
367
12.8k
        passBucket.start = minBucket ? buckets[minBucket-1] : 0;
368
12.8k
        passBucket.end = buckets[maxBucket];
369
12.8k
    }
370
371
    // If we were passing until we reached last few buckets with insufficient data, then report those as failed
372
22.0k
    if (passing && !newBucketRange) {
373
21.3k
        unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
374
21.3k
        unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
375
21.3k
        failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
376
21.3k
        failBucket.end = buckets[failMaxBucket];
377
21.3k
        failBucket.withinTarget = nConf;
378
21.3k
        failBucket.totalConfirmed = totalNum;
379
21.3k
        failBucket.inMempool = extraNum;
380
21.3k
        failBucket.leftMempool = failNum;
381
21.3k
    }
382
383
22.0k
    float passed_within_target_perc = 0.0;
384
22.0k
    float failed_within_target_perc = 0.0;
385
22.0k
    if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {
386
12.8k
        passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);
387
12.8k
    }
388
22.0k
    if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {
389
15.4k
        failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);
390
15.4k
    }
391
392
22.0k
    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",
393
22.0k
             confTarget, 100.0 * successBreakPoint, decay,
394
22.0k
             median, passBucket.start, passBucket.end,
395
22.0k
             passed_within_target_perc,
396
22.0k
             passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,
397
22.0k
             failBucket.start, failBucket.end,
398
22.0k
             failed_within_target_perc,
399
22.0k
             failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);
400
401
402
22.0k
    if (result) {
403
22.0k
        result->pass = passBucket;
404
22.0k
        result->fail = failBucket;
405
22.0k
        result->decay = decay;
406
22.0k
        result->scale = scale;
407
22.0k
    }
408
22.0k
    return median;
409
22.0k
}
410
411
void TxConfirmStats::Write(AutoFile& fileout) const
412
3.12k
{
413
3.12k
    fileout << Using<EncodedDoubleFormatter>(decay);
414
3.12k
    fileout << scale;
415
3.12k
    fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
416
3.12k
    fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
417
3.12k
    fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
418
3.12k
    fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
419
3.12k
}
420
421
void TxConfirmStats::Read(AutoFile& filein, size_t numBuckets)
422
1.67k
{
423
    // Read data file and do some very basic sanity checking
424
    // buckets and bucketMap are not updated yet, so don't access them
425
    // If there is a read failure, we'll just discard this entire object anyway
426
1.67k
    uint64_t maxConfirms, maxPeriods;
427
428
    // The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
429
1.67k
    filein >> Using<EncodedDoubleFormatter>(decay);
430
1.67k
    if (decay <= 0 || decay >= 1) {
431
0
        throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
432
0
    }
433
1.67k
    filein >> scale;
434
1.67k
    if (scale == 0) {
435
0
        throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
436
0
    }
437
438
1.67k
    filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
439
1.67k
    if (m_feerate_avg.size() != numBuckets) {
440
0
        throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
441
0
    }
442
1.67k
    filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
443
1.67k
    if (txCtAvg.size() != numBuckets) {
444
0
        throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
445
0
    }
446
1.67k
    filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
447
1.67k
    maxPeriods = confAvg.size();
448
1.67k
    maxConfirms = scale * maxPeriods;
449
450
1.67k
    if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) { // one week
451
0
        throw std::runtime_error("Corrupt estimates file.  Must maintain estimates for between 1 and 1008 (one week) confirms");
452
0
    }
453
45.1k
    for (unsigned int i = 0; i < maxPeriods; i++) {
454
43.4k
        if (confAvg[i].size() != numBuckets) {
455
0
            throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count");
456
0
        }
457
43.4k
    }
458
459
1.67k
    filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
460
1.67k
    if (maxPeriods != failAvg.size()) {
461
0
        throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures");
462
0
    }
463
45.1k
    for (unsigned int i = 0; i < maxPeriods; i++) {
464
43.4k
        if (failAvg[i].size() != numBuckets) {
465
0
            throw std::runtime_error("Corrupt estimates file. Mismatch in one of failure average bucket counts");
466
0
        }
467
43.4k
    }
468
469
    // Resize the current block variables which aren't stored in the data file
470
    // to match the number of confirms and buckets
471
1.67k
    resizeInMemoryCounters(numBuckets);
472
473
1.67k
    LogDebug(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n",
474
1.67k
             numBuckets, maxConfirms);
475
1.67k
}
476
477
unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)
478
137k
{
479
137k
    unsigned int bucketindex = bucketMap.lower_bound(val)->second;
480
137k
    unsigned int blockIndex = nBlockHeight % unconfTxs.size();
481
137k
    unconfTxs[blockIndex][bucketindex]++;
482
137k
    return bucketindex;
483
137k
}
484
485
void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketindex, bool inBlock)
486
137k
{
487
    //nBestSeenHeight is not updated yet for the new block
488
137k
    int blocksAgo = nBestSeenHeight - entryHeight;
489
137k
    if (nBestSeenHeight == 0)  // the BlockPolicyEstimator hasn't seen any blocks yet
490
0
        blocksAgo = 0;
491
137k
    if (blocksAgo < 0) {
492
0
        LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, blocks ago is negative for mempool tx\n");
493
0
        return;  //This can't happen because we call this with our best seen height, no entries can have higher
494
0
    }
495
496
137k
    if (blocksAgo >= (int)unconfTxs.size()) {
497
4.60k
        if (oldUnconfTxs[bucketindex] > 0) {
498
4.60k
            oldUnconfTxs[bucketindex]--;
499
4.60k
        } else {
500
0
            LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n",
501
0
                     bucketindex);
502
0
        }
503
4.60k
    }
504
133k
    else {
505
133k
        unsigned int blockIndex = entryHeight % unconfTxs.size();
506
133k
        if (unconfTxs[blockIndex][bucketindex] > 0) {
507
133k
            unconfTxs[blockIndex][bucketindex]--;
508
133k
        } else {
509
0
            LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n",
510
0
                     blockIndex, bucketindex);
511
0
        }
512
133k
    }
513
137k
    if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
514
920
        assert(scale != 0);
515
920
        unsigned int periodsAgo = blocksAgo / scale;
516
2.44k
        for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) {
517
1.52k
            failAvg[i][bucketindex]++;
518
1.52k
        }
519
920
    }
520
137k
}
521
522
bool CBlockPolicyEstimator::removeTx(Txid hash)
523
1.85k
{
524
1.85k
    LOCK(m_cs_fee_estimator);
525
1.85k
    return _removeTx(hash, /*inBlock=*/false);
526
1.85k
}
527
528
bool CBlockPolicyEstimator::_removeTx(const Txid& hash, bool inBlock)
529
49.1k
{
530
49.1k
    AssertLockHeld(m_cs_fee_estimator);
531
49.1k
    std::map<Txid, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
532
49.1k
    if (pos != mapMemPoolTxs.end()) {
533
45.9k
        feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
534
45.9k
        shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
535
45.9k
        longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
536
45.9k
        mapMemPoolTxs.erase(hash);
537
45.9k
        return true;
538
45.9k
    } else {
539
3.20k
        return false;
540
3.20k
    }
541
49.1k
}
542
543
CBlockPolicyEstimator::CBlockPolicyEstimator(const fs::path& estimation_filepath, const bool read_stale_estimates)
544
1.04k
    : m_estimation_filepath{estimation_filepath}
545
1.04k
{
546
1.04k
    static_assert(MIN_BUCKET_FEERATE > 0, "Min feerate must be nonzero");
547
1.04k
    size_t bucketIndex = 0;
548
549
246k
    for (double bucketBoundary = MIN_BUCKET_FEERATE; bucketBoundary <= MAX_BUCKET_FEERATE; bucketBoundary *= FEE_SPACING, bucketIndex++) {
550
245k
        buckets.push_back(bucketBoundary);
551
245k
        bucketMap[bucketBoundary] = bucketIndex;
552
245k
    }
553
1.04k
    buckets.push_back(INF_FEERATE);
554
1.04k
    bucketMap[INF_FEERATE] = bucketIndex;
555
1.04k
    assert(bucketMap.size() == buckets.size());
556
557
1.04k
    feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
558
1.04k
    shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
559
1.04k
    longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
560
561
1.04k
    AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "rb")};
562
563
1.04k
    if (est_file.IsNull()) {
564
482
        LogInfo("%s is not found. Continue anyway.", fs::PathToString(m_estimation_filepath));
565
482
        return;
566
482
    }
567
568
558
    std::chrono::hours file_age = GetFeeEstimatorFileAge();
569
558
    if (file_age > MAX_FILE_AGE && !read_stale_estimates) {
570
1
        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));
571
1
        return;
572
1
    }
573
574
557
    if (!Read(est_file)) {
575
0
        LogWarning("Failed to read fee estimates from %s. Continue anyway.", fs::PathToString(m_estimation_filepath));
576
0
    }
577
557
}
578
579
1.04k
CBlockPolicyEstimator::~CBlockPolicyEstimator() = default;
580
581
void CBlockPolicyEstimator::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /*unused*/)
582
49.8k
{
583
49.8k
    processTransaction(tx);
584
49.8k
}
585
586
void CBlockPolicyEstimator::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason /*unused*/, uint64_t /*unused*/)
587
1.85k
{
588
1.85k
    removeTx(tx->GetHash());
589
1.85k
}
590
591
void CBlockPolicyEstimator::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
592
98.1k
{
593
98.1k
    processBlock(txs_removed_for_block, nBlockHeight);
594
98.1k
}
595
596
void CBlockPolicyEstimator::processTransaction(const NewMempoolTransactionInfo& tx)
597
49.8k
{
598
49.8k
    LOCK(m_cs_fee_estimator);
599
49.8k
    const unsigned int txHeight = tx.info.txHeight;
600
49.8k
    const auto& hash = tx.info.m_tx->GetHash();
601
49.8k
    if (mapMemPoolTxs.contains(hash)) {
602
0
        LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n",
603
0
                 hash.ToString());
604
0
        return;
605
0
    }
606
607
49.8k
    if (txHeight != nBestSeenHeight) {
608
        // Ignore side chains and re-orgs; assuming they are random they don't
609
        // affect the estimate.  We'll potentially double count transactions in 1-block reorgs.
610
        // Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip().
611
        // It will be synced next time a block is processed.
612
514
        return;
613
514
    }
614
    // This transaction should only count for fee estimation if:
615
    // - it's not being re-added during a reorg which bypasses typical mempool fee limits
616
    // - the node is not behind
617
    // - the transaction is not dependent on any other transactions in the mempool
618
    // - it's not part of a package.
619
49.3k
    const bool validForFeeEstimation = !tx.m_mempool_limit_bypassed && !tx.m_submitted_in_package && tx.m_chainstate_is_current && tx.m_has_no_mempool_parents;
620
621
    // Only want to be updating estimates when our blockchain is synced,
622
    // otherwise we'll miscalculate how many blocks its taking to get included.
623
49.3k
    if (!validForFeeEstimation) {
624
3.38k
        untrackedTxs++;
625
3.38k
        return;
626
3.38k
    }
627
45.9k
    trackedTxs++;
628
629
    // Feerates are stored and reported as BTC-per-kb:
630
45.9k
    const CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size);
631
632
45.9k
    mapMemPoolTxs[hash].blockHeight = txHeight;
633
45.9k
    unsigned int bucketIndex = feeStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
634
45.9k
    mapMemPoolTxs[hash].bucketIndex = bucketIndex;
635
45.9k
    unsigned int bucketIndex2 = shortStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
636
45.9k
    assert(bucketIndex == bucketIndex2);
637
45.9k
    unsigned int bucketIndex3 = longStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK()));
638
45.9k
    assert(bucketIndex == bucketIndex3);
639
45.9k
}
640
641
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const RemovedMempoolTransactionInfo& tx)
642
46.2k
{
643
46.2k
    AssertLockHeld(m_cs_fee_estimator);
644
46.2k
    if (!_removeTx(tx.info.m_tx->GetHash(), true)) {
645
        // This transaction wasn't being tracked for fee estimation
646
2.58k
        return false;
647
2.58k
    }
648
649
    // How many blocks did it take for miners to include this transaction?
650
    // blocksToConfirm is 1-based, so a transaction included in the earliest
651
    // possible block has confirmation count of 1
652
43.6k
    int blocksToConfirm = nBlockHeight - tx.info.txHeight;
653
43.6k
    if (blocksToConfirm <= 0) {
654
        // This can't happen because we don't process transactions from a block with a height
655
        // lower than our greatest seen height
656
0
        LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error Transaction had negative blocksToConfirm\n");
657
0
        return false;
658
0
    }
659
660
    // Feerates are stored and reported as BTC-per-kb:
661
43.6k
    CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size);
662
663
43.6k
    feeStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
664
43.6k
    shortStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
665
43.6k
    longStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK()));
666
43.6k
    return true;
667
43.6k
}
668
669
void CBlockPolicyEstimator::processBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block,
670
                                         unsigned int nBlockHeight)
671
98.1k
{
672
98.1k
    LOCK(m_cs_fee_estimator);
673
98.1k
    if (nBlockHeight <= nBestSeenHeight) {
674
        // Ignore side chains and re-orgs; assuming they are random
675
        // they don't affect the estimate.
676
        // And if an attacker can re-org the chain at will, then
677
        // you've got much bigger problems than "attacker can influence
678
        // transaction fees."
679
16.0k
        return;
680
16.0k
    }
681
682
    // Must update nBestSeenHeight in sync with ClearCurrent so that
683
    // calls to removeTx (via processBlockTx) correctly calculate age
684
    // of unconfirmed txs to remove from tracking.
685
82.0k
    nBestSeenHeight = nBlockHeight;
686
687
    // Update unconfirmed circular buffer
688
82.0k
    feeStats->ClearCurrent(nBlockHeight);
689
82.0k
    shortStats->ClearCurrent(nBlockHeight);
690
82.0k
    longStats->ClearCurrent(nBlockHeight);
691
692
    // Decay all exponential averages
693
82.0k
    feeStats->UpdateMovingAverages();
694
82.0k
    shortStats->UpdateMovingAverages();
695
82.0k
    longStats->UpdateMovingAverages();
696
697
82.0k
    unsigned int countedTxs = 0;
698
    // Update averages with data points from current block
699
82.0k
    for (const auto& tx : txs_removed_for_block) {
700
46.2k
        if (processBlockTx(nBlockHeight, tx))
701
43.6k
            countedTxs++;
702
46.2k
    }
703
704
82.0k
    if (firstRecordedHeight == 0 && countedTxs > 0) {
705
245
        firstRecordedHeight = nBestSeenHeight;
706
245
        LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy first recorded height %u\n", firstRecordedHeight);
707
245
    }
708
709
710
82.0k
    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",
711
82.0k
             countedTxs, txs_removed_for_block.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
712
82.0k
             MaxUsableEstimate(), HistoricalBlockSpan() > BlockSpan() ? "historical" : "current");
713
714
82.0k
    trackedTxs = 0;
715
82.0k
    untrackedTxs = 0;
716
82.0k
}
717
718
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const
719
94
{
720
    // It's not possible to get reasonable estimates for confTarget of 1
721
94
    if (confTarget <= 1)
722
6
        return CFeeRate(0);
723
724
88
    return estimateRawFee(confTarget, DOUBLE_SUCCESS_PCT, FeeEstimateHorizon::MED_HALFLIFE);
725
94
}
726
727
CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult* result) const
728
407
{
729
407
    TxConfirmStats* stats = nullptr;
730
407
    double sufficientTxs = SUFFICIENT_FEETXS;
731
407
    switch (horizon) {
732
63
    case FeeEstimateHorizon::SHORT_HALFLIFE: {
733
63
        stats = shortStats.get();
734
63
        sufficientTxs = SUFFICIENT_TXS_SHORT;
735
63
        break;
736
0
    }
737
216
    case FeeEstimateHorizon::MED_HALFLIFE: {
738
216
        stats = feeStats.get();
739
216
        break;
740
0
    }
741
128
    case FeeEstimateHorizon::LONG_HALFLIFE: {
742
128
        stats = longStats.get();
743
128
        break;
744
0
    }
745
407
    } // no default case, so the compiler can warn about missing cases
746
407
    assert(stats);
747
748
407
    LOCK(m_cs_fee_estimator);
749
    // Return failure if trying to analyze a target we're not tracking
750
407
    if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms())
751
0
        return CFeeRate(0);
752
407
    if (successThreshold > 1)
753
0
        return CFeeRate(0);
754
755
407
    double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
756
757
407
    if (median < 0)
758
25
        return CFeeRate(0);
759
760
382
    return CFeeRate(llround(median));
761
407
}
762
763
unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const
764
4.33k
{
765
4.33k
    LOCK(m_cs_fee_estimator);
766
4.33k
    switch (horizon) {
767
128
    case FeeEstimateHorizon::SHORT_HALFLIFE: {
768
128
        return shortStats->GetMaxConfirms();
769
0
    }
770
128
    case FeeEstimateHorizon::MED_HALFLIFE: {
771
128
        return feeStats->GetMaxConfirms();
772
0
    }
773
4.07k
    case FeeEstimateHorizon::LONG_HALFLIFE: {
774
4.07k
        return longStats->GetMaxConfirms();
775
0
    }
776
4.33k
    } // no default case, so the compiler can warn about missing cases
777
4.33k
    assert(false);
778
0
}
779
780
unsigned int CBlockPolicyEstimator::BlockSpan() const
781
171k
{
782
171k
    if (firstRecordedHeight == 0) return 0;
783
171k
    assert(nBestSeenHeight >= firstRecordedHeight);
784
785
27.0k
    return nBestSeenHeight - firstRecordedHeight;
786
27.0k
}
787
788
unsigned int CBlockPolicyEstimator::HistoricalBlockSpan() const
789
171k
{
790
171k
    if (historicalFirst == 0) return 0;
791
171k
    assert(historicalBest >= historicalFirst);
792
793
4.29k
    if (nBestSeenHeight - historicalBest > OLDEST_ESTIMATE_HISTORY) return 0;
794
795
4.29k
    return historicalBest - historicalFirst;
796
4.29k
}
797
798
unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const
799
88.6k
{
800
    // Block spans are divided by 2 to make sure there are enough potential failing data points for the estimate
801
88.6k
    return std::min(longStats->GetMaxConfirms(), std::max(BlockSpan(), HistoricalBlockSpan()) / 2);
802
88.6k
}
803
804
/** Return a fee estimate at the required successThreshold from the shortest
805
 * time horizon which tracks confirmations up to the desired target.  If
806
 * checkShorterHorizon is requested, also allow short time horizon estimates
807
 * for a lower target to reduce the given answer */
808
double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
809
14.4k
{
810
14.4k
    double estimate = -1;
811
14.4k
    if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
812
        // Find estimate from shortest time horizon possible
813
14.4k
        if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
814
11.3k
            estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
815
11.3k
        }
816
3.10k
        else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
817
1.54k
            estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
818
1.54k
        }
819
1.56k
        else { // long horizon
820
1.56k
            estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
821
1.56k
        }
822
14.4k
        if (checkShorterHorizon) {
823
14.3k
            EstimationResult tempResult;
824
            // If a lower confTarget from a more recent horizon returns a lower answer use it.
825
14.3k
            if (confTarget > feeStats->GetMaxConfirms()) {
826
1.56k
                double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
827
1.56k
                if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
828
781
                    estimate = medMax;
829
781
                    if (result) *result = tempResult;
830
781
                }
831
1.56k
            }
832
14.3k
            if (confTarget > shortStats->GetMaxConfirms()) {
833
3.10k
                double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
834
3.10k
                if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
835
160
                    estimate = shortMax;
836
160
                    if (result) *result = tempResult;
837
160
                }
838
3.10k
            }
839
14.3k
        }
840
14.4k
    }
841
14.4k
    return estimate;
842
14.4k
}
843
844
/** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met
845
 * at 2 * target for any longer time horizons.
846
 */
847
double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
848
1.61k
{
849
1.61k
    double estimate = -1;
850
1.61k
    EstimationResult tempResult;
851
1.61k
    if (doubleTarget <= shortStats->GetMaxConfirms()) {
852
1.24k
        estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);
853
1.24k
    }
854
1.61k
    if (doubleTarget <= feeStats->GetMaxConfirms()) {
855
1.36k
        double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);
856
1.36k
        if (longEstimate > estimate) {
857
1
            estimate = longEstimate;
858
1
            if (result) *result = tempResult;
859
1
        }
860
1.36k
    }
861
1.61k
    return estimate;
862
1.61k
}
863
864
/** estimateSmartFee returns the max of the feerates calculated with a 60%
865
 * threshold required at target / 2, an 85% threshold required at target and a
866
 * 95% threshold required at 2 * target.  Each calculation is performed at the
867
 * shortest time horizon which tracks the required target.  Conservative
868
 * estimates, however, required the 95% threshold at 2 * target be met for any
869
 * longer time horizons also.
870
 */
871
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
872
6.59k
{
873
6.59k
    LOCK(m_cs_fee_estimator);
874
875
6.59k
    if (feeCalc) {
876
2.89k
        feeCalc->desiredTarget = confTarget;
877
2.89k
        feeCalc->returnedTarget = confTarget;
878
2.89k
        feeCalc->best_height = nBestSeenHeight;
879
2.89k
    }
880
881
6.59k
    double median = -1;
882
6.59k
    EstimationResult tempResult;
883
884
    // Return failure if trying to analyze a target we're not tracking
885
6.59k
    if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) {
886
0
        return CFeeRate(0);  // error condition
887
0
    }
888
889
    // It's not possible to get reasonable estimates for confTarget of 1
890
6.59k
    if (confTarget == 1) confTarget = 2;
891
892
6.59k
    unsigned int maxUsableEstimate = MaxUsableEstimate();
893
6.59k
    if ((unsigned int)confTarget > maxUsableEstimate) {
894
5.57k
        confTarget = maxUsableEstimate;
895
5.57k
    }
896
6.59k
    if (feeCalc) feeCalc->returnedTarget = confTarget;
897
898
6.59k
    if (confTarget <= 1) return CFeeRate(0); // error condition
899
900
6.59k
    assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
901
    /** true is passed to estimateCombined fee for target/2 and target so
902
     * that we check the max confirms for shorter time horizons as well.
903
     * This is necessary to preserve monotonically increasing estimates.
904
     * For non-conservative estimates we do the same thing for 2*target, but
905
     * for conservative estimates we want to skip these shorter horizons
906
     * checks for 2*target because we are taking the max over all time
907
     * horizons so we already have monotonically increasing estimates and
908
     * the purpose of conservative estimates is not to let short term
909
     * fluctuations lower our estimates by too much.
910
     *
911
     * Note: In certain rare edge cases, monotonically increasing estimates may
912
     * not be guaranteed. Specifically, given two targets N and M, where M > N,
913
     * if a sub-estimate for target N fails to return a valid fee rate, while
914
     * target M has valid fee rate for that sub-estimate, target M may result
915
     * in a higher fee rate estimate than target N.
916
     *
917
     * See: https://github.com/bitcoin/bitcoin/issues/11800#issuecomment-349697807
918
     */
919
4.80k
    double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
920
4.80k
    if (feeCalc) {
921
2.10k
        feeCalc->est = tempResult;
922
2.10k
        feeCalc->reason = FeeReason::HALF_ESTIMATE;
923
2.10k
    }
924
4.80k
    median = halfEst;
925
4.80k
    double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
926
4.80k
    if (actualEst > median) {
927
51
        median = actualEst;
928
51
        if (feeCalc) {
929
45
            feeCalc->est = tempResult;
930
45
            feeCalc->reason = FeeReason::FULL_ESTIMATE;
931
45
        }
932
51
    }
933
4.80k
    double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
934
4.80k
    if (doubleEst > median) {
935
7
        median = doubleEst;
936
7
        if (feeCalc) {
937
7
            feeCalc->est = tempResult;
938
7
            feeCalc->reason = FeeReason::DOUBLE_ESTIMATE;
939
7
        }
940
7
    }
941
942
4.80k
    if (conservative || median == -1) {
943
1.61k
        double consEst =  estimateConservativeFee(2 * confTarget, &tempResult);
944
1.61k
        if (consEst > median) {
945
49
            median = consEst;
946
49
            if (feeCalc) {
947
49
                feeCalc->est = tempResult;
948
49
                feeCalc->reason = FeeReason::CONSERVATIVE;
949
49
            }
950
49
        }
951
1.61k
    }
952
953
4.80k
    if (median < 0) return CFeeRate(0); // error condition
954
955
3.24k
    return CFeeRate(llround(median));
956
4.80k
}
957
958
1.03k
void CBlockPolicyEstimator::Flush() {
959
1.03k
    FlushUnconfirmed();
960
1.03k
    FlushFeeEstimates();
961
1.03k
}
962
963
void CBlockPolicyEstimator::FlushFeeEstimates()
964
1.04k
{
965
1.04k
    AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "wb")};
966
1.04k
    if (est_file.IsNull() || !Write(est_file)) {
967
0
        LogWarning("Failed to write fee estimates to %s. Continue anyway.", fs::PathToString(m_estimation_filepath));
968
0
        (void)est_file.fclose();
969
0
        return;
970
0
    }
971
1.04k
    if (est_file.fclose() != 0) {
972
0
        LogWarning("Failed to close fee estimates file %s: %s. Continuing anyway.", fs::PathToString(m_estimation_filepath), SysErrorString(errno));
973
0
        return;
974
0
    }
975
1.04k
    LogDebug(BCLog::ESTIMATEFEE, "Flushed fee estimates to %s.", fs::PathToString(m_estimation_filepath));
976
1.04k
}
977
978
bool CBlockPolicyEstimator::Write(AutoFile& fileout) const
979
1.04k
{
980
1.04k
    try {
981
1.04k
        LOCK(m_cs_fee_estimator);
982
1.04k
        fileout << CURRENT_FEES_FILE_VERSION;
983
1.04k
        fileout << nBestSeenHeight;
984
1.04k
        if (BlockSpan() > HistoricalBlockSpan()/2) {
985
169
            fileout << firstRecordedHeight << nBestSeenHeight;
986
169
        }
987
874
        else {
988
874
            fileout << historicalFirst << historicalBest;
989
874
        }
990
1.04k
        fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets);
991
1.04k
        feeStats->Write(fileout);
992
1.04k
        shortStats->Write(fileout);
993
1.04k
        longStats->Write(fileout);
994
1.04k
    }
995
1.04k
    catch (const std::exception&) {
996
0
        LogWarning("Unable to write policy estimator data (non-fatal)");
997
0
        return false;
998
0
    }
999
1.04k
    return true;
1000
1.04k
}
1001
1002
bool CBlockPolicyEstimator::Read(AutoFile& filein)
1003
557
{
1004
557
    try {
1005
557
        LOCK(m_cs_fee_estimator);
1006
557
        int nVersionRequired;
1007
557
        filein >> nVersionRequired;
1008
557
        if (nVersionRequired > CURRENT_FEES_FILE_VERSION) {
1009
0
            throw std::runtime_error{strprintf("File version (%d) too high to be read.", nVersionRequired)};
1010
0
        }
1011
1012
        // Read fee estimates file into temporary variables so existing data
1013
        // structures aren't corrupted if there is an exception.
1014
557
        unsigned int nFileBestSeenHeight;
1015
557
        filein >> nFileBestSeenHeight;
1016
1017
557
        if (nVersionRequired < CURRENT_FEES_FILE_VERSION) {
1018
0
            LogWarning("Incompatible old fee estimation data (non-fatal). Version: %d", nVersionRequired);
1019
557
        } else { // nVersionRequired == CURRENT_FEES_FILE_VERSION
1020
557
            unsigned int nFileHistoricalFirst, nFileHistoricalBest;
1021
557
            filein >> nFileHistoricalFirst >> nFileHistoricalBest;
1022
557
            if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
1023
0
                throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
1024
0
            }
1025
557
            std::vector<double> fileBuckets;
1026
557
            filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
1027
557
            size_t numBuckets = fileBuckets.size();
1028
557
            if (numBuckets <= 1 || numBuckets > 1000) {
1029
0
                throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
1030
0
            }
1031
1032
557
            std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
1033
557
            std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
1034
557
            std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
1035
557
            fileFeeStats->Read(filein, numBuckets);
1036
557
            fileShortStats->Read(filein, numBuckets);
1037
557
            fileLongStats->Read(filein, numBuckets);
1038
1039
            // Fee estimates file parsed correctly
1040
            // Copy buckets from file and refresh our bucketmap
1041
557
            buckets = fileBuckets;
1042
557
            bucketMap.clear();
1043
132k
            for (unsigned int i = 0; i < buckets.size(); i++) {
1044
132k
                bucketMap[buckets[i]] = i;
1045
132k
            }
1046
1047
            // Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
1048
557
            feeStats = std::move(fileFeeStats);
1049
557
            shortStats = std::move(fileShortStats);
1050
557
            longStats = std::move(fileLongStats);
1051
1052
557
            nBestSeenHeight = nFileBestSeenHeight;
1053
557
            historicalFirst = nFileHistoricalFirst;
1054
557
            historicalBest = nFileHistoricalBest;
1055
557
        }
1056
557
    }
1057
557
    catch (const std::exception& e) {
1058
0
        LogWarning("Unable to read policy estimator data (non-fatal): %s", e.what());
1059
0
        return false;
1060
0
    }
1061
557
    return true;
1062
557
}
1063
1064
void CBlockPolicyEstimator::FlushUnconfirmed()
1065
1.03k
{
1066
1.03k
    const auto startclear{SteadyClock::now()};
1067
1.03k
    LOCK(m_cs_fee_estimator);
1068
1.03k
    size_t num_entries = mapMemPoolTxs.size();
1069
    // Remove every entry in mapMemPoolTxs
1070
2.11k
    while (!mapMemPoolTxs.empty()) {
1071
1.07k
        auto mi = mapMemPoolTxs.begin();
1072
1.07k
        _removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs
1073
1.07k
    }
1074
1.03k
    const auto endclear{SteadyClock::now()};
1075
1.03k
    LogDebug(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %.3fs\n", num_entries, Ticks<SecondsDouble>(endclear - startclear));
1076
1.03k
}
1077
1078
std::chrono::hours CBlockPolicyEstimator::GetFeeEstimatorFileAge()
1079
558
{
1080
558
    auto file_time{fs::last_write_time(m_estimation_filepath)};
1081
558
    auto now{fs::file_time_type::clock::now()};
1082
558
    return std::chrono::duration_cast<std::chrono::hours>(now - file_time);
1083
558
}
1084
1085
static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee,
1086
                                   double max_filter_fee_rate,
1087
                                   double fee_filter_spacing)
1088
1.16k
{
1089
1.16k
    std::set<double> fee_set;
1090
1091
1.16k
    const CAmount min_fee_limit{std::max(CAmount(1), min_incremental_fee.GetFeePerK() / 2)};
1092
1.16k
    fee_set.insert(0);
1093
1.16k
    for (double bucket_boundary = min_fee_limit;
1094
151k
         bucket_boundary <= max_filter_fee_rate;
1095
150k
         bucket_boundary *= fee_filter_spacing) {
1096
1097
150k
        fee_set.insert(bucket_boundary);
1098
150k
    }
1099
1100
1.16k
    return fee_set;
1101
1.16k
}
1102
1103
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee, FastRandomContext& rng)
1104
1.16k
    : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)},
1105
1.16k
      insecure_rand{rng}
1106
1.16k
{
1107
1.16k
}
1108
1109
CAmount FeeFilterRounder::round(CAmount currentMinFee)
1110
3.28k
{
1111
3.28k
    AssertLockNotHeld(m_insecure_rand_mutex);
1112
3.28k
    std::set<double>::iterator it = m_fee_set.lower_bound(currentMinFee);
1113
3.28k
    if (it == m_fee_set.end() ||
1114
3.28k
        (it != m_fee_set.begin() &&
1115
2.49k
         WITH_LOCK(m_insecure_rand_mutex, return insecure_rand.rand32()) % 3 != 0)) {
1116
854
        --it;
1117
854
    }
1118
3.28k
    return static_cast<CAmount>(*it);
1119
3.28k
}