diff --git a/src/init.cpp b/src/init.cpp index 92c467f27b8f05..e4b8199e1c7871 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -1664,6 +1665,24 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) ChainstateManager& chainman = *Assert(node.chainman); + assert(!node.fee_estimator); + // Don't initialize block policy fee estimator if we don't relay transactions, + // as new data will not be appended and old data will never be updated. + if (!peerman_opts.ignore_incoming_txs) { + bool read_stale_estimates = args.GetBoolArg("-acceptstalefeeestimates", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES); + if (read_stale_estimates && (chainparams.GetChainType() != ChainType::REGTEST)) { + return InitError(strprintf(_("acceptstalefeeestimates is not supported on %s chain."), chainparams.GetChainTypeString())); + } + + node.fee_estimator = std::make_unique(FeeestPath(args), read_stale_estimates, node.mempool.get()); + node.fee_estimator->RegisterForecaster(std::make_unique(node.mempool.get(), &(chainman.ActiveChainstate()))); + + // Flush block policy estimates to disk periodically + CBlockPolicyEstimator* block_policy_estimator = node.fee_estimator->block_policy_estimator->get(); + scheduler.scheduleEvery([block_policy_estimator] { block_policy_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL); + validation_signals.RegisterValidationInterface(block_policy_estimator); + } + assert(!node.peerman); node.peerman = PeerManager::make(*node.connman, *node.addrman, node.banman.get(), chainman, diff --git a/src/policy/fees/fee_estimator.cpp b/src/policy/fees/fee_estimator.cpp index 0c11c6811949fa..2ae859725232ab 100644 --- a/src/policy/fees/fee_estimator.cpp +++ b/src/policy/fees/fee_estimator.cpp @@ -2,15 +2,19 @@ // Distributed under the MIT software license. See the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include #include +#include #include #include +#include +#include +#include #include -FeeEstimator::FeeEstimator(const fs::path& block_policy_estimator_file_path, const bool read_stale_block_policy_estimates) - : block_policy_estimator(std::make_unique(block_policy_estimator_file_path, read_stale_block_policy_estimates)) +FeeEstimator::FeeEstimator(const fs::path& block_policy_estimator_file_path, const bool read_stale_block_policy_estimates, const CTxMemPool* mempool) + : m_mempool(mempool), block_policy_estimator(std::make_unique(block_policy_estimator_file_path, read_stale_block_policy_estimates)) { } @@ -45,4 +49,59 @@ ForecastResult FeeEstimator::GetPolicyEstimatorEstimate(ConfirmationTarget& targ return ForecastResult(response); } +std::pair, std::vector> FeeEstimator::GetFeeEstimateFromForecasters(ConfirmationTarget& target) +{ + std::vector err_messages; + + // Check for mempool availability + if (m_mempool == nullptr) { + err_messages.emplace_back("Mempool not available."); + return {std::nullopt, err_messages}; + } + + { + LOCK(m_mempool->cs); + if (!m_mempool->GetLoadTried()) { + err_messages.emplace_back("Mempool not finished loading; can't get accurate feerate forecast."); + return {std::nullopt, err_messages}; + } + } + + // Retrieve forecasts from policy estimator and mempool forecasters + const auto policy_estimator_forecast = GetPolicyEstimatorEstimate(target); + if (policy_estimator_forecast.empty()) { + err_messages.emplace_back(strprintf("%s: %s", forecastTypeToString(policy_estimator_forecast.GetResponse().forecaster), + policy_estimator_forecast.GetError().value_or(""))); + } + + auto mempool_forecaster = forecasters.find(ForecastType::MEMPOOL_FORECAST); + Assume(mempool_forecaster != forecasters.end()); + const auto mempool_forecast = mempool_forecaster->second->EstimateFee(target); + if (mempool_forecast.empty()) { + err_messages.emplace_back(strprintf("%s: %s", forecastTypeToString(mempool_forecast.GetResponse().forecaster), + mempool_forecast.GetError().value_or(""))); + } + + std::optional selected_forecast{std::nullopt}; + if (!policy_estimator_forecast.empty() && !mempool_forecast.empty()) { + // Use the forecast with the lower fee rate when both forecasts are available + selected_forecast = std::min(mempool_forecast, policy_estimator_forecast); + } else if (!policy_estimator_forecast.empty()) { + // Use the policy estimator forecast if mempool forecast is not available + selected_forecast = policy_estimator_forecast; + } // Note: If both are empty, selected_forecast remains nullopt + + if (selected_forecast) { + const auto& forecast = *selected_forecast; + LogDebug(BCLog::ESTIMATEFEE, "FeeEst %s: Block height %s, low priority feerate %s %s/kvB, high priority feerate %s %s/kvB.\n", + forecastTypeToString(forecast.GetResponse().forecaster), + forecast.GetResponse().current_block_height, + CFeeRate(forecast.GetResponse().low_priority.fee, forecast.GetResponse().low_priority.size).GetFeePerK(), + CURRENCY_ATOM, + CFeeRate(forecast.GetResponse().high_priority.fee, forecast.GetResponse().high_priority.size).GetFeePerK(), + CURRENCY_ATOM); + } + return {selected_forecast, err_messages}; +} + FeeEstimator::~FeeEstimator() = default; diff --git a/src/policy/fees/forecaster_man.h b/src/policy/fees/forecaster_man.h index 4856b947089b2b..3de9506f5fe21d 100644 --- a/src/policy/fees/forecaster_man.h +++ b/src/policy/fees/forecaster_man.h @@ -1,4 +1,4 @@ -// Copyright (c) 2025 The Bitcoin Core developers +// Copyright (c) 2024 The Bitcoin Core developers // Distributed under the MIT software license. See the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -17,46 +17,41 @@ class ForecastResult; enum class ForecastType; -<<<<<<< HEAD:src/policy/fees/forecaster_man.h -/** \class FeeRateForecasterManager - * Module for managing and utilising multiple fee rate forecasters to provide a forecast for a target. -======= struct ConfirmationTarget; /** \class FeeEstimator * Module for managing and utilising multiple fee rate forecasters to provide fee estimates. ->>>>>>> bc19c02ff3f (fees: add `GetPolicyEstimatorEstimate` method):src/policy/fees/fee_estimator.h * - * The FeeRateForecasterManager class allows for the registration of multiple fee rate + * The ForecasterManager class allows for the registration of multiple fee rate * forecasters. */ -class FeeRateForecasterManager +class ForecasterManager { private: - //! Map of all registered forecasters to their unique pointers. + //! Map of all registered forecasters to their shared pointers. std::unordered_map> forecasters; //! Given a confirmation target get a fee estimate from Block Policy Estimator ForecastResult GetPolicyEstimatorEstimate(ConfirmationTarget& target) const; public: - //! Optional unique pointer to block Block policy forecaster. - std::optional> block_policy_forecaster; + //! Optional unique pointer to block Block policy estimator. + std::optional> block_policy_estimator; /** - * Constructor that initialises FeeRateForecasterManager with a Block policy forecaster forecaster + * Constructor that initialises ForecasterManager with a Block policy forecaster forecaster * - * @param[in] block_policy_forecaster_file_path Path to the Block policy forecaster dump file. - * @param[in] read_stale_block_policy_data Boolean flag indicating whether to read stale Block policy forecaster data. + * @param[in] block_policy_estimator_file_path Path to the Block policy estimator estimator dump file. + * @param[in] read_stale_block_policy_estimates Boolean flag indicating whether to read stale Block policy estimator estimates. */ - FeeRateForecasterManager(const fs::path& block_policy_forecaster_file_path, const bool read_stale_block_policy_data); + ForecasterManager(const fs::path& block_policy_forecaster_file_path, const bool read_stale_block_policy_data); /** - * Default constructor that initialises without a Block policy forecaster. + * Default constructor that initialises without a Block policy estimator estimator. */ - FeeRateForecasterManager(); + ForecasterManager(); - ~FeeRateForecasterManager(); + ~ForecasterManager(); /** * Register a forecaster to provide fee rate estimates.