Bitcoin Core 内存池 RPC 弃用字段清理:fullrbf 与 bip125-replaceable 键的移除及 -deprecatedrpc 恢复机制
本文围绕 Bitcoin Core 的内存池(mempool)RPC 接口变更展开:自 v28 起 full-RBF 策略成为默认行为、v29 起 -mempoolfullrbf 启动参数被移除之后,getmempoolinfo 的 fullrbf 键与四个 mempool RPC 中的 bip125-replaceable 键也随之退出默认响应,仅可通过 -deprecatedrpc=fullrbf / -deprecatedrpc=bip125 显式恢复。读完本篇,你将理解这笔清理的完整来龙去脉、各 RPC 键的源码位置与判定逻辑,以及自动化脚本、监控工具需要做哪些适配。
背景:full-RBF 策略的演进时间线
要理解这次 RPC 变更,先要回顾 full-RBF(不检查 BIP125 替代信号即允许交易替换)策略在 Bitcoin Core 中的三步演进:
- v24.0.1 引入开关:新增
mempoolfullrbf配置项,允许节点改变对未确认交易的替换中继/挖矿策略,默认仍为关闭(即仅接受 BIP125 opt-in RBF)。该选项在 release-notes-24.0.1.md 中有专门章节,解释了部分比特币服务依赖"先看到、后替换"(first-seen)简化假设的历史背景。 - v28 默认开启:
-mempoolfullrbf默认值由 0 改为 1,即mempoolfullrbf=1成为默认策略(#30493),同时有限度的 package RBF 也被启用(#28984)。见 release-notes-28.0.md。 - v29 移除开关:由于该策略已被广泛采纳,用户关闭它已无实际收益,
-mempoolfullrbf启动参数被彻底移除,full replace-by-fee 成为标准行为(#30592)。见 release-notes-29.0.md。
也就是说,在现行代码中节点行为恒等于 full-RBF,不存在可配置的中间状态。策略侧的证据可见 src/validation.cpp 中 RBF 替换检查处的注释:代码明确注明不再检查交易是否通过 BIP125 或 TRUC 主动声明可替换性(src/validation.cpp 第 967 行附近的注释 "we are not checking whether it opts in to replaceability via BIP125 or TRUC"),替换合法性改由集群(cluster)计数上限与费率图(feerate diagram)改进等规则约束,相关实现位于 src/policy/rbf.cpp。
本次变更的核心内容
按 release-notes-34911.md 的描述,本笔变更包含两个部分:
getmempoolinfo 不再默认返回 fullrbf 键
getmempoolinfo RPC 的响应中停止返回已弃用的 fullrbf 键,除非用户通过 -deprecatedrpc=fullrbf 启动参数显式请求。由于 full-RBF 已是唯一行为,该键此前恒为 true,属于无信息量的遗留字段。
四个 mempool RPC 移除 bip125-replaceable 键
bip125-replaceable 键同样从 mempool RPC 响应中移除(它自 v29 起已被标记为 deprecated),除非用户通过 -deprecatedrpc=bip125 启动参数请求恢复。受影响的 mempool RPC 共四个:
| RPC | 说明 | 移除的键 |
|---|---|---|
getrawmempool |
返回内存池全部交易 ID(verbose 模式返回详情) | bip125-replaceable |
getmempoolancestors |
返回指定交易在池中的全部祖先 | bip125-replaceable |
getmempooldescendants |
返回指定交易在池中的全部后代 | bip125-replaceable |
getmempoolentry |
返回指定交易的内存池条目详情 | bip125-replaceable |
bip125-replaceable 的语义是:该交易自身通过 nSequence 信号声明了 BIP125 可替换性,或者其某个未确认祖先声明了 BIP125 可替换性。在 full-RBF 成为唯一策略后,这一字段不再决定节点是否接受替换,因此从默认可见性中移除是顺理成章的收尾动作。
源码级实现:弃用键如何被条件化返回
IsDeprecatedRPCEnabled 的判定逻辑
两个弃用键的开关都走同一个判定函数,实现位于 src/rpc/server.cpp:
bool IsDeprecatedRPCEnabled(const std::string& method)
{
const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
}
可以观察到:-deprecatedrpc 是一个可重复出现的启动参数,每次调用 GetArgs 取回所有值,函数仅做字符串精确匹配。也就是说,恢复 fullrbf 键与恢复 bip125 键是相互独立的,可以只开其一,也可以同时开启。
getmempoolinfo:fullrbf 键的条件分支
getmempoolinfo 的响应体由 MempoolInfoToJSON 构造(src/rpc/mempool.cpp)。在默认情况下,它输出 loaded、size、bytes、usage、total_fee、maxmempool、mempoolminfee、minrelaytxfee、incrementalrelayfee、unbroadcastcount、permitbaremultisig、maxdatacarriersize、limitclustercount、limitclustersize、optimal 等字段,末尾是本次变更的条件分支:
if (IsDeprecatedRPCEnabled("fullrbf")) {
ret.pushKV("fullrbf", true);
}
注意 fullrbf 的值被硬编码为 true——这与"full-RBF 已是唯一行为"的事实一致。RPC 元数据(RPCResult 描述,供 help 输出与接口文档生成)也做了同样的条件化,见 src/rpc/mempool.cpp:仅当启用 fullrbf 弃用开关时,fullrbf 字段描述("True if the mempool accepts RBF without replaceability signaling inspection (DEPRECATED)")才会出现在结果 schema 中。
四个 RPC 共享 MempoolEntryDescription 与 entryToJSON
四个受影响的 RPC 之所以行为一致,是因为它们共享同一套条目序列化代码:
- RPC 元数据层:
MempoolEntryDescription()中bip125-replaceable字段描述被包裹在if (IsDeprecatedRPCEnabled("bip125"))分支内(src/rpc/mempool.cpp)。getrawmempool(verbose=true)、getmempoolancestors、getmempooldescendants、getmempoolentry的RPCResult均引用该描述函数,因此一个条件控制四处的 schema。 - 实际响应层:
entryToJSON()负责把CTxMemPoolEntry序列化为 JSON,其末尾的 RBF 状态段同样条件化(src/rpc/mempool.cpp):
// Add opt-in RBF status
if (IsDeprecatedRPCEnabled("bip125")) {
bool rbfStatus = false;
RBFTransactionState rbfState = IsRBFOptIn(tx, pool);
if (rbfState == RBFTransactionState::UNKNOWN) {
throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool");
} else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) {
rbfStatus = true;
}
info.pushKV("bip125-replaceable", rbfStatus);
}
这段代码还保留了一个历史遗留的防御:若交易不在内存池中(IsRBFOptIn 返回 UNKNOWN),会抛出 RPC_MISC_ERROR。正常调用路径下 getmempoolentry 等 RPC 会先校验 txid 在池中,因此该分支实际不会触发。
bip125-replaceable 的判定原理
即便显式恢复了该键,理解其判定逻辑也有助于正确解读旧脚本的输出。判定函数 IsRBFOptIn 位于 src/policy/rbf.cpp,在持有内存池锁的前提下执行:
- 先查交易自身:调用
SignalsOptInRBF(tx),若任一输入的nSequence <= MAX_BIP125_RBF_SEQUENCE则视为主动声明。该常数是0xfffffffd(即SEQUENCE_FINAL - 2),定义于 src/util/rbf.h,也是send/createrawtransaction等 RPC 默认使用的"replaceable"序列号(src/wallet/rpc/spend.cpp 中m_signal_bip125_rbf为真时即写入该值)。 - 交易不在本地内存池则返回 UNKNOWN:无法确认其所有输入来源时不做臆断。
- 检查未确认祖先:交易自身所有输入
nSequence >= maxint-1时,仍可能因为其某个未确认祖先声明了 RBF 而被视为可替换(BIP125 的"继承"语义)。
返回的三态枚举(FINAL / REPLACEABLE_BIP125 / UNKNOWN)定义于 src/policy/rbf.h,entryToJSON 只把 REPLACEABLE_BIP125 映射为 JSON 布尔 true,其余映射为 false。
实操:如何恢复旧字段并验证
启动参数写法
在 bitcoind 命令行或 bitcoin.conf 中使用(可重复指定以同时恢复多个字段):
-bitcoind -deprecatedrpc=fullrbf
bitcoind -deprecatedrpc=fullrbf -deprecatedrpc=bip125
- 只开
-deprecatedrpc=fullrbf:getmempoolinfo恢复fullrbf键,四个 mempool 条目 RPC 仍不返回bip125-replaceable。 - 只开
-deprecatedrpc=bip125:四个 mempool 条目 RPC 恢复bip125-replaceable键,getmempoolinfo仍无fullrbf。 - 注意该机制是节点级的,无法按单次 RPC 调用切换;需要旧字段的消费方必须在启动参数层面固定开启。
验证方式
可以运行内置功能测试 test/functional/feature_rbf.py,其测试参数即为 self.extra_args = [["-deprecatedrpc=fullrbf", "-deprecatedrpc=bip125"], []],正是本变更配套的回归验证:它依赖两个弃用键同时可用来断言旧响应结构,确保恢复路径仍然工作。本地复现可用 CMake 构建后通过 ctest 执行该测试(构建方式见 INSTALL.md)。
迁移建议
- 只读取字段的脚本(如监控
getmempoolinfo的 Grafana 面板、依赖bip125-replaceable做 RBF 状态判断的钱包工具):优先改为不依赖这两个字段——full-RBF 下fullrbf恒为true,bip125-replaceable也不影响替换是否被接受;如必须兼容,则在服务启动参数中加-deprecatedrpc=fullrbf/-deprecatedrpc=bip125,并留意这两个键在后续版本中可能进一步移除。 - 做 RBF 状态判断的客户端:可以推断,在 full-RBF 节点上更有意义的信号是费率图(
getmempoolfeeratediagram)与集群/分块(chunk)信息,而非单条交易的 BIP125 声明;entryToJSON保留的fees、chunkweight、chunks等字段不受本次变更影响。 - 关联变更提醒:同一批次还有钱包侧的姊妹变更(release-notes-34917.md):
listtransactions、listsinceblock、gettransaction等钱包交易 RPC 中的bip125-replaceable键同样标记弃用,仍可通过同一个-deprecatedrpc=bip125恢复;-walletrbf启动选项也已弃用且将在下个版本移除,使用时会在日志中产生警告。注意钱包侧与 mempool 侧共用bip125这一个开关名,开启后两处同时恢复。
小结
本次变更(doc/release-notes-34911.md)是 full-RBF 策略落地后的收尾工作:v24.0.1 引入开关、v28 默认开启、v29 移除参数之后,RPC 响应里最后残留的 fullrbf 与 bip125-replaceable 两个弃用键也退出了默认可见性。核心实现集中在 src/rpc/mempool.cpp(MempoolInfoToJSON 与 entryToJSON 的条件分支)和 src/rpc/server.cpp(IsDeprecatedRPCEnabled 通用开关),行为回归由 test/functional/feature_rbf.py 覆盖。对下游工具而言,影响面是"两个 JSON 键消失",恢复路径明确且粒度独立,迁移成本可控;但弃用键不应作为长期依赖,建议借机将监控与自动化脚本改为基于费率图、集群信息等 full-RBF 时代的正式字段。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00