/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/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 289900+1 on the next breaking change. | 
| 37 |  | constexpr int CURRENT_FEES_FILE_VERSION{149900}; | 
| 38 |  |  | 
| 39 |  | static constexpr double INF_FEERATE = 1e99; | 
| 40 |  |  | 
| 41 |  | std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon) | 
| 42 | 0 | { | 
| 43 | 0 |     switch (horizon) { | 
| 44 | 0 |     case FeeEstimateHorizon::SHORT_HALFLIFE: return "short"; | 
| 45 | 0 |     case FeeEstimateHorizon::MED_HALFLIFE: return "medium"; | 
| 46 | 0 |     case FeeEstimateHorizon::LONG_HALFLIFE: return "long"; | 
| 47 | 0 |     } // no default case, so the compiler can warn about missing cases | 
| 48 | 0 |     assert(false); | 
| 49 | 0 | } | 
| 50 |  |  | 
| 51 |  | namespace { | 
| 52 |  |  | 
| 53 |  | struct EncodedDoubleFormatter | 
| 54 |  | { | 
| 55 |  |     template<typename Stream> void Ser(Stream &s, double v) | 
| 56 | 0 |     { | 
| 57 | 0 |         s << EncodeDouble(v); | 
| 58 | 0 |     } | 
| 59 |  |  | 
| 60 |  |     template<typename Stream> void Unser(Stream& s, double& v) | 
| 61 | 0 |     { | 
| 62 | 0 |         uint64_t encoded; | 
| 63 | 0 |         s >> encoded; | 
| 64 | 0 |         v = DecodeDouble(encoded); | 
| 65 | 0 |     } | 
| 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 | 0 |     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 | 0 |     : buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale) | 
| 183 | 0 | { | 
| 184 | 0 |     assert(_scale != 0 && "_scale must be non-zero"); | 
| 185 | 0 |     confAvg.resize(maxPeriods); | 
| 186 | 0 |     failAvg.resize(maxPeriods); | 
| 187 | 0 |     for (unsigned int i = 0; i < maxPeriods; i++) { | 
| 188 | 0 |         confAvg[i].resize(buckets.size()); | 
| 189 | 0 |         failAvg[i].resize(buckets.size()); | 
| 190 | 0 |     } | 
| 191 |  | 
 | 
| 192 | 0 |     txCtAvg.resize(buckets.size()); | 
| 193 | 0 |     m_feerate_avg.resize(buckets.size()); | 
| 194 |  | 
 | 
| 195 | 0 |     resizeInMemoryCounters(buckets.size()); | 
| 196 | 0 | } | 
| 197 |  |  | 
| 198 | 0 | 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 | 0 |     unconfTxs.resize(GetMaxConfirms()); | 
| 201 | 0 |     for (unsigned int i = 0; i < unconfTxs.size(); i++) { | 
| 202 | 0 |         unconfTxs[i].resize(newbuckets); | 
| 203 | 0 |     } | 
| 204 | 0 |     oldUnconfTxs.resize(newbuckets); | 
| 205 | 0 | } | 
| 206 |  |  | 
| 207 |  | // Roll the unconfirmed txs circular buffer | 
| 208 |  | void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight) | 
| 209 | 0 | { | 
| 210 | 0 |     for (unsigned int j = 0; j < buckets.size(); j++) { | 
| 211 | 0 |         oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j]; | 
| 212 | 0 |         unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0; | 
| 213 | 0 |     } | 
| 214 | 0 | } | 
| 215 |  |  | 
| 216 |  |  | 
| 217 |  | void TxConfirmStats::Record(int blocksToConfirm, double feerate) | 
| 218 | 0 | { | 
| 219 |  |     // blocksToConfirm is 1-based | 
| 220 | 0 |     if (blocksToConfirm < 1) | 
| 221 | 0 |         return; | 
| 222 | 0 |     int periodsToConfirm = (blocksToConfirm + scale - 1) / scale; | 
| 223 | 0 |     unsigned int bucketindex = bucketMap.lower_bound(feerate)->second; | 
| 224 | 0 |     for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) { | 
| 225 | 0 |         confAvg[i - 1][bucketindex]++; | 
| 226 | 0 |     } | 
| 227 | 0 |     txCtAvg[bucketindex]++; | 
| 228 | 0 |     m_feerate_avg[bucketindex] += feerate; | 
| 229 | 0 | } | 
| 230 |  |  | 
| 231 |  | void TxConfirmStats::UpdateMovingAverages() | 
| 232 | 0 | { | 
| 233 | 0 |     assert(confAvg.size() == failAvg.size()); | 
| 234 | 0 |     for (unsigned int j = 0; j < buckets.size(); j++) { | 
| 235 | 0 |         for (unsigned int i = 0; i < confAvg.size(); i++) { | 
| 236 | 0 |             confAvg[i][j] *= decay; | 
| 237 | 0 |             failAvg[i][j] *= decay; | 
| 238 | 0 |         } | 
| 239 | 0 |         m_feerate_avg[j] *= decay; | 
| 240 | 0 |         txCtAvg[j] *= decay; | 
| 241 | 0 |     } | 
| 242 | 0 | } | 
| 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 | 0 | { | 
| 249 |  |     // Counters for a bucket (or range of buckets) | 
| 250 | 0 |     double nConf = 0; // Number of tx's confirmed within the confTarget | 
| 251 | 0 |     double totalNum = 0; // Total number of tx's that were ever confirmed | 
| 252 | 0 |     int extraNum = 0;  // Number of tx's still in mempool for confTarget or longer | 
| 253 | 0 |     double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget | 
| 254 | 0 |     const int periodTarget = (confTarget + scale - 1) / scale; | 
| 255 | 0 |     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 | 0 |     unsigned int curNearBucket = maxbucketindex; | 
| 263 | 0 |     unsigned int bestNearBucket = maxbucketindex; | 
| 264 | 0 |     unsigned int curFarBucket = maxbucketindex; | 
| 265 | 0 |     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 | 0 |     double partialNum = 0; | 
| 271 |  | 
 | 
| 272 | 0 |     bool foundAnswer = false; | 
| 273 | 0 |     unsigned int bins = unconfTxs.size(); | 
| 274 | 0 |     bool newBucketRange = true; | 
| 275 | 0 |     bool passing = true; | 
| 276 | 0 |     EstimatorBucket passBucket; | 
| 277 | 0 |     EstimatorBucket failBucket; | 
| 278 |  |  | 
| 279 |  |     // Start counting from highest feerate transactions | 
| 280 | 0 |     for (int bucket = maxbucketindex; bucket >= 0; --bucket) { | 
| 281 | 0 |         if (newBucketRange) { | 
| 282 | 0 |             curNearBucket = bucket; | 
| 283 | 0 |             newBucketRange = false; | 
| 284 | 0 |         } | 
| 285 | 0 |         curFarBucket = bucket; | 
| 286 | 0 |         nConf += confAvg[periodTarget - 1][bucket]; | 
| 287 | 0 |         partialNum += txCtAvg[bucket]; | 
| 288 | 0 |         totalNum += txCtAvg[bucket]; | 
| 289 | 0 |         failNum += failAvg[periodTarget - 1][bucket]; | 
| 290 | 0 |         for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++) | 
| 291 | 0 |             extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket]; | 
| 292 | 0 |         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 | 0 |         if (partialNum < sufficientTxVal / (1 - decay)) { | 
| 299 |  |             // the buckets we've added in this round aren't sufficient | 
| 300 |  |             // so keep adding | 
| 301 | 0 |             continue; | 
| 302 | 0 |         } else { | 
| 303 | 0 |             partialNum = 0; // reset for the next range we'll add | 
| 304 |  | 
 | 
| 305 | 0 |             double curPct = nConf / (totalNum + failNum + extraNum); | 
| 306 |  |  | 
| 307 |  |             // Check to see if we are no longer getting confirmed at the success rate | 
| 308 | 0 |             if (curPct < successBreakPoint) { | 
| 309 | 0 |                 if (passing == true) { | 
| 310 |  |                     // First time we hit a failure record the failed bucket | 
| 311 | 0 |                     unsigned int failMinBucket = std::min(curNearBucket, curFarBucket); | 
| 312 | 0 |                     unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket); | 
| 313 | 0 |                     failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0; | 
| 314 | 0 |                     failBucket.end = buckets[failMaxBucket]; | 
| 315 | 0 |                     failBucket.withinTarget = nConf; | 
| 316 | 0 |                     failBucket.totalConfirmed = totalNum; | 
| 317 | 0 |                     failBucket.inMempool = extraNum; | 
| 318 | 0 |                     failBucket.leftMempool = failNum; | 
| 319 | 0 |                     passing = false; | 
| 320 | 0 |                 } | 
| 321 | 0 |                 continue; | 
| 322 | 0 |             } | 
| 323 |  |             // Otherwise update the cumulative stats, and the bucket variables | 
| 324 |  |             // and reset the counters | 
| 325 | 0 |             else { | 
| 326 | 0 |                 failBucket = EstimatorBucket(); // Reset any failed bucket, currently passing | 
| 327 | 0 |                 foundAnswer = true; | 
| 328 | 0 |                 passing = true; | 
| 329 | 0 |                 passBucket.withinTarget = nConf; | 
| 330 | 0 |                 nConf = 0; | 
| 331 | 0 |                 passBucket.totalConfirmed = totalNum; | 
| 332 | 0 |                 totalNum = 0; | 
| 333 | 0 |                 passBucket.inMempool = extraNum; | 
| 334 | 0 |                 passBucket.leftMempool = failNum; | 
| 335 | 0 |                 failNum = 0; | 
| 336 | 0 |                 extraNum = 0; | 
| 337 | 0 |                 bestNearBucket = curNearBucket; | 
| 338 | 0 |                 bestFarBucket = curFarBucket; | 
| 339 | 0 |                 newBucketRange = true; | 
| 340 | 0 |             } | 
| 341 | 0 |         } | 
| 342 | 0 |     } | 
| 343 |  | 
 | 
| 344 | 0 |     double median = -1; | 
| 345 | 0 |     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 | 0 |     unsigned int minBucket = std::min(bestNearBucket, bestFarBucket); | 
| 352 | 0 |     unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket); | 
| 353 | 0 |     for (unsigned int j = minBucket; j <= maxBucket; j++) { | 
| 354 | 0 |         txSum += txCtAvg[j]; | 
| 355 | 0 |     } | 
| 356 | 0 |     if (foundAnswer && txSum != 0) { | 
| 357 | 0 |         txSum = txSum / 2; | 
| 358 | 0 |         for (unsigned int j = minBucket; j <= maxBucket; j++) { | 
| 359 | 0 |             if (txCtAvg[j] < txSum) | 
| 360 | 0 |                 txSum -= txCtAvg[j]; | 
| 361 | 0 |             else { // we're in the right bucket | 
| 362 | 0 |                 median = m_feerate_avg[j] / txCtAvg[j]; | 
| 363 | 0 |                 break; | 
| 364 | 0 |             } | 
| 365 | 0 |         } | 
| 366 |  | 
 | 
| 367 | 0 |         passBucket.start = minBucket ? buckets[minBucket-1] : 0; | 
| 368 | 0 |         passBucket.end = buckets[maxBucket]; | 
| 369 | 0 |     } | 
| 370 |  |  | 
| 371 |  |     // If we were passing until we reached last few buckets with insufficient data, then report those as failed | 
| 372 | 0 |     if (passing && !newBucketRange) { | 
| 373 | 0 |         unsigned int failMinBucket = std::min(curNearBucket, curFarBucket); | 
| 374 | 0 |         unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket); | 
| 375 | 0 |         failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0; | 
| 376 | 0 |         failBucket.end = buckets[failMaxBucket]; | 
| 377 | 0 |         failBucket.withinTarget = nConf; | 
| 378 | 0 |         failBucket.totalConfirmed = totalNum; | 
| 379 | 0 |         failBucket.inMempool = extraNum; | 
| 380 | 0 |         failBucket.leftMempool = failNum; | 
| 381 | 0 |     } | 
| 382 |  | 
 | 
| 383 | 0 |     float passed_within_target_perc = 0.0; | 
| 384 | 0 |     float failed_within_target_perc = 0.0; | 
| 385 | 0 |     if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) { | 
| 386 | 0 |         passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool); | 
| 387 | 0 |     } | 
| 388 | 0 |     if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) { | 
| 389 | 0 |         failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool); | 
| 390 | 0 |     } | 
| 391 |  | 
 | 
| 392 | 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 |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 393 | 0 |              confTarget, 100.0 * successBreakPoint, decay, | 
| 394 | 0 |              median, passBucket.start, passBucket.end, | 
| 395 | 0 |              passed_within_target_perc, | 
| 396 | 0 |              passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool, | 
| 397 | 0 |              failBucket.start, failBucket.end, | 
| 398 | 0 |              failed_within_target_perc, | 
| 399 | 0 |              failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool); | 
| 400 |  |  | 
| 401 |  | 
 | 
| 402 | 0 |     if (result) { | 
| 403 | 0 |         result->pass = passBucket; | 
| 404 | 0 |         result->fail = failBucket; | 
| 405 | 0 |         result->decay = decay; | 
| 406 | 0 |         result->scale = scale; | 
| 407 | 0 |     } | 
| 408 | 0 |     return median; | 
| 409 | 0 | } | 
| 410 |  |  | 
| 411 |  | void TxConfirmStats::Write(AutoFile& fileout) const | 
| 412 | 0 | { | 
| 413 | 0 |     fileout << Using<EncodedDoubleFormatter>(decay); | 
| 414 | 0 |     fileout << scale; | 
| 415 | 0 |     fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg); | 
| 416 | 0 |     fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg); | 
| 417 | 0 |     fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg); | 
| 418 | 0 |     fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg); | 
| 419 | 0 | } | 
| 420 |  |  | 
| 421 |  | void TxConfirmStats::Read(AutoFile& filein, size_t numBuckets) | 
| 422 | 0 | { | 
| 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 | 0 |     size_t maxConfirms, maxPeriods; | 
| 427 |  |  | 
| 428 |  |     // The current version will store the decay with each individual TxConfirmStats and also keep a scale factor | 
| 429 | 0 |     filein >> Using<EncodedDoubleFormatter>(decay); | 
| 430 | 0 |     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 | 0 |     filein >> scale; | 
| 434 | 0 |     if (scale == 0) { | 
| 435 | 0 |         throw std::runtime_error("Corrupt estimates file. Scale must be non-zero"); | 
| 436 | 0 |     } | 
| 437 |  |  | 
| 438 | 0 |     filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg); | 
| 439 | 0 |     if (m_feerate_avg.size() != numBuckets) { | 
| 440 | 0 |         throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count"); | 
| 441 | 0 |     } | 
| 442 | 0 |     filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg); | 
| 443 | 0 |     if (txCtAvg.size() != numBuckets) { | 
| 444 | 0 |         throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count"); | 
| 445 | 0 |     } | 
| 446 | 0 |     filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg); | 
| 447 | 0 |     maxPeriods = confAvg.size(); | 
| 448 | 0 |     maxConfirms = scale * maxPeriods; | 
| 449 |  | 
 | 
| 450 | 0 |     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 | 0 |     for (unsigned int i = 0; i < maxPeriods; i++) { | 
| 454 | 0 |         if (confAvg[i].size() != numBuckets) { | 
| 455 | 0 |             throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count"); | 
| 456 | 0 |         } | 
| 457 | 0 |     } | 
| 458 |  |  | 
| 459 | 0 |     filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg); | 
| 460 | 0 |     if (maxPeriods != failAvg.size()) { | 
| 461 | 0 |         throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures"); | 
| 462 | 0 |     } | 
| 463 | 0 |     for (unsigned int i = 0; i < maxPeriods; i++) { | 
| 464 | 0 |         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 | 0 |     } | 
| 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 | 0 |     resizeInMemoryCounters(numBuckets); | 
| 472 |  | 
 | 
| 473 | 0 |     LogDebug(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n", | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 474 | 0 |              numBuckets, maxConfirms); | 
| 475 | 0 | } | 
| 476 |  |  | 
| 477 |  | unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val) | 
| 478 | 0 | { | 
| 479 | 0 |     unsigned int bucketindex = bucketMap.lower_bound(val)->second; | 
| 480 | 0 |     unsigned int blockIndex = nBlockHeight % unconfTxs.size(); | 
| 481 | 0 |     unconfTxs[blockIndex][bucketindex]++; | 
| 482 | 0 |     return bucketindex; | 
| 483 | 0 | } | 
| 484 |  |  | 
| 485 |  | void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketindex, bool inBlock) | 
| 486 | 0 | { | 
| 487 |  |     //nBestSeenHeight is not updated yet for the new block | 
| 488 | 0 |     int blocksAgo = nBestSeenHeight - entryHeight; | 
| 489 | 0 |     if (nBestSeenHeight == 0)  // the BlockPolicyEstimator hasn't seen any blocks yet | 
| 490 | 0 |         blocksAgo = 0; | 
| 491 | 0 |     if (blocksAgo < 0) { | 
| 492 | 0 |         LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, blocks ago is negative for mempool tx\n"); | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 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 | 0 |     if (blocksAgo >= (int)unconfTxs.size()) { | 
| 497 | 0 |         if (oldUnconfTxs[bucketindex] > 0) { | 
| 498 | 0 |             oldUnconfTxs[bucketindex]--; | 
| 499 | 0 |         } else { | 
| 500 | 0 |             LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n", | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 501 | 0 |                      bucketindex); | 
| 502 | 0 |         } | 
| 503 | 0 |     } | 
| 504 | 0 |     else { | 
| 505 | 0 |         unsigned int blockIndex = entryHeight % unconfTxs.size(); | 
| 506 | 0 |         if (unconfTxs[blockIndex][bucketindex] > 0) { | 
| 507 | 0 |             unconfTxs[blockIndex][bucketindex]--; | 
| 508 | 0 |         } else { | 
| 509 | 0 |             LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n", | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 510 | 0 |                      blockIndex, bucketindex); | 
| 511 | 0 |         } | 
| 512 | 0 |     } | 
| 513 | 0 |     if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period | 
| 514 | 0 |         assert(scale != 0); | 
| 515 | 0 |         unsigned int periodsAgo = blocksAgo / scale; | 
| 516 | 0 |         for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) { | 
| 517 | 0 |             failAvg[i][bucketindex]++; | 
| 518 | 0 |         } | 
| 519 | 0 |     } | 
| 520 | 0 | } | 
| 521 |  |  | 
| 522 |  | bool CBlockPolicyEstimator::removeTx(Txid hash) | 
| 523 | 0 | { | 
| 524 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 525 | 0 |     return _removeTx(hash, /*inBlock=*/false); | 
| 526 | 0 | } | 
| 527 |  |  | 
| 528 |  | bool CBlockPolicyEstimator::_removeTx(const Txid& hash, bool inBlock) | 
| 529 | 0 | { | 
| 530 | 0 |     AssertLockHeld(m_cs_fee_estimator); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 531 | 0 |     std::map<Txid, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash); | 
| 532 | 0 |     if (pos != mapMemPoolTxs.end()) { | 
| 533 | 0 |         feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock); | 
| 534 | 0 |         shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock); | 
| 535 | 0 |         longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock); | 
| 536 | 0 |         mapMemPoolTxs.erase(hash); | 
| 537 | 0 |         return true; | 
| 538 | 0 |     } else { | 
| 539 | 0 |         return false; | 
| 540 | 0 |     } | 
| 541 | 0 | } | 
| 542 |  |  | 
| 543 |  | CBlockPolicyEstimator::CBlockPolicyEstimator(const fs::path& estimation_filepath, const bool read_stale_estimates) | 
| 544 | 0 |     : m_estimation_filepath{estimation_filepath} | 
| 545 | 0 | { | 
| 546 | 0 |     static_assert(MIN_BUCKET_FEERATE > 0, "Min feerate must be nonzero"); | 
| 547 | 0 |     size_t bucketIndex = 0; | 
| 548 |  | 
 | 
| 549 | 0 |     for (double bucketBoundary = MIN_BUCKET_FEERATE; bucketBoundary <= MAX_BUCKET_FEERATE; bucketBoundary *= FEE_SPACING, bucketIndex++) { | 
| 550 | 0 |         buckets.push_back(bucketBoundary); | 
| 551 | 0 |         bucketMap[bucketBoundary] = bucketIndex; | 
| 552 | 0 |     } | 
| 553 | 0 |     buckets.push_back(INF_FEERATE); | 
| 554 | 0 |     bucketMap[INF_FEERATE] = bucketIndex; | 
| 555 | 0 |     assert(bucketMap.size() == buckets.size()); | 
| 556 |  |  | 
| 557 | 0 |     feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE)); | 
| 558 | 0 |     shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE)); | 
| 559 | 0 |     longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE)); | 
| 560 |  | 
 | 
| 561 | 0 |     AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "rb")}; | 
| 562 |  | 
 | 
| 563 | 0 |     if (est_file.IsNull()) { | 
| 564 | 0 |         LogInfo("%s is not found. Continue anyway.", fs::PathToString(m_estimation_filepath));| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 565 | 0 |         return; | 
| 566 | 0 |     } | 
| 567 |  |  | 
| 568 | 0 |     std::chrono::hours file_age = GetFeeEstimatorFileAge(); | 
| 569 | 0 |     if (file_age > MAX_FILE_AGE && !read_stale_estimates) { | 
| 570 | 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 |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 571 | 0 |         return; | 
| 572 | 0 |     } | 
| 573 |  |  | 
| 574 | 0 |     if (!Read(est_file)) { | 
| 575 | 0 |         LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(m_estimation_filepath));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 576 | 0 |     } | 
| 577 | 0 | } | 
| 578 |  |  | 
| 579 | 0 | CBlockPolicyEstimator::~CBlockPolicyEstimator() = default; | 
| 580 |  |  | 
| 581 |  | void CBlockPolicyEstimator::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /*unused*/) | 
| 582 | 0 | { | 
| 583 | 0 |     processTransaction(tx); | 
| 584 | 0 | } | 
| 585 |  |  | 
| 586 |  | void CBlockPolicyEstimator::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason /*unused*/, uint64_t /*unused*/) | 
| 587 | 0 | { | 
| 588 | 0 |     removeTx(tx->GetHash()); | 
| 589 | 0 | } | 
| 590 |  |  | 
| 591 |  | void CBlockPolicyEstimator::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight) | 
| 592 | 0 | { | 
| 593 | 0 |     processBlock(txs_removed_for_block, nBlockHeight); | 
| 594 | 0 | } | 
| 595 |  |  | 
| 596 |  | void CBlockPolicyEstimator::processTransaction(const NewMempoolTransactionInfo& tx) | 
| 597 | 0 | { | 
| 598 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 599 | 0 |     const unsigned int txHeight = tx.info.txHeight; | 
| 600 | 0 |     const auto& hash = tx.info.m_tx->GetHash(); | 
| 601 | 0 |     if (mapMemPoolTxs.count(hash)) { | 
| 602 | 0 |         LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n", | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 603 | 0 |                  hash.ToString()); | 
| 604 | 0 |         return; | 
| 605 | 0 |     } | 
| 606 |  |  | 
| 607 | 0 |     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 | 0 |         return; | 
| 613 | 0 |     } | 
| 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 | 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; | 
| 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 | 0 |     if (!validForFeeEstimation) { | 
| 624 | 0 |         untrackedTxs++; | 
| 625 | 0 |         return; | 
| 626 | 0 |     } | 
| 627 | 0 |     trackedTxs++; | 
| 628 |  |  | 
| 629 |  |     // Feerates are stored and reported as BTC-per-kb: | 
| 630 | 0 |     const CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size); | 
| 631 |  | 
 | 
| 632 | 0 |     mapMemPoolTxs[hash].blockHeight = txHeight; | 
| 633 | 0 |     unsigned int bucketIndex = feeStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK())); | 
| 634 | 0 |     mapMemPoolTxs[hash].bucketIndex = bucketIndex; | 
| 635 | 0 |     unsigned int bucketIndex2 = shortStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK())); | 
| 636 | 0 |     assert(bucketIndex == bucketIndex2); | 
| 637 | 0 |     unsigned int bucketIndex3 = longStats->NewTx(txHeight, static_cast<double>(feeRate.GetFeePerK())); | 
| 638 | 0 |     assert(bucketIndex == bucketIndex3); | 
| 639 | 0 | } | 
| 640 |  |  | 
| 641 |  | bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const RemovedMempoolTransactionInfo& tx) | 
| 642 | 0 | { | 
| 643 | 0 |     AssertLockHeld(m_cs_fee_estimator); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 644 | 0 |     if (!_removeTx(tx.info.m_tx->GetHash(), true)) { | 
| 645 |  |         // This transaction wasn't being tracked for fee estimation | 
| 646 | 0 |         return false; | 
| 647 | 0 |     } | 
| 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 | 0 |     int blocksToConfirm = nBlockHeight - tx.info.txHeight; | 
| 653 | 0 |     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"); | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 657 | 0 |         return false; | 
| 658 | 0 |     } | 
| 659 |  |  | 
| 660 |  |     // Feerates are stored and reported as BTC-per-kb: | 
| 661 | 0 |     CFeeRate feeRate(tx.info.m_fee, tx.info.m_virtual_transaction_size); | 
| 662 |  | 
 | 
| 663 | 0 |     feeStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK())); | 
| 664 | 0 |     shortStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK())); | 
| 665 | 0 |     longStats->Record(blocksToConfirm, static_cast<double>(feeRate.GetFeePerK())); | 
| 666 | 0 |     return true; | 
| 667 | 0 | } | 
| 668 |  |  | 
| 669 |  | void CBlockPolicyEstimator::processBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, | 
| 670 |  |                                          unsigned int nBlockHeight) | 
| 671 | 0 | { | 
| 672 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 673 | 0 |     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 | 0 |         return; | 
| 680 | 0 |     } | 
| 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 | 0 |     nBestSeenHeight = nBlockHeight; | 
| 686 |  |  | 
| 687 |  |     // Update unconfirmed circular buffer | 
| 688 | 0 |     feeStats->ClearCurrent(nBlockHeight); | 
| 689 | 0 |     shortStats->ClearCurrent(nBlockHeight); | 
| 690 | 0 |     longStats->ClearCurrent(nBlockHeight); | 
| 691 |  |  | 
| 692 |  |     // Decay all exponential averages | 
| 693 | 0 |     feeStats->UpdateMovingAverages(); | 
| 694 | 0 |     shortStats->UpdateMovingAverages(); | 
| 695 | 0 |     longStats->UpdateMovingAverages(); | 
| 696 |  | 
 | 
| 697 | 0 |     unsigned int countedTxs = 0; | 
| 698 |  |     // Update averages with data points from current block | 
| 699 | 0 |     for (const auto& tx : txs_removed_for_block) { | 
| 700 | 0 |         if (processBlockTx(nBlockHeight, tx)) | 
| 701 | 0 |             countedTxs++; | 
| 702 | 0 |     } | 
| 703 |  | 
 | 
| 704 | 0 |     if (firstRecordedHeight == 0 && countedTxs > 0) { | 
| 705 | 0 |         firstRecordedHeight = nBestSeenHeight; | 
| 706 | 0 |         LogDebug(BCLog::ESTIMATEFEE, "Blockpolicy first recorded height %u\n", firstRecordedHeight); | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 707 | 0 |     } | 
| 708 |  |  | 
| 709 |  | 
 | 
| 710 | 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 |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 711 | 0 |              countedTxs, txs_removed_for_block.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(), | 
| 712 | 0 |              MaxUsableEstimate(), HistoricalBlockSpan() > BlockSpan() ? "historical" : "current"); | 
| 713 |  | 
 | 
| 714 | 0 |     trackedTxs = 0; | 
| 715 | 0 |     untrackedTxs = 0; | 
| 716 | 0 | } | 
| 717 |  |  | 
| 718 |  | CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const | 
| 719 | 0 | { | 
| 720 |  |     // It's not possible to get reasonable estimates for confTarget of 1 | 
| 721 | 0 |     if (confTarget <= 1) | 
| 722 | 0 |         return CFeeRate(0); | 
| 723 |  |  | 
| 724 | 0 |     return estimateRawFee(confTarget, DOUBLE_SUCCESS_PCT, FeeEstimateHorizon::MED_HALFLIFE); | 
| 725 | 0 | } | 
| 726 |  |  | 
| 727 |  | CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult* result) const | 
| 728 | 0 | { | 
| 729 | 0 |     TxConfirmStats* stats = nullptr; | 
| 730 | 0 |     double sufficientTxs = SUFFICIENT_FEETXS; | 
| 731 | 0 |     switch (horizon) { | 
| 732 | 0 |     case FeeEstimateHorizon::SHORT_HALFLIFE: { | 
| 733 | 0 |         stats = shortStats.get(); | 
| 734 | 0 |         sufficientTxs = SUFFICIENT_TXS_SHORT; | 
| 735 | 0 |         break; | 
| 736 | 0 |     } | 
| 737 | 0 |     case FeeEstimateHorizon::MED_HALFLIFE: { | 
| 738 | 0 |         stats = feeStats.get(); | 
| 739 | 0 |         break; | 
| 740 | 0 |     } | 
| 741 | 0 |     case FeeEstimateHorizon::LONG_HALFLIFE: { | 
| 742 | 0 |         stats = longStats.get(); | 
| 743 | 0 |         break; | 
| 744 | 0 |     } | 
| 745 | 0 |     } // no default case, so the compiler can warn about missing cases | 
| 746 | 0 |     assert(stats); | 
| 747 |  |  | 
| 748 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 749 |  |     // Return failure if trying to analyze a target we're not tracking | 
| 750 | 0 |     if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms()) | 
| 751 | 0 |         return CFeeRate(0); | 
| 752 | 0 |     if (successThreshold > 1) | 
| 753 | 0 |         return CFeeRate(0); | 
| 754 |  |  | 
| 755 | 0 |     double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result); | 
| 756 |  | 
 | 
| 757 | 0 |     if (median < 0) | 
| 758 | 0 |         return CFeeRate(0); | 
| 759 |  |  | 
| 760 | 0 |     return CFeeRate(llround(median)); | 
| 761 | 0 | } | 
| 762 |  |  | 
| 763 |  | unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const | 
| 764 | 0 | { | 
| 765 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 766 | 0 |     switch (horizon) { | 
| 767 | 0 |     case FeeEstimateHorizon::SHORT_HALFLIFE: { | 
| 768 | 0 |         return shortStats->GetMaxConfirms(); | 
| 769 | 0 |     } | 
| 770 | 0 |     case FeeEstimateHorizon::MED_HALFLIFE: { | 
| 771 | 0 |         return feeStats->GetMaxConfirms(); | 
| 772 | 0 |     } | 
| 773 | 0 |     case FeeEstimateHorizon::LONG_HALFLIFE: { | 
| 774 | 0 |         return longStats->GetMaxConfirms(); | 
| 775 | 0 |     } | 
| 776 | 0 |     } // no default case, so the compiler can warn about missing cases | 
| 777 | 0 |     assert(false); | 
| 778 | 0 | } | 
| 779 |  |  | 
| 780 |  | unsigned int CBlockPolicyEstimator::BlockSpan() const | 
| 781 | 0 | { | 
| 782 | 0 |     if (firstRecordedHeight == 0) return 0; | 
| 783 | 0 |     assert(nBestSeenHeight >= firstRecordedHeight); | 
| 784 |  |  | 
| 785 | 0 |     return nBestSeenHeight - firstRecordedHeight; | 
| 786 | 0 | } | 
| 787 |  |  | 
| 788 |  | unsigned int CBlockPolicyEstimator::HistoricalBlockSpan() const | 
| 789 | 0 | { | 
| 790 | 0 |     if (historicalFirst == 0) return 0; | 
| 791 | 0 |     assert(historicalBest >= historicalFirst); | 
| 792 |  |  | 
| 793 | 0 |     if (nBestSeenHeight - historicalBest > OLDEST_ESTIMATE_HISTORY) return 0; | 
| 794 |  |  | 
| 795 | 0 |     return historicalBest - historicalFirst; | 
| 796 | 0 | } | 
| 797 |  |  | 
| 798 |  | unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const | 
| 799 | 0 | { | 
| 800 |  |     // Block spans are divided by 2 to make sure there are enough potential failing data points for the estimate | 
| 801 | 0 |     return std::min(longStats->GetMaxConfirms(), std::max(BlockSpan(), HistoricalBlockSpan()) / 2); | 
| 802 | 0 | } | 
| 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 | 0 | { | 
| 810 | 0 |     double estimate = -1; | 
| 811 | 0 |     if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) { | 
| 812 |  |         // Find estimate from shortest time horizon possible | 
| 813 | 0 |         if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon | 
| 814 | 0 |             estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result); | 
| 815 | 0 |         } | 
| 816 | 0 |         else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon | 
| 817 | 0 |             estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result); | 
| 818 | 0 |         } | 
| 819 | 0 |         else { // long horizon | 
| 820 | 0 |             estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result); | 
| 821 | 0 |         } | 
| 822 | 0 |         if (checkShorterHorizon) { | 
| 823 | 0 |             EstimationResult tempResult; | 
| 824 |  |             // If a lower confTarget from a more recent horizon returns a lower answer use it. | 
| 825 | 0 |             if (confTarget > feeStats->GetMaxConfirms()) { | 
| 826 | 0 |                 double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult); | 
| 827 | 0 |                 if (medMax > 0 && (estimate == -1 || medMax < estimate)) { | 
| 828 | 0 |                     estimate = medMax; | 
| 829 | 0 |                     if (result) *result = tempResult; | 
| 830 | 0 |                 } | 
| 831 | 0 |             } | 
| 832 | 0 |             if (confTarget > shortStats->GetMaxConfirms()) { | 
| 833 | 0 |                 double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult); | 
| 834 | 0 |                 if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) { | 
| 835 | 0 |                     estimate = shortMax; | 
| 836 | 0 |                     if (result) *result = tempResult; | 
| 837 | 0 |                 } | 
| 838 | 0 |             } | 
| 839 | 0 |         } | 
| 840 | 0 |     } | 
| 841 | 0 |     return estimate; | 
| 842 | 0 | } | 
| 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 | 0 | { | 
| 849 | 0 |     double estimate = -1; | 
| 850 | 0 |     EstimationResult tempResult; | 
| 851 | 0 |     if (doubleTarget <= shortStats->GetMaxConfirms()) { | 
| 852 | 0 |         estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result); | 
| 853 | 0 |     } | 
| 854 | 0 |     if (doubleTarget <= feeStats->GetMaxConfirms()) { | 
| 855 | 0 |         double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult); | 
| 856 | 0 |         if (longEstimate > estimate) { | 
| 857 | 0 |             estimate = longEstimate; | 
| 858 | 0 |             if (result) *result = tempResult; | 
| 859 | 0 |         } | 
| 860 | 0 |     } | 
| 861 | 0 |     return estimate; | 
| 862 | 0 | } | 
| 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 | 0 | { | 
| 873 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 874 |  | 
 | 
| 875 | 0 |     if (feeCalc) { | 
| 876 | 0 |         feeCalc->desiredTarget = confTarget; | 
| 877 | 0 |         feeCalc->returnedTarget = confTarget; | 
| 878 | 0 |     } | 
| 879 |  | 
 | 
| 880 | 0 |     double median = -1; | 
| 881 | 0 |     EstimationResult tempResult; | 
| 882 |  |  | 
| 883 |  |     // Return failure if trying to analyze a target we're not tracking | 
| 884 | 0 |     if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) { | 
| 885 | 0 |         return CFeeRate(0);  // error condition | 
| 886 | 0 |     } | 
| 887 |  |  | 
| 888 |  |     // It's not possible to get reasonable estimates for confTarget of 1 | 
| 889 | 0 |     if (confTarget == 1) confTarget = 2; | 
| 890 |  | 
 | 
| 891 | 0 |     unsigned int maxUsableEstimate = MaxUsableEstimate(); | 
| 892 | 0 |     if ((unsigned int)confTarget > maxUsableEstimate) { | 
| 893 | 0 |         confTarget = maxUsableEstimate; | 
| 894 | 0 |     } | 
| 895 | 0 |     if (feeCalc) feeCalc->returnedTarget = confTarget; | 
| 896 |  | 
 | 
| 897 | 0 |     if (confTarget <= 1) return CFeeRate(0); // error condition | 
| 898 |  |  | 
| 899 | 0 |     assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints | 
| 900 |  |     /** true is passed to estimateCombined fee for target/2 and target so | 
| 901 |  |      * that we check the max confirms for shorter time horizons as well. | 
| 902 |  |      * This is necessary to preserve monotonically increasing estimates. | 
| 903 |  |      * For non-conservative estimates we do the same thing for 2*target, but | 
| 904 |  |      * for conservative estimates we want to skip these shorter horizons | 
| 905 |  |      * checks for 2*target because we are taking the max over all time | 
| 906 |  |      * horizons so we already have monotonically increasing estimates and | 
| 907 |  |      * the purpose of conservative estimates is not to let short term | 
| 908 |  |      * fluctuations lower our estimates by too much. | 
| 909 |  |      * | 
| 910 |  |      * Note: In certain rare edge cases, monotonically increasing estimates may | 
| 911 |  |      * not be guaranteed. Specifically, given two targets N and M, where M > N, | 
| 912 |  |      * if a sub-estimate for target N fails to return a valid fee rate, while | 
| 913 |  |      * target M has valid fee rate for that sub-estimate, target M may result | 
| 914 |  |      * in a higher fee rate estimate than target N. | 
| 915 |  |      * | 
| 916 |  |      * See: https://github.com/bitcoin/bitcoin/issues/11800#issuecomment-349697807 | 
| 917 |  |      */ | 
| 918 | 0 |     double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult); | 
| 919 | 0 |     if (feeCalc) { | 
| 920 | 0 |         feeCalc->est = tempResult; | 
| 921 | 0 |         feeCalc->reason = FeeReason::HALF_ESTIMATE; | 
| 922 | 0 |     } | 
| 923 | 0 |     median = halfEst; | 
| 924 | 0 |     double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult); | 
| 925 | 0 |     if (actualEst > median) { | 
| 926 | 0 |         median = actualEst; | 
| 927 | 0 |         if (feeCalc) { | 
| 928 | 0 |             feeCalc->est = tempResult; | 
| 929 | 0 |             feeCalc->reason = FeeReason::FULL_ESTIMATE; | 
| 930 | 0 |         } | 
| 931 | 0 |     } | 
| 932 | 0 |     double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult); | 
| 933 | 0 |     if (doubleEst > median) { | 
| 934 | 0 |         median = doubleEst; | 
| 935 | 0 |         if (feeCalc) { | 
| 936 | 0 |             feeCalc->est = tempResult; | 
| 937 | 0 |             feeCalc->reason = FeeReason::DOUBLE_ESTIMATE; | 
| 938 | 0 |         } | 
| 939 | 0 |     } | 
| 940 |  | 
 | 
| 941 | 0 |     if (conservative || median == -1) { | 
| 942 | 0 |         double consEst =  estimateConservativeFee(2 * confTarget, &tempResult); | 
| 943 | 0 |         if (consEst > median) { | 
| 944 | 0 |             median = consEst; | 
| 945 | 0 |             if (feeCalc) { | 
| 946 | 0 |                 feeCalc->est = tempResult; | 
| 947 | 0 |                 feeCalc->reason = FeeReason::CONSERVATIVE; | 
| 948 | 0 |             } | 
| 949 | 0 |         } | 
| 950 | 0 |     } | 
| 951 |  | 
 | 
| 952 | 0 |     if (median < 0) return CFeeRate(0); // error condition | 
| 953 |  |  | 
| 954 | 0 |     return CFeeRate(llround(median)); | 
| 955 | 0 | } | 
| 956 |  |  | 
| 957 | 0 | void CBlockPolicyEstimator::Flush() { | 
| 958 | 0 |     FlushUnconfirmed(); | 
| 959 | 0 |     FlushFeeEstimates(); | 
| 960 | 0 | } | 
| 961 |  |  | 
| 962 |  | void CBlockPolicyEstimator::FlushFeeEstimates() | 
| 963 | 0 | { | 
| 964 | 0 |     AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "wb")}; | 
| 965 | 0 |     if (est_file.IsNull() || !Write(est_file)) { | 
| 966 | 0 |         LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(m_estimation_filepath));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 967 | 0 |         (void)est_file.fclose(); | 
| 968 | 0 |         return; | 
| 969 | 0 |     } | 
| 970 | 0 |     if (est_file.fclose() != 0) { | 
| 971 | 0 |         LogError("Failed to close fee estimates file %s: %s. Continuing anyway.", fs::PathToString(m_estimation_filepath), SysErrorString(errno));| Line | Count | Source |  | 358 | 0 | #define LogError(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Error, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 972 | 0 |         return; | 
| 973 | 0 |     } | 
| 974 | 0 |     LogInfo("Flushed fee estimates to %s.", fs::PathToString(m_estimation_filepath.filename()));| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 975 | 0 | } | 
| 976 |  |  | 
| 977 |  | bool CBlockPolicyEstimator::Write(AutoFile& fileout) const | 
| 978 | 0 | { | 
| 979 | 0 |     try { | 
| 980 | 0 |         LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 981 | 0 |         fileout << CURRENT_FEES_FILE_VERSION; | 
| 982 | 0 |         fileout << int{0}; // Unused dummy field. Written files may contain any value in [0, 289900] | 
| 983 | 0 |         fileout << nBestSeenHeight; | 
| 984 | 0 |         if (BlockSpan() > HistoricalBlockSpan()/2) { | 
| 985 | 0 |             fileout << firstRecordedHeight << nBestSeenHeight; | 
| 986 | 0 |         } | 
| 987 | 0 |         else { | 
| 988 | 0 |             fileout << historicalFirst << historicalBest; | 
| 989 | 0 |         } | 
| 990 | 0 |         fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets); | 
| 991 | 0 |         feeStats->Write(fileout); | 
| 992 | 0 |         shortStats->Write(fileout); | 
| 993 | 0 |         longStats->Write(fileout); | 
| 994 | 0 |     } | 
| 995 | 0 |     catch (const std::exception&) { | 
| 996 | 0 |         LogWarning("Unable to write policy estimator data (non-fatal)");| Line | Count | Source |  | 357 | 0 | #define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 997 | 0 |         return false; | 
| 998 | 0 |     } | 
| 999 | 0 |     return true; | 
| 1000 | 0 | } | 
| 1001 |  |  | 
| 1002 |  | bool CBlockPolicyEstimator::Read(AutoFile& filein) | 
| 1003 | 0 | { | 
| 1004 | 0 |     try { | 
| 1005 | 0 |         LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 1006 | 0 |         int nVersionRequired, dummy; | 
| 1007 | 0 |         filein >> nVersionRequired >> dummy; | 
| 1008 | 0 |         if (nVersionRequired > CURRENT_FEES_FILE_VERSION) { | 
| 1009 | 0 |             throw std::runtime_error{strprintf("File version (%d) too high to be read.", nVersionRequired)};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 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 | 0 |         unsigned int nFileBestSeenHeight; | 
| 1015 | 0 |         filein >> nFileBestSeenHeight; | 
| 1016 |  | 
 | 
| 1017 | 0 |         if (nVersionRequired < CURRENT_FEES_FILE_VERSION) { | 
| 1018 | 0 |             LogWarning("Incompatible old fee estimation data (non-fatal). Version: %d", nVersionRequired);| Line | Count | Source |  | 357 | 0 | #define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 1019 | 0 |         } else { // nVersionRequired == CURRENT_FEES_FILE_VERSION | 
| 1020 | 0 |             unsigned int nFileHistoricalFirst, nFileHistoricalBest; | 
| 1021 | 0 |             filein >> nFileHistoricalFirst >> nFileHistoricalBest; | 
| 1022 | 0 |             if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) { | 
| 1023 | 0 |                 throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid"); | 
| 1024 | 0 |             } | 
| 1025 | 0 |             std::vector<double> fileBuckets; | 
| 1026 | 0 |             filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets); | 
| 1027 | 0 |             size_t numBuckets = fileBuckets.size(); | 
| 1028 | 0 |             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 | 0 |             std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE)); | 
| 1033 | 0 |             std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE)); | 
| 1034 | 0 |             std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE)); | 
| 1035 | 0 |             fileFeeStats->Read(filein, numBuckets); | 
| 1036 | 0 |             fileShortStats->Read(filein, numBuckets); | 
| 1037 | 0 |             fileLongStats->Read(filein, numBuckets); | 
| 1038 |  |  | 
| 1039 |  |             // Fee estimates file parsed correctly | 
| 1040 |  |             // Copy buckets from file and refresh our bucketmap | 
| 1041 | 0 |             buckets = fileBuckets; | 
| 1042 | 0 |             bucketMap.clear(); | 
| 1043 | 0 |             for (unsigned int i = 0; i < buckets.size(); i++) { | 
| 1044 | 0 |                 bucketMap[buckets[i]] = i; | 
| 1045 | 0 |             } | 
| 1046 |  |  | 
| 1047 |  |             // Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap | 
| 1048 | 0 |             feeStats = std::move(fileFeeStats); | 
| 1049 | 0 |             shortStats = std::move(fileShortStats); | 
| 1050 | 0 |             longStats = std::move(fileLongStats); | 
| 1051 |  | 
 | 
| 1052 | 0 |             nBestSeenHeight = nFileBestSeenHeight; | 
| 1053 | 0 |             historicalFirst = nFileHistoricalFirst; | 
| 1054 | 0 |             historicalBest = nFileHistoricalBest; | 
| 1055 | 0 |         } | 
| 1056 | 0 |     } | 
| 1057 | 0 |     catch (const std::exception& e) { | 
| 1058 | 0 |         LogWarning("Unable to read policy estimator data (non-fatal): %s", e.what());| Line | Count | Source |  | 357 | 0 | #define LogWarning(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Warning, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 1059 | 0 |         return false; | 
| 1060 | 0 |     } | 
| 1061 | 0 |     return true; | 
| 1062 | 0 | } | 
| 1063 |  |  | 
| 1064 |  | void CBlockPolicyEstimator::FlushUnconfirmed() | 
| 1065 | 0 | { | 
| 1066 | 0 |     const auto startclear{SteadyClock::now()}; | 
| 1067 | 0 |     LOCK(m_cs_fee_estimator); | Line | Count | Source |  | 259 | 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 | 
 | 
 | 
 | 
 | 
| 1068 | 0 |     size_t num_entries = mapMemPoolTxs.size(); | 
| 1069 |  |     // Remove every entry in mapMemPoolTxs | 
| 1070 | 0 |     while (!mapMemPoolTxs.empty()) { | 
| 1071 | 0 |         auto mi = mapMemPoolTxs.begin(); | 
| 1072 | 0 |         _removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs | 
| 1073 | 0 |     } | 
| 1074 | 0 |     const auto endclear{SteadyClock::now()}; | 
| 1075 | 0 |     LogDebug(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %.3fs\n", num_entries, Ticks<SecondsDouble>(endclear - startclear)); | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 1076 | 0 | } | 
| 1077 |  |  | 
| 1078 |  | std::chrono::hours CBlockPolicyEstimator::GetFeeEstimatorFileAge() | 
| 1079 | 0 | { | 
| 1080 | 0 |     auto file_time{fs::last_write_time(m_estimation_filepath)}; | 
| 1081 | 0 |     auto now{fs::file_time_type::clock::now()}; | 
| 1082 | 0 |     return std::chrono::duration_cast<std::chrono::hours>(now - file_time); | 
| 1083 | 0 | } | 
| 1084 |  |  | 
| 1085 |  | static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee, | 
| 1086 |  |                                    double max_filter_fee_rate, | 
| 1087 |  |                                    double fee_filter_spacing) | 
| 1088 | 51.2k | { | 
| 1089 | 51.2k |     std::set<double> fee_set; | 
| 1090 |  |  | 
| 1091 | 51.2k |     const CAmount min_fee_limit{std::max(CAmount(1), min_incremental_fee.GetFeePerK() / 2)}; | 
| 1092 | 51.2k |     fee_set.insert(0); | 
| 1093 | 51.2k |     for (double bucket_boundary = min_fee_limit; | 
| 1094 | 6.66M |          bucket_boundary <= max_filter_fee_rate; | 
| 1095 | 6.61M |          bucket_boundary *= fee_filter_spacing) { | 
| 1096 |  |  | 
| 1097 | 6.61M |         fee_set.insert(bucket_boundary); | 
| 1098 | 6.61M |     } | 
| 1099 |  |  | 
| 1100 | 51.2k |     return fee_set; | 
| 1101 | 51.2k | } | 
| 1102 |  |  | 
| 1103 |  | FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee, FastRandomContext& rng) | 
| 1104 | 51.2k |     : m_fee_set{MakeFeeSet(minIncrementalFee, MAX_FILTER_FEERATE, FEE_FILTER_SPACING)}, | 
| 1105 | 51.2k |       insecure_rand{rng} | 
| 1106 | 51.2k | { | 
| 1107 | 51.2k | } | 
| 1108 |  |  | 
| 1109 |  | CAmount FeeFilterRounder::round(CAmount currentMinFee) | 
| 1110 | 71.9k | { | 
| 1111 | 71.9k |     AssertLockNotHeld(m_insecure_rand_mutex); | Line | Count | Source |  | 142 | 71.9k | #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1112 | 71.9k |     std::set<double>::iterator it = m_fee_set.lower_bound(currentMinFee); | 
| 1113 | 71.9k |     if (it == m_fee_set.end() || | 
| 1114 | 71.9k |         (29.0k it != m_fee_set.begin()29.0k&& | 
| 1115 | 42.8k |          WITH_LOCK0 (m_insecure_rand_mutex, return insecure_rand.rand32()) % 3 != 00)) { | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1116 | 42.8k |         --it; | 
| 1117 | 42.8k |     } | 
| 1118 | 71.9k |     return static_cast<CAmount>(*it); | 
| 1119 | 71.9k | } |