mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 21:52:53 +02:00
Introduce FeeRateEstimatorManager to wrap CBlockPolicyEstimator and act as the single point of contact for fee rate estimation in the node. It inherits CValidationInterface so it can register directly with the validation signals and receive mempool/block events. Wire it into NodeContext (fee_estimator_man), init, shutdown, the RPC server utility helpers (EnsureAnyFeeEstimatorMan), and the wallet-facing interfaces::Chain API. The Chain method estimateSmartFee is renamed to getFeeRateEstimate and now returns util::Expected<FeeRateEstimation, FeeRateEstimationError> instead of CFeeRate, so callers get the full estimation context without needing FeeCalculation. estimateMaxBlocks is renamed to maximumFeeEstimationTargetBlocks (still returns the max target). CBlockPolicyEstimator no longer inherits CValidationInterface; the manager now receives the mempool/block validation events and forwards them to the CBlockPolicyEstimator. Co-authored-by: willcl-ark <will@256k1.dev>
102 lines
4.4 KiB
C++
102 lines
4.4 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <wallet/fees.h>
|
|
|
|
#include <policy/feerate.h>
|
|
#include <util/fees.h>
|
|
#include <wallet/coincontrol.h>
|
|
#include <wallet/wallet.h>
|
|
|
|
#include <optional>
|
|
|
|
namespace wallet {
|
|
CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
|
|
{
|
|
return GetRequiredFeeRate(wallet).GetFee(static_cast<int32_t>(nTxBytes));
|
|
}
|
|
|
|
|
|
CAmount GetMinimumFee(const MinimumFeeRateResult& min_fee_rate, unsigned int nTxBytes)
|
|
{
|
|
return min_fee_rate.fee_rate.GetFee(static_cast<int32_t>(nTxBytes));
|
|
}
|
|
|
|
CFeeRate GetRequiredFeeRate(const CWallet& wallet)
|
|
{
|
|
return std::max(wallet.m_min_fee, wallet.chain().relayMinFee());
|
|
}
|
|
|
|
MinimumFeeRateResult GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control)
|
|
{
|
|
/* User control of how to calculate fee uses the following parameter precedence:
|
|
1. coin_control.m_feerate
|
|
2. coin_control.m_confirm_target
|
|
3. m_confirm_target (user-set member variable of wallet)
|
|
The first parameter that is set is used.
|
|
*/
|
|
if (coin_control.m_feerate) { // 1.
|
|
CFeeRate fee_rate{*coin_control.m_feerate};
|
|
// Allow to override automatic min/max check over coin control instance
|
|
if (coin_control.fOverrideFeeRate) return {fee_rate, FeeReason::USER_SPECIFIED, std::nullopt};
|
|
|
|
CFeeRate required_feerate = GetRequiredFeeRate(wallet);
|
|
if (required_feerate > fee_rate) return {required_feerate, FeeReason::REQUIRED, std::nullopt};
|
|
|
|
return {fee_rate, FeeReason::USER_SPECIFIED, std::nullopt};
|
|
}
|
|
|
|
// We will use smart fee estimation
|
|
unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : wallet.m_confirm_target;
|
|
// By default estimates are economical iff we are signaling opt-in-RBF
|
|
bool conservative_estimate = !coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf);
|
|
// Allow to override the default fee estimate mode over the CoinControl instance
|
|
if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE)
|
|
conservative_estimate = true;
|
|
else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL)
|
|
conservative_estimate = false;
|
|
|
|
const auto fee_estimation_res = wallet.chain().getFeeRateEstimate(target, conservative_estimate);
|
|
const FeeRateEstimation& estimation{FeeRateEstimationRef(fee_estimation_res)};
|
|
CFeeRate fee_rate{estimation.feerate};
|
|
FeeReason fee_reason{FeeReason::FEE_RATE_ESTIMATOR};
|
|
// Only fee rate estimator results have a returned target.
|
|
std::optional<int> returned_target{estimation.returned_target};
|
|
if (fee_rate == CFeeRate(0)) {
|
|
// if we don't have enough data for getFeeRateEstimate, then use fallback fee
|
|
fee_rate = wallet.m_fallback_fee;
|
|
fee_reason = FeeReason::FALLBACK;
|
|
returned_target = std::nullopt;
|
|
// directly return if fallback fee is disabled (feerate 0 == disabled)
|
|
if (wallet.m_fallback_fee == CFeeRate(0)) return {fee_rate, FeeReason::FALLBACK, std::nullopt};
|
|
}
|
|
|
|
// Obey mempool min fee when using smart fee estimation or fallback fee
|
|
CFeeRate min_mempool_feerate = wallet.chain().mempoolMinFee();
|
|
if (fee_rate < min_mempool_feerate) {
|
|
fee_rate = min_mempool_feerate;
|
|
fee_reason = FeeReason::MEMPOOL_MIN;
|
|
returned_target = std::nullopt;
|
|
}
|
|
|
|
CFeeRate required_feerate = GetRequiredFeeRate(wallet);
|
|
if (required_feerate > fee_rate) return {required_feerate, FeeReason::REQUIRED, std::nullopt};
|
|
|
|
return {fee_rate, fee_reason, returned_target};
|
|
}
|
|
|
|
CFeeRate GetDiscardRate(const CWallet& wallet)
|
|
{
|
|
unsigned int highest_target = wallet.chain().maximumFeeEstimationTargetBlocks();
|
|
const auto res = wallet.chain().getFeeRateEstimate(highest_target, /*conservative=*/false);
|
|
auto discard_rate = res ? CFeeRate(res->feerate) : CFeeRate(0);
|
|
// Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
|
|
discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate);
|
|
// Discard rate must be at least dust relay feerate
|
|
discard_rate = std::max(discard_rate, wallet.chain().relayDustFee());
|
|
return discard_rate;
|
|
}
|
|
} // namespace wallet
|