Bitcoin Core 23.1 版本解析:升级指南、P2P 自我广播修复与 RPC/构建/GUI 维护更新全景
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 属于同主版本线内的就地升级,发布说明给出的标准动作是:
- 彻底关闭旧版本:先停止运行中的
bitcoind/ Bitcoin-Qt,等待进程完全退出。某些情况下(例如正在冲刷 UTXO 缓存、flush 数据目录)完全退出可能需要几分钟,不能中途强制结束。 - 覆盖安装新二进制:
- 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)承载,这点可通过同一文件里的 IsAddrCompatible 与 PushAddress(src/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 |
可选 legacy、p2sh-segwit、bech32 |
返回对象含 address、redeemScript(十六进制赎回脚本)、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.cpp 的 ParseDescriptorRange:
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 万。随后实际派生循环 DeriveAddresses(src/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 了解数据目录各文件职责,并对钱包与数据目录先行备份。
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 StartedRust0627
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