Skip to content

Commit

Permalink
fees: add GetFeeEstimateFromForecasters method
Browse files Browse the repository at this point in the history
- Fallback to Block policy estimator estimates whenever mempool forecaster
  estimates are higher than block policy estimator.
  • Loading branch information
ismaelsadeeq committed Jan 9, 2025
1 parent d53f070 commit 5f51d44
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 21 deletions.
19 changes: 19 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include <policy/feerate.h>
#include <policy/fees/forecasters/block_policy.h>
#include <policy/fees/forecaster_man.h>
#include <policy/fees/forecasters/mempool.h>
#include <policy/fees_args.h>
#include <policy/policy.h>
#include <policy/settings.h>
Expand Down Expand Up @@ -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<FeeEstimator>(FeeestPath(args), read_stale_estimates, node.mempool.get());
node.fee_estimator->RegisterForecaster(std::make_unique<MemPoolForecaster>(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,
Expand Down
65 changes: 62 additions & 3 deletions src/policy/fees/fee_estimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <policy/fees/fee_estimator.h>
#include <logging.h>
#include <policy/fees.h>
#include <policy/fees/fee_estimator.h>
#include <policy/fees/forecaster.h>
#include <policy/fees/forecaster_util.h>
#include <policy/fees/forecasters/mempool.h>
#include <txmempool.h>

#include <algorithm>
#include <utility>

FeeEstimator::FeeEstimator(const fs::path& block_policy_estimator_file_path, const bool read_stale_block_policy_estimates)
: block_policy_estimator(std::make_unique<CBlockPolicyEstimator>(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<CBlockPolicyEstimator>(block_policy_estimator_file_path, read_stale_block_policy_estimates))
{
}

Expand Down Expand Up @@ -45,4 +49,59 @@ ForecastResult FeeEstimator::GetPolicyEstimatorEstimate(ConfirmationTarget& targ
return ForecastResult(response);
}

std::pair<std::optional<ForecastResult>, std::vector<std::string>> FeeEstimator::GetFeeEstimateFromForecasters(ConfirmationTarget& target)
{
std::vector<std::string> 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<ForecastResult> 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;
31 changes: 13 additions & 18 deletions src/policy/fees/forecaster_man.h
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -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<ForecastType, std::unique_ptr<Forecaster>> 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<std::unique_ptr<CBlockPolicyEstimator>> block_policy_forecaster;
//! Optional unique pointer to block Block policy estimator.
std::optional<std::unique_ptr<CBlockPolicyEstimator>> 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.
Expand Down

0 comments on commit 5f51d44

Please sign in to comment.