diff --git a/src/init.cpp b/src/init.cpp index 7d3cfe22a80..25832b06a0e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -1494,24 +1495,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) rng.rand64(), *node.addrman, *node.netgroupman, chainparams, args.GetBoolArg("-networkactive", true)); - assert(!node.forecasterman); - // Don't initialize fee estimation with old data if we don't relay transactions, - // as they would never get 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.forecasterman = std::make_unique(); - auto block_policy_estimator = std::make_shared(FeeestPath(args), read_stale_estimates); - validation_signals.RegisterSharedValidationInterface(block_policy_estimator); - // Flush block policy estimates to disk periodically - scheduler.scheduleEvery([block_policy_estimator] { block_policy_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL); - - // Register block policy estimator to forecaster manager - node.forecasterman->RegisterForecaster(block_policy_estimator); - } - for (const std::string& socket_addr : args.GetArgs("-bind")) { std::string host_out; uint16_t port_out{0}; @@ -1726,6 +1709,26 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) ChainstateManager& chainman = *Assert(node.chainman); + assert(!node.forecasterman); + // Don't initialize fee estimation with old data if we don't relay transactions, + // as they would never get 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.forecasterman = std::make_unique(); + auto mempool_forecaster = std::make_shared(node.mempool.get(), &(chainman.ActiveChainstate())); + node.forecasterman->RegisterForecaster(mempool_forecaster); + auto block_policy_estimator = std::make_shared(FeeestPath(args), read_stale_estimates); + validation_signals.RegisterSharedValidationInterface(block_policy_estimator); + // Flush block policy estimates to disk periodically + scheduler.scheduleEvery([block_policy_estimator] { block_policy_estimator->FlushFeeEstimates(); }, FEE_FLUSH_INTERVAL); + + // Register block policy estimator to forecaster manager + node.forecasterman->RegisterForecaster(block_policy_estimator); + } + assert(!node.peerman); node.peerman = PeerManager::make(*node.connman, *node.addrman, node.banman.get(), chainman, diff --git a/src/policy/fees/forecaster_man.cpp b/src/policy/fees/forecaster_man.cpp index 9157daab8bf..f4fc66ce50a 100644 --- a/src/policy/fees/forecaster_man.cpp +++ b/src/policy/fees/forecaster_man.cpp @@ -2,11 +2,13 @@ // 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 void FeeRateForecasterManager::RegisterForecaster(std::shared_ptr forecaster) @@ -20,3 +22,53 @@ CBlockPolicyEstimator* FeeRateForecasterManager::GetBlockPolicyEstimator() Forecaster* block_policy_estimator = forecasters.find(ForecastType::BLOCK_POLICY)->second.get(); return dynamic_cast(block_policy_estimator); } + +std::pair> FeeRateForecasterManager::ForecastFeeRateFromForecasters( + int target, bool conservative) const +{ + std::vector err_messages; + ForecastResult feerate_forecast; + + for (const auto& forecaster : forecasters) { + auto curr_forecast = forecaster.second->ForecastFeeRate(target, conservative); + + if (curr_forecast.m_error.has_value()) { + err_messages.emplace_back( + strprintf("%s: %s", forecastTypeToString(forecaster.first), curr_forecast.m_error.value())); + } + + // Handle case where the block policy forecaster does not have enough data. + if (forecaster.first == ForecastType::BLOCK_POLICY && curr_forecast.feerate.IsEmpty()) { + return {ForecastResult(), err_messages}; + } + + if (!curr_forecast.feerate.IsEmpty()) { + if (feerate_forecast.feerate.IsEmpty()) { + // If there's no selected forecast, choose curr_forecast as feerate_forecast. + feerate_forecast = curr_forecast; + } else { + // Otherwise, choose the smaller as feerate_forecast. + feerate_forecast = std::min(feerate_forecast, curr_forecast); + } + } + } + + if (!feerate_forecast.feerate.IsEmpty()) { + LogInfo("Fee rate Forecaster %s: Block height %s, fee rate %s %s/kvB.\n", + forecastTypeToString(feerate_forecast.forecaster), + feerate_forecast.current_block_height, + CFeeRate(feerate_forecast.feerate.fee, feerate_forecast.feerate.size).GetFeePerK(), + CURRENCY_ATOM); + } + + return {feerate_forecast, err_messages}; +} + +unsigned int FeeRateForecasterManager::MaximumTarget() const +{ + unsigned int maximum_target{0}; + for (const auto& forecaster : forecasters) { + maximum_target = std::max(maximum_target, forecaster.second->MaximumTarget()); + } + return maximum_target; +} diff --git a/src/policy/fees/forecaster_man.h b/src/policy/fees/forecaster_man.h index f6b409ec402..bf46d85f321 100644 --- a/src/policy/fees/forecaster_man.h +++ b/src/policy/fees/forecaster_man.h @@ -6,6 +6,7 @@ #define BITCOIN_POLICY_FEES_FORECASTER_MAN_H #include +#include #include class CBlockPolicyEstimator; @@ -22,7 +23,6 @@ class FeeRateForecasterManager private: //! Map of all registered forecasters to their shared pointers. std::unordered_map> forecasters; - public: /** * Register a forecaster. @@ -34,6 +34,22 @@ public: * Return the pointer to block policy estimator. */ CBlockPolicyEstimator* GetBlockPolicyEstimator(); + + /** + * Get a fee rate forecast from all registered forecasters for a given confirmation target. + * + * Polls all registered forecasters and selects the lowest fee rate. + * + * @param[in] target The target within which the transaction should be confirmed. + * @param[in] conservative True if the package cannot be fee bumped later. + * @return A pair consisting of the forecast result and a vector of forecaster names. + */ + std::pair> ForecastFeeRateFromForecasters(int target, bool conservative) const; + + /** + * @brief Returns the maximum supported confirmation target from all forecasters. + */ + unsigned int MaximumTarget() const; }; #endif // BITCOIN_POLICY_FEES_FORECASTER_MAN_H