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