首页
/ Bitcoin Core 23.1 版本解析:升级指南、P2P 自我广播修复与 RPC/构建/GUI 维护更新全景

Bitcoin Core 23.1 版本解析:升级指南、P2P 自我广播修复与 RPC/构建/GUI 维护更新全景

2026-09-06 18:44:29作者:滑思眉Philip

Bitcoin Core 23.1 是 23.x 版本线发布的首个维护版,围绕地址广播时间戳、RPC 边界崩溃、Qt 构建链、Windows 代码签名与跨平台兼容性进行集中修补。本文以 doc/release-notes/release-notes-23.1.md 为骨架,逐条展开每项修复的来龙去脉,并结合当前仓库源码核对其实现逻辑。阅读后你将掌握 23.0 → 23.1 的升级与兼容性要点,理解每条 PR 背后对应的网络层、RPC 层与构建层机制,以及如何在当前源码中定位这些逻辑。需要说明的是,本仓库现处于更高版本的主线,文中源码证据来自当前树,反映的是当年修复逻辑的演进形态,可作为理解该版本历史行为的地图。

发布定位:23.x 分支的首个维护版

自 Bitcoin Core 切换到简化版本号(22.0、23.0、24.0…)后,同一主版本线内的补丁发布(如 23.1)以修复缺陷、加固安全、更新翻译为主,语义上即"可直接替换 23.0 的维护版本"。原始发布说明将其概括为 "This release includes new features, various bug fixes and performance improvements, as well as updated translations",并提示用户通过官方问题跟踪渠道上报 bug、订阅安全与更新公告。

23.1 的修复面横跨五个领域:

领域 涉及 PR 核心问题
P2P #25314 自我广播地址未设置时间戳,可能被对端当作陈旧地址丢弃
RPC/API #25220、#25237、#25983、#26275 多签地址误告警、文档检查工具拷贝开销、HTTP 路径处理器数据竞争、deriveaddresses 极端索引崩溃
构建系统 #25201、#25788、#25861、#25985 Windows 证书续期、NSIS 可复现构建、Guix 交叉工具链、Homebrew sqlite 行为回退
GUI #24668、gui#631、gui#680 Qt 版本提升、watch-only 钱包加密入口、macOS 13 崩溃
测试/杂项 #24454、#26321 外部输入权重计算、翻译工作流调整

后续各节将逐一展开,并在 23.x 分支全部发布说明的归档目录 doc/release-notes/ 中可以找到 23.0、23.1、23.2 等相邻版本用于对照。

升级方法与系统兼容性

升级步骤(How to Upgrade)

23.1 属于同主版本线内的就地升级,发布说明给出的标准动作是:

  1. 彻底关闭旧版本:先停止运行中的 bitcoind / Bitcoin-Qt,等待进程完全退出。某些情况下(例如正在冲刷 UTXO 缓存、flush 数据目录)完全退出可能需要几分钟,不能中途强制结束。
  2. 覆盖安装新二进制
    • Windows:运行新版安装包;
    • macOS:用新版本覆盖 /Applications/Bitcoin-Qt
    • Linux:用新构建的 bitcoind / bitcoin-qt 覆盖旧文件。

从已到达 EOL(生命周期结束)的远古版本直接升级到 23.1 也是允许的,但如果数据目录需要迁移,首次启动可能耗时较长;而旧版钱包格式一般仍然兼容支持,无需额外转换。

补充一条可落地的下载校验建议:对发布二进制有疑问的操作者,可以使用本仓库自带的官方校验脚本 contrib/verify-binaries/verify.py 及其使用说明 contrib/verify-binaries/README.md 验证签名与哈希,再执行升级,而不是盲目信任下载源。

兼容性边界

按 23.1 的发布说明,官方支持并经过充分测试的操作系统是:Linux 内核系操作系统、macOS 10.15+、Windows 7 及更新版本。其余类 Unix 系统"应当可用"但测试频率较低,不推荐在未支持系统上运行节点。需注意这是 23.1 时代的平台下限,后续更高主版本已上调最低系统要求,各版本请以其自身发布说明为准。

P2P 层:#25314 为自我广播地址补齐时间戳

自我广播(self-advertisement)的机制

节点为了让自己对网络可达(让对等节点能反向连接、或通过 getaddr 把自己的地址传播出去),会周期性向对端发送 addr(或 addr2/addrv2)消息,其中携带自己的对外地址,这类报文叫自我广播。当前主线源码中,这一逻辑位于 src/net_processing.cpp,关键片段为:

if (std::optional<CService> local_service = GetLocalAddrForPeer(node)) {
    CAddress local_addr{*local_service, peer.m_our_services, Now<NodeSeconds>()};
    if (peer.m_next_local_addr_send == 0us) {
        // 首条自我宣告单独成消息发送,避免地址频率限制
        // 在首条消息已包含多个地址时忽略它
        if (IsAddrCompatible(peer, local_addr)) {
            std::vector<CAddress> self_announcement{local_addr};
            if (peer.m_wants_addrv2) {
                MakeAndPushMessage(node, NetMsgType::ADDRV2, CAddress::V2_NETWORK(self_announcement));
            } else {
                MakeAndPushMessage(node, NetMsgType::ADDR, CAddress::V1_NETWORK(self_announcement));
            }
        }
    } else {
        // 后续的自我宣告与其它地址一起经 PushAddress 发送
        PushAddress(peer, local_addr);
    }
    peer.m_next_local_addr_send = current_time + m_rng.rand_exp_duration(AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL);
}

注意这里构造 CAddress 时显式传入了 Now<NodeSeconds>() 作为第三个参数,即把 nTime 设为"当前时间"——这正是 #25314 要保证的行为:自我广播地址必须总是携带有效的时间戳

修复的根源:不带 nTime 的地址会被对端判为陈旧

addr 消息中每个地址记录都带 4 字节 nTime。接收方在把地址喂给地址管理器之前,会先对时间戳做健康检查。当前主线的对应处理(src/net_processing.cpp)如下:

if (addr.nTime <= NodeSeconds{100000000s} || addr.nTime > current_time + 10min) {
    addr.nTime = std::chrono::time_point_cast<std::chrono::seconds>(current_time - 5 * 24h);
}
if (addr.nTime > current_time - 10min && !peer.m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) { ... }

即:时间戳过老(早于纪元后 100000000 秒,基本等价于"未设置/为 0")或超出当前时间 10 分钟以上的记录,会被直接改写时间。在 #25314 修复前,自我广播构造出的 CAddress 没有正确填充当前时间,导致对端在时效性检查中将其判为不可信/陈旧,进而影响本节点地址在公网中的可发现性——地址可能进不了对端的候选池,也难以被反向连接。修复后,无论首条独立广播还是后续混合广播,自我地址都携带"刚刚生成"的时间戳。

实现细节上值得注意:首条自我宣告必须独立成消息。代码注释说明,这样做的目的是避免首条消息恰好包含多条地址时,因发送端地址频率限制(rate-limit 起始令牌不足)而把自我广播挤掉。对端则按 m_wants_addrv2 能力协商,选择 addrv2(V2 序列化,可携带 BIP155 网络地址)或传统 addr(V1)承载,这点可通过同一文件里的 IsAddrCompatiblePushAddresssrc/net_processing.cpp)观察完整发送路径。

RPC 与 API 修复

createmultisig:修复 p2sh-segwit 的误告警(#25220)

createmultisig 用于创建 m-of-n 多重签名地址,其实现位于 src/rpc/output_script.cpp,参数结构如下:

参数 类型 必填/默认 说明
nrequired num 必填 需要的签名数量(m)
keys json array 必填 十六进制公钥数组
address_type str 默认 legacy 可选 legacyp2sh-segwitbech32

返回对象含 addressredeemScript(十六进制赎回脚本)、descriptor(对应描述符)以及可选的 warnings 数组。

告警逻辑的关键在当前源码 src/rpc/output_script.cpp

UniValue warnings(UniValue::VARR);
if (descriptor->GetOutputType() != output_type.value()) {
    // 仅当用户显式选择的地址类型确实无法生成时才告警
    warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
}
PushWarnings(warnings, result);

修复前,当用户请求 p2sh-segwit 且提供的是压缩公钥时,旧逻辑在特定条件下也会错误地弹出告警(#25220 描述的 "incorrect warning for address type p2sh-segwit")。修复后的判定原则是:比较实际推断出的描述符输出类型与用户请求的输出类型,只有二者不一致(典型场景是存在未压缩公钥导致无法生成 SegWit 赎回脚本,被迫降级为 P2SH legacy)才给出提示——未压缩公钥是 SegWit 脚本被禁止的输入。此外当前实现还显式拒绝 bech32m(见 src/rpc/output_script.cpp),因为多重签名场景下不应生成 witness v1 之后的地址。

CLI 实际调用示例(来自源码内嵌 Help 示例,可 bitcoin-cli help createmultisig 查看完整原文):

bitcoin-cli createmultisig 2 "[03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd,03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626]"
# 指定地址类型:
bitcoin-cli createmultisig 2 "[<pubkey1>,<pubkey2>]" p2sh-segwit

deriveaddresses:杜绝 2^31-1 索引引发的崩溃(#26275)

deriveaddresses 依据输出描述符(descriptor)派生一个或多个地址,实现位于 src/rpc/output_script.cpp。该命令支持两种模式:

  • 普通描述符:直接派生单个地址;
  • 带范围描述符(如路径含 /*):必须提供 range 参数(单个终点值,或 [begin,end] 写法),否则报错;反过来,非范围描述符传入 range 同样报错。

#26275 修复的崩溃场景是 range 终点取到 2^31-1(2147483647):旧代码路径下,这个极大索引在派生循环内造成溢出/非法行为,deriveaddresses 直接崩溃。修复思路并非放开限制,而是在范围解析层做三重防护,见 src/rpc/util.cppParseDescriptorRange

std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
{
    int64_t low, high;
    std::tie(low, high) = ParseRange(value);
    if (low < 0) {
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
    }
    if ((high >> 31) != 0) {
        throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
    }
    if (high >= low + 1000000) {
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
    }
    return {low, high};
}

三条规则分别拦截:负起点、终点 ≥ 2^31(high >> 31 非零即命中,因此 2147483647 作为上限本身仍可接受,但再往上一档就会被拒)、跨度超过 100 万。随后实际派生循环 DeriveAddressessrc/rpc/output_script.cpp)以 int64_t 推进,配合范围上限校验,从根上消除了此前固定宽度整数循环在极端输入下的越界行为。此外,范围描述符必须以带校验和的形式解析(代码中对 Parse 传入了 require_checksum = true),描述符语法可参考 doc/descriptors.md

典型用法:

bitcoin-cli deriveaddresses "<ranged_descriptor>#checksum" "[0,100]"

其它 RPC 层面的并发与质量修复(#25237、#25983)

  • #25237(rpc: Capture UniValue by ref for rpcdoccheck):这是 RPC 文档一致性检查工具(rpcdoccheck)的内部改动,将循环中遍历的 UniValue 改为按引用捕获,避免无谓的深拷贝开销。属于检查/自检链路的代码质量修复,不改变任何 RPC 行为。
  • #25983(Prevent data race for pathHandlers):修复 HTTP JSON-RPC 服务中路径处理器注册表(pathHandlers)的并发数据竞争。此类表由 RPC 服务在启动期注册、由 HTTP 线程在请求期遍历读取,缺少同步时在多线程请求与优雅关停场景下存在竞争窗口。该修复让服务在并发与停机流程下更加稳健,属于"不改变协议语义但消除未定义行为"的一类补丁。

构建系统与发布供应链加固

23.1 的构建修复集中于可复现构建签名发布两条链上:

  • #25201(windeploy: 续期 Windows 代码签名证书):Windows 安装包与二进制需要代码签名以通过 SmartScreen 与防病毒软件信任链。23.1 使用续期后的证书重新签名。当前仓库保留的 Windows 部署工具位于 contrib/windeploy/,其中 contrib/windeploy/detached-sig-create.sh 负责离线生成分离签名、contrib/windeploy/win-codesign.cert 为签名用证书,可了解签名产物形态。
  • #25788(guix: 修补 NSIS,去除安装桩的 .reloc 段):Windows 安装包由 NSIS 生成,其安装器桩代码中带有的 .reloc 重定位段在不同构建环境下内容不稳定,会破坏 Guix 可复现构建的二进制一致性。该 PR 在 Guix 构建阶段给 NSIS 打补丁剔除这些段,使安装包哈希可复现。Guix 构建相关资产集中于 contrib/guix/,完整构建流程文档见 doc/guix.md
  • #25861(guix: 交叉工具链使用 --build={arch}-guix-linux-gnu):让 Guix 交叉工具链显式声明宿主架构三元组,消除工具链自检阶段对构建机架构的隐式假设,进一步提升交叉编译的可复现性与健壮性。
  • #25985(Revert "build: Use Homebrew's sqlite package if it is available"):撤销了 macOS 原生构建在检测到 Homebrew sqlite 时优先使用它的行为。理由是系统/第三方 sqlite 版本不可控,会引入构建结果分叉;回退后统一走 depends 自带的 sqlite 源码构建(见 depends/packages/sqlite.mk),保证原生构建与 depends/ 交叉构建链行为一致。

GUI 客户端修复

  • #24668(build, qt: Qt5 提升至 5.15.3):将各平台 GUI 构建所使用的 Qt 5 统一提升到 5.15.3。该版本处于 Qt 5 生命周期中后期、聚合了一批上游安全与稳定性修复,属于"升级构建依赖、不新增界面特性"的维护动作,影响 Windows/Linux 打包用的 Qt 及 macOS 原生 Qt 的版本基线。
  • gui#631(禁止加密 watch-only 钱包):watch-only(只含观察地址、不含任何私钥)的钱包根本没有可加密的密钥材料,"加密"只会让用户误以为私钥受到保护。修复后 GUI 直接禁用对这类钱包的加密入口。逻辑上它与钱包层的认识一致:加密的意义在于保护私钥材料,地址监视本就无密钥可守。
  • gui#680(修复 macOS 13 崩溃):macOS 13(Ventura)上,某些系统通知事件被 Qt GUI 处理后会触发段错误。该 PR 通过屏蔽特定通知处理器避免崩溃,保证新版 macOS 上图形界面稳定运行。

这三项均属 Bitcoin Core GUI 仓库(bitcoin-core/gui)的 PR,随 23.1 一并发布,GUI 相关源码可在 src/qt/ 继续深挖。

测试与杂项

  • #24454(tests: 修正外部输入权重计算):对涉及"外部输入"(非本钱包 UTXO、由调用方在 CCoinControl 中直接指定)的测试用例,修正其 effective value / 权重估算口径。外部输入没有本钱包可用的脚本信息,权重需要按输入大小推算,该 PR 让测试断言与钱包 coin selection 的真实计算保持一致。此类逻辑对应的生产代码路径位于钱包交易构造与余额评估模块,可参考 src/wallet/
  • #26321(调整 .tx/config 适配新版 Transifex CLI):翻译工作流配置随 Transifex 新命令行工具的配置格式变化而更新,属于多语言协作基础设施的例行维护。23.1 同样包含大量新翻译。

致谢与延伸阅读

23.1 的直接代码贡献者名单(据发布说明)包括:Andrew Chow、brunoerg、Hennadii Stepanov、John Moffett、MacroFake、Martin Zumsande、Michael Ford、muxator、Pavol Rusnak、Sebastian Falbesoner、W. J. van der Laan,另有通过 Transifex 参与翻译的社区贡献者。

若要继续追踪这条 23.x 修复线,可在 doc/release-notes/release-notes-23.0.md 对比父版本特性基线、在 doc/release-notes/release-notes-23.2.md 查看该系列的后续修补;本仓库同时保留了完整的发布说明归档(doc/release-notes/)与自 v0.3 时代起的全部历史文档,是研读各版本演进的一手资料。生产运维者在升级 23.1 前,建议结合本仓库的 doc/files.md 了解数据目录各文件职责,并对钱包与数据目录先行备份。

登录后查看全文
热门项目推荐
相关项目推荐