Bitcoin Core 费率估算升级:estimatesmartfee 双估算器机制与 mempool 费率估算器详解
【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin
Bitcoin Core 的estimatesmartfeeRPC 在本版本(对应发布说明 doc/release-notes-34075.md)中完成了一次重要的费率估算架构升级:它不再只依赖历史上的块策略(block policy)费率估算器,而是引入了一个全新的内存池(mempool)费率估算器,并将二者结果组合取低。读完本文,你可以掌握estimatesmartfee新增的options参数、响应字段的变化、费率数据文件的迁移规则,以及钱包费率选择中fee_reason语义调整背后的源码实现。
变更总览
本次变更由 doc/release-notes-34075.md 描述,核心要点如下:
estimatesmartfee现在组合两个费率估算器:原有的块策略估算器和新增的内存池估算器;- 新的内存池估算器基于当前内存池内容,产生 conservative(保守)和 economical(经济)两种费率估算;只有当最近区块显示内存池"健康"时才产出估算,内存池过于稀疏时退化为"最低中继费率与当前内存池最低费率中的较高者";其统计信息持久化到
fees/mempool_policy_estimator.dat,并在启动时重新加载; estimatesmartfee返回两个估算器结果中的较低者,即内存池估算器只能调低而不能调高块策略的估算;- 组合估算要求两个估算器都成功。若内存池估算器无法产出估算(例如内存池仍在加载、观察到的近期区块过少、或怀疑内存池不健康),RPC 直接返回错误,而不是静默回退到块策略估算;
estimatesmartfee新增options对象,包含fee_rate_estimator与verbosity两个参数;- 块策略估算器的数据文件迁移到
fees/block_policy_estimates.dat,旧的fee_estimates.dat在启动时自动迁移或删除; - 钱包的费率估算使用默认的组合估算;
- 钱包交易创建 RPC 返回的
fee_reason字段语义调整(见后文)。
两个费率估算器及其分工
块策略估算器(原有)
原有的CBlockPolicyEstimator基于历史确认数据(哪些费率档位的交易在多少个区块内被确认)做统计推断,支持 1–1008 个区块的确认目标。实现位于 src/policy/fees/block_policy_estimator.h。
内存池估算器(新增)
新增的MemPoolFeeRateEstimator定义在 src/policy/fees/mempool_estimator.h,其工作原理从源码注释可以直接读出:
- 它调用 Bitcoin Core 的区块构建算法,从当前内存池生成分块(chunk)形式的"块模板",然后对选中 chunk 的费率计算分位数:75 分位数作为 economical 估算,50 分位数作为 conservative 估算(见 src/policy/fees/mempool_estimator.h#L80-L94 的类注释);
- 由于内存池状况变化快,它只对较短的确认目标可靠,
MaximumTarget()固定返回MEMPOOL_FEE_ESTIMATOR_MAX_TARGET{2}(见 src/policy/fees/mempool_estimator.h#L31-L33),因此该估算器的blocks字段恒为 2; - 估算结果带 7 秒的进程内缓存(
CACHE_LIFE{7}),仅当链尖未变化且缓存未过期时复用,避免高频调用重复构建块模板; - 内存池健康检查:估算器会追踪最近
MEMPOOL_HEALTH_WINDOW_BLOCKS = 6个已挖区块的重量统计(MinedBlockStats,包含区块高度、区块内非 coinbase 交易总权重、为这些区块从内存池移除的交易总权重),并以MEMPOOL_REPRESENTATION_THRESHOLD = 0.75作为"内存池代表度"阈值(见 src/policy/fees/mempool_estimator.h#L36-L48)。从源码结构看,只有当被挖区块的内容能较好代表当前内存池时,内存池估算才可信;否则按发布说明的约定,返回"最低中继费率与内存池最低费率中的较高者"或干脆报错。
组合逻辑:取低值且不容错
两个估算器由FeeRateEstimatorManager统一管理,实现见 src/policy/fees/estimator_man.cpp:
// estimator_man.cpp 中 GetFeeRateEstimate 的核心逻辑 auto block_policy_estimate = m_block_policy_estimator->EstimateFeeRate(target, conservative); if (!block_policy_estimate) { ...; return block_policy_estimate; } auto mempool_estimate = m_mempool_estimator->EstimateFeeRate(conservative); if (!mempool_estimate) { // A failed mempool estimate is surfaced as a warning rather than silently returning the // block policy estimate, which callers can still request explicitly. return mempool_estimate; } auto selected_estimate = std::min(*block_policy_estimate, *mempool_estimate);(见 src/policy/fees/estimator_man.cpp#L25-L44)
这里有两点值得注意:
std::min决定了"只降不升"的语义:默认组合估算只会选出两者中较低的费率,这正是发布说明中"the mempool fee rate estimator can only lower the block policy fee rate estimate"的实现依据;- 内存池估算失败即整体失败:源码注释明确说明,内存池估算失败不会静默回退到块策略结果,而是把错误暴露给调用方——调用者此时可显式改用
"block_policy"取值。这与发布说明中"If the mempool fee rate estimator cannot produce an estimate ... an error is returned"完全一致。
estimatesmartfee:参数与响应字段
RPC 的完整定义在 src/rpc/fees.cpp#L33-L142,参数签名为:
estimatesmartfee conf_target estimate_mode optionsconf_target(必填):确认目标,单位区块数(1 - 1008);estimate_mode(默认"economical"):economical或conservative,与 BIP 125 RBF 场景相关;options(可选对象),新增两个键:
| 键 | 类型 / 默认值 | 说明 |
|---|---|---|
fee_rate_estimator | 字符串,默认"none" | "none":组合行为,返回两个估算器中的较低值;若内存池估算不可用则直接报错。"block_policy":仅使用块策略估算器(即旧版本行为)。"mempool_policy":仅使用内存池估算器。未知取值一律按"none"处理 |
verbosity | 数字,默认1 | 1只返回 feerate 或错误;2额外返回mempool_health_statistics |
参数解析处的对应实现见 src/rpc/fees.cpp#L100-L112,未知取值按"none"处理由FeeRateEstimatorTypeFromString保证。
响应字段
成功时(fee_rate_estimator为"none"):
| 字段 | 说明 |
|---|---|
feerate | 估算费率(BTC/kvB,按 BIP 141 虚拟大小折算)。注意 RPC 层还会再做一次抬升:feerate = max(估算值, 内存池当前最低费率, 最低中继费率),见 src/rpc/fees.cpp#L113-L117 |
estimator | 标识本次结果由哪个估算器产出(仅在fee_rate_estimator为"none"且估算成功时出现) |
blocks | 估算所对应的确认目标。块策略估算器返回实际找到估算的目标(至少 2、至多估算器可用最大目标);内存池估算器恒为 2 |
mempool_health_statistics | 仅verbosity >= 2时出现:最近被挖区块的健康统计数组,每项含block_height、block_weight、mempool_txs_weight,数据来源为MempoolPolicyEstimatorBlocksStats(),见 src/rpc/fees.cpp#L127-L138 |
失败时返回errors数组。调用示例:
# 默认组合估算(可能返回错误而不是回退) bitcoin-cli estimatesmartfee 6 # 显式使用块策略估算器(旧行为) bitcoin-cli estimatesmartfee 6 economical '{"fee_rate_estimator":"block_policy"}' # 仅使用内存池估算器,并查看健康统计 bitcoin-cli estimatesmartfee 6 economical '{"fee_rate_estimator":"mempool_policy","verbosity":2}'fee_rate_estimator帮助文本与发布说明措辞一一对应(见 src/rpc/fees.cpp#L47-L54)。
数据文件:迁移到 fees/ 子目录并清理旧文件
费率估算器的持久化路径与旧文件迁移逻辑实现在 src/policy/fees/estimator_args.cpp:
- 块策略估算器数据:
<数据目录>/<chain>/fees/block_policy_estimates.dat; - 内存池估算器统计:
<数据目录>/<chain>/fees/mempool_policy_estimator.dat; - 旧文件:
<数据目录>/<chain>/fee_estimates.dat。
迁移函数MaybeMigrateLegacyFeeEstimates(见 src/policy/fees/estimator_args.cpp#L24-L50)的三条分支与发布说明完全吻合:
- 旧文件不存在:不做任何事;
- 新文件不存在:把旧
fee_estimates.dat移动(rename)到fees/block_policy_estimates.dat,并先确保fees/目录存在; - 两个文件都存在:直接删除旧文件(删除失败仅记录警告并继续运行)。
数据文件布局与 doc/files.md 中的数据目录清单一致:fees/目录下存放block_policy_estimates.dat和mempool_policy_estimator.dat,用于存储块策略与内存池策略估算器数据。对运维的含义是:升级后首次启动会在fees/目录下看到这两个.dat文件,根目录下不再新增fee_estimates.dat;两个文件每 1 小时刷盘一次(FEE_FLUSH_INTERVAL,见 src/policy/fees/estimator_man.h#L24-L25),关机时也会强制刷盘。
钱包侧影响:fee_reason 语义调整
钱包构造交易时的默认费率链路在 src/wallet/fees.cpp 的GetMinimumFeeRate中(见 src/wallet/fees.cpp#L32-L88)。参数优先级为:coin_control.m_feerate>coin_control.m_confirm_target> 钱包的m_confirm_target;当未显式指定费率时,钱包通过wallet.chain().getFeeRateEstimate(target, conservative_estimate)取估算——这正是发布说明中"Wallet fee rate estimation uses the default combined estimate"的实现路径,即钱包默认享受组合(取低)估算。
同时,返回给 RPC 的fee_reason字段不再透出块策略估算器内部的阈值细节(如 HALF_ESTIMATE/FULL_ESTIMATE/DOUBLE_ESTIMATE/CONSERVATIVE 这类内部原因),而是报告"钱包为何选中该费率"的宏观原因:
| fee_reason | 触发条件(对应源码) |
|---|---|
| 费率估算器(FEE_RATE_ESTIMATOR) | 估算成功且未被后续规则改写(src/wallet/fees.cpp#L64) |
| fallback | 估算不可用(费率为 0)时使用wallet.m_fallback_fee(src/wallet/fees.cpp#L67-L74) |
| mempool minimum | 估算/fallback 费率低于当前内存池最低费率,被抬升到内存池最低值(src/wallet/fees.cpp#L76-L82) |
| minimum required | 仍低于节点强制的最低要求费率时,抬升至该值(src/wallet/fees.cpp#L84-L85) |
这些内部阈值细节在调试时仍可从块策略估算器的 debug 日志中获得(启用ESTIMATEFEE日志类别即可,组合估算的选择过程也会记录BCLog::ESTIMATEFEE级别的 debug 日志,见 src/policy/fees/estimator_man.cpp#L40-L42)。
测试与验证路径
改动配套的功能测试与单元测试可用于验证行为:
- test/functional/feature_fee_estimation.py:费率估算功能级测试,覆盖双估算器组合与数据文件迁移(含
mempool_policy_estimator相关断言); - test/functional/rpc_estimatefee.py:针对
estimatesmartfee/estimaterawfee的fee_rate_estimator参数取值与错误路径的测试; - src/test/mempool_fee_estimator_tests.cpp:内存池估算器的单元测试;
- src/test/fees_util_tests.cpp:估算器类型字符串解析等工具函数测试;
- src/test/fuzz/fees.cpp:对
FeeReason等枚举字符串化的模糊测试。
小结
这次变更让estimatesmartfee在保留历史统计估算能力的同时,能够利用"如果现在挖区块,实际会选哪些交易"这一更强的即时信号:默认取两者较低值以给出更经济的费率,同时通过"两者都成功才成功"的严格契约避免在不健康的内存池状态下给出误导性估算;而fee_rate_estimator选项和mempool_health_statistics调试输出,则为依赖旧行为或需要排障的调用方提供了显式的逃生通道与可观测性。
【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考