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.
This commit is contained in:
ismaelsadeeq 2023-06-14 22:32:27 +01:00
parent 5b886f2b43
commit 3eb241a141
2 changed files with 31 additions and 2 deletions

View file

@ -24,6 +24,7 @@
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdint>
@ -545,9 +546,22 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const fs::path& estimation_filepath
shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
// If the fee estimation file is present, read recorded estimations
AutoFile est_file{fsbridge::fopen(m_estimation_filepath, "rb")};
if (est_file.IsNull() || !Read(est_file)) {
// Whenever the fee estimation file is not present return early
if (est_file.IsNull()) {
LogPrintf("%s is not found. Continue anyway.\n", fs::PathToString(m_estimation_filepath));
return;
}
std::chrono::hours file_age = GetFeeEstimatorFileAge();
// fee estimate file must not be too old to avoid wrong fee estimates.
if (file_age > MAX_FILE_AGE) {
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));
return;
}
if (!Read(est_file)) {
LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(m_estimation_filepath));
}
}
@ -1017,6 +1031,13 @@ void CBlockPolicyEstimator::FlushUnconfirmed()
LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, Ticks<SecondsDouble>(endclear - startclear));
}
std::chrono::hours CBlockPolicyEstimator::GetFeeEstimatorFileAge()
{
auto file_time = std::filesystem::last_write_time(m_estimation_filepath);
auto now = std::filesystem::file_time_type::clock::now();
return std::chrono::duration_cast<std::chrono::hours>(now - file_time);
}
static std::set<double> MakeFeeSet(const CFeeRate& min_incremental_fee,
double max_filter_fee_rate,
double fee_filter_spacing)

View file

@ -25,6 +25,11 @@
// How often to flush fee estimates to fee_estimates.dat.
static constexpr std::chrono::hours FEE_FLUSH_INTERVAL{1};
/** fee_estimates.dat that are more than 60 hours (2.5 days) will not be read,
* as the estimates in the file are stale.
*/
static constexpr std::chrono::hours MAX_FILE_AGE{60};
class AutoFile;
class CTxMemPoolEntry;
class TxConfirmStats;
@ -248,6 +253,9 @@ public:
void FlushFeeEstimates()
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
/** Calculates the age of the file, since last modified */
std::chrono::hours GetFeeEstimatorFileAge();
private:
mutable Mutex m_cs_fee_estimator;