Skip to content

Commit 3eb241a

Browse files
committed
tx fees, policy: do not read estimates of old fee_estimates.dat
Old fee estimates could cause transactions to become stuck in the mempool. This commit prevents the node from using stale estimates from an old file.
1 parent 5b886f2 commit 3eb241a

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

src/policy/fees.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include <algorithm>
2626
#include <cassert>
27+
#include <chrono>
2728
#include <cmath>
2829
#include <cstddef>
2930
#include <cstdint>
@@ -545,9 +546,22 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const fs::path& estimation_filepath
545546
shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
546547
longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
547548

548-
// If the fee estimation file is present, read recorded estimations
549549
AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "rb")};
550-
if (est_file.IsNull() || !Read(est_file)) {
550+
551+
// Whenever the fee estimation file is not present return early
552+
if (est_file.IsNull()) {
553+
LogPrintf("%s is not found. Continue anyway.\n", fs::PathToString(m_estimation_filepath));
554+
return;
555+
}
556+
557+
std::chrono::hours file_age = GetFeeEstimatorFileAge();
558+
// fee estimate file must not be too old to avoid wrong fee estimates.
559+
if (file_age > MAX_FILE_AGE) {
560+
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));
561+
return;
562+
}
563+
564+
if (!Read(est_file)) {
551565
LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(m_estimation_filepath));
552566
}
553567
}
@@ -1017,6 +1031,13 @@ void CBlockPolicyEstimator::FlushUnconfirmed()
10171031
LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, Ticks<SecondsDouble>(endclear - startclear));
10181032
}
10191033

1034+
std::chrono::hours CBlockPolicyEstimator::GetFeeEstimatorFileAge()
1035+
{
1036+
auto file_time = std::filesystem::last_write_time(m_estimation_filepath);
1037+
auto now = std::filesystem::file_time_type::clock::now();
1038+
return std::chrono::duration_cast<std::chrono::hours>(now - file_time);
1039+
}
1040+
10201041
static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee,
10211042
double max_filter_fee_rate,
10221043
double fee_filter_spacing)

src/policy/fees.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
// How often to flush fee estimates to fee_estimates.dat.
2626
static constexpr std::chrono::hours FEE_FLUSH_INTERVAL{1};
2727

28+
/** fee_estimates.dat that are more than 60 hours (2.5 days) will not be read,
29+
* as the estimates in the file are stale.
30+
*/
31+
static constexpr std::chrono::hours MAX_FILE_AGE{60};
32+
2833
class AutoFile;
2934
class CTxMemPoolEntry;
3035
class TxConfirmStats;
@@ -248,6 +253,9 @@ class CBlockPolicyEstimator
248253
void FlushFeeEstimates()
249254
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
250255

256+
/** Calculates the age of the file, since last modified */
257+
std::chrono::hours GetFeeEstimatorFileAge();
258+
251259
private:
252260
mutable Mutex m_cs_fee_estimator;
253261

0 commit comments

Comments
 (0)