fuzz coverage

Coverage Report

Created: 2025-06-01 19:34

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