Bitcoin Core 源码解读:33414 为 Tor 洋葱服务自动启用 PoW 防护
在 Bitcoin Core 中,以 -listenonion(默认开启)方式自动创建的 Tor 隐藏服务,从 PR #33414 起会尝试向 Tor 守护进程请求启用 PoW(Proof-of-Work)防护——只要 Tor 版本支持该能力。本文以 release notes 草稿 为线索,结合 Tor 控制接口实现、回复码定义 与 fuzz 测试,完整拆解这一改动的命令构造、降级回退机制与验证方式,帮助运维自托管节点的开发者理解洋葱服务的抗 DoS 能力是如何在 Bitcoin Core 与 Tor 之间协商的。
一、变更概述:这条 release note 说了什么
doc/release-notes-33414.md 是挂在下一个版本 release notes 下的待发布条目,其正文只有简短的一行(这是 Bitcoin Core release notes 分片文件的典型形态,最终会在发版时合并进 doc/release-notes 中的版本总文档):
P2P and network changes Tor hidden services that are created automatically by Bitcoin Core will have PoW defenses enabled if the Tor daemon supports that. (#33414)
翻译过来:由 Bitcoin Core 自动创建的 Tor 隐藏服务(onion service),在 Tor 守护进程支持时,将启用 PoW 防护。关键词是 "automatically created" 和 "if the Tor daemon supports that"——它限定了两个边界:
- 仅影响通过 Tor Control 接口
ADD_ONION命令动态创建的临时洋葱服务,不影响用户在torrc中手动配置的HiddenServiceDir; - 存在能力探测式的兼容处理:旧版 Tor 不认识
PoWDefensesEnabled参数时,Bitcoin Core 会自动降级重试,保证功能不因 Tor 版本过旧而中断。
二、PoW 防护在洋葱服务中的角色
先解释为什么需要这项防御。Tor 的 onion service 对外暴露一个 .onion 地址,任何客户端都可以通过 Tor 网络尝试与它建立电路并发起握手。对 P2P 节点而言,这意味着匿名网络上任意攻击者都能反复对你的节点发起连接尝试,消耗 CPU 和带宽——这是一种经典的连接型 DoS 攻击面。
PoW 防护的思路是:要求连接发起方在建立电路前完成一道轻量级的工作量证明(哈希求逆类问题),把"建立一次连接的边际成本"提高,从而显著抬高大规模连接洪水攻击的成本。这一机制由 Tor 项目实现,Bitcoin Core 侧的职责是在创建洋葱服务时把开关打开,而不是自己实现 PoW 校验。
需要区分两个层面的"PoW",避免混淆:本条变更与比特币共识中的工作量证明、与 pow.cpp 中的 CheckProofOfWork 完全无关,它只是 Tor 网络层的一个抗 DoS 开关。
三、源码拆解:命令如何构造
3.1 MakeAddOnionCmd:把开关拼进 ADD_ONION 命令
改动新增了一个静态函数 MakeAddOnionCmd,统一负责拼出发给 Tor Control 端口的 ADD_ONION 命令:
static std::string MakeAddOnionCmd(const std::string& private_key, const std::string& target, bool enable_pow)
{
// Note that the 'virtual' 端口 always 使用默认端口,避免用其他端口的节点被"脱匿"。
return strprintf("ADD_ONION %s%s Port=%i,%s",
private_key,
enable_pow ? " PoWDefensesEnabled=1" : "",
Params().GetDefaultPort(),
target);
}
三个要点:
PoWDefensesEnabled=1是 Tor Control 规范中ADD_ONION命令新增的 key=value 选项(由 Tor 在 0.4.9.2-alpha 引入该控制协议能力),Bitcoin Core 只是把开关透传给 Tor 守护进程;- 虚拟端口固定为默认 P2P 端口(主网 8333):注释明确说明这是为了避免"用非默认端口监听的节点被流量特征去匿(decloaking)"——所有自动创建的洋葱服务对外看起来端口一致;
- 该函数被抽出来正是因为同一条命令需要生成两个变体(带开关 / 不带开关),服务于后面的降级重试逻辑。
3.2 首次请求:认证通过后带 PoW 开关发起
Tor 控制连接的认证完成回调 TorController::auth_cb 中,生成或复用 ED25519-V3 私钥后,立即以 enable_pow=true 发起请求:
// 请求洋葱服务,重定向端口。
_conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/true),
this {
add_onion_cb(conn, reply, /*pow_was_enabled=*/true);
});
这里的策略是乐观启用:先假定 Tor 支持该参数,直接带 PoWDefensesEnabled=1 下发。
3.3 降级回退:收到 512 语法错误时重试
兼容性处理集中在回调 TorController::add_onion_cb。Tor Control 协议对不支持的命令参数返回 512 Syntax Error,Bitcoin Core 据此识别"Tor 版本太旧"并自动重试:
} else if (pow_was_enabled && reply.code == TOR_REPLY_SYNTAX_ERROR) {
LogDebug(BCLog::TOR, "ADD_ONION failed with PoW defenses, retrying without");
_conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/false),
this {
add_onion_cb(conn, reply, /*pow_was_enabled=*/false);
});
}
注意回退的两个限定条件:
- 仅在
pow_was_enabled为真时才可能触发重试,避免无限循环(重试后pow_was_enabled=false,若仍失败则落入最终的Add onion failed; error code %d警告分支); - 精确匹配
TOR_REPLY_SYNTAX_ERROR,而不是所有错误码——真正的失败(如 551 连接失败)不会被误判为版本问题。
对应的回复码常量定义在 src/torcontrol.h#L30-L32:
inline constexpr int TOR_REPLY_OK{250};
inline constexpr int TOR_REPLY_UNRECOGNIZED{510};
inline constexpr int TOR_REPLY_SYNTAX_ERROR{512}; //!< 命令参数语法错误
其中 TOR_REPLY_UNRECOGNIZED(510) 的既有处理逻辑会提示"You probably need to upgrade Tor";本次改动让 512 走一条静默降级路径,只在 -debug=tor 日志里留一条 ADD_ONION failed with PoW defenses, retrying without。
成功路径上,日志也区分了防护状态:ADD_ONION successful (PoW defenses enabled/disabled)(见 src/torcontrol.cpp#L518),方便运维确认本节点最终是否启用了防护。
四、适用前提与验证方法
适用前提:
- 该能力只作用于自动创建的洋葱服务,即 doc/tor.md 第 2 节描述的机制:
-listenonion默认开启(只要节点处于 listen 状态且 Tor 可用),通过-torcontrol/-torpassword(或 cookie 认证)连接 Tor Control 端口后由 Bitcoin Core 编程创建临时洋葱服务; - Tor 守护进程需支持
ADD_ONION的PoWDefensesEnabled参数(该控制协议能力自 Tor 0.4.9.2-alpha 引入);不支持的版本会走上面的降级路径,洋葱服务照常创建,只是没有 PoW 防护; - 版本、命令或配置相关的行为以当前仓库源码为准:降级重试是"语法错误才重试一次",没有可配置开关,也没有单独的 RPC 暴露 PoW 状态。
如何验证(沿用 doc/tor.md 中给出的观测手段):
- 启动 bitcoind 时加
-debug=tor,在 debug log 中 grepADD_ONION:- 看到
ADD_ONION successful (PoW defenses enabled)→ Tor 版本受支持且启用成功; - 看到
ADD_ONION failed with PoW defenses, retrying without后接... (PoW defenses disabled)→ 当前 Tor 不支持,已自动降级;
- 看到
- 用
bitcoin-cli -netinfo的 "Local addresses" 输出、getnetworkinfo的localaddresses字段确认.onion地址已正常 advertise——降级路径不应影响洋葱服务本身的可用性。
五、手动配置的洋葱服务如何获得同等防护
#33414 顺带更新了 doc/tor.md 第 3 节"手动创建 Bitcoin Core 洋葱服务",为 torrc 配置补充了 PoW 防护提示(见 doc/tor.md#L182-L187):
HiddenServiceDir /var/lib/tor/bitcoin-service/
HiddenServicePort 8333 127.0.0.1:8334
# If `tor --list-modules` shows "pow: yes", then enable PoW protection.
# It is available in tor-0.4.8.1-alpha and newer when configured with
# `./configure --enable-gpl`.
HiddenServicePoWDefensesEnabled 1
与自动创建路径的关键差异在于:手动模式下开关写在 Tor 自己的配置文件里,由 Tor 守护进程直接消费,Bitcoin Core 不介入——因此文档要求先用 tor --list-modules 检查 pow 模块是否存在(注意文档给出的版本线是 tor-0.4.8.1-alpha 且需 --enable-gpl 编译,与自动创建路径所依赖的控制协议能力 0.4.9.2-alpha 不是同一个门槛,二者不要混用)。
六、测试覆盖:fuzz 目标同步扩展
接口签名变化(add_onion_cb 新增 pow_was_enabled 参数)同步落到了 fuzz 目标 src/test/fuzz/torcontrol.cpp:
- 回复码候选集中新增
TOR_REPLY_SYNTAX_ERROR,确保"PoW 请求被拒"这一分支能被模糊输入命中; add_onion_cb的调用被拆成pow_was_enabled=true/false两个分支,覆盖降级重试触发与不触发两种路径。
这意味着该降级逻辑不是仅靠代码审查保证正确性,而是进入了持续运行的 fuzz 覆盖面。
七、小结
| 维度 | 说明 |
|---|---|
| 变更条目 | doc/release-notes-33414.md,PR #33414 |
| 生效范围 | 由 Bitcoin Core 通过 Tor Control ADD_ONION 自动创建的洋葱服务 |
| 核心实现 | MakeAddOnionCmd 拼接 PoWDefensesEnabled=1;auth_cb 乐观启用 |
| 兼容策略 | 收到 TOR_REPLY_SYNTAX_ERROR(512) 自动降级重试一次(add_onion_cb) |
| 手动服务 | 在 torrc 中自行加 HiddenServicePoWDefensesEnabled 1(doc/tor.md#L182-L187) |
| 观测手段 | -debug=tor 日志中的 ADD_ONION successful (PoW defenses enabled/disabled) |
| 测试 | src/test/fuzz/torcontrol.cpp fuzz 目标覆盖降级分支 |
对运营 Tor 全节点的用户而言,这是一条"零配置生效"的防护增强:升级 Bitcoin Core 与 Tor 后,自动创建的洋葱服务即获得连接级 DoS 抗扰能力;旧版 Tor 上节点行为与之前完全一致,只是多一条 debug 级日志。理解这条调用链(auth_cb → ADD_ONION 带开关 → add_onion_cb 按 512 降级)之后,也可以据此判断其他 Tor 控制命令的兼容性处理是否遵循了同样的"乐观启用 + 精确错误码回退"模式。
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 StartedRust0623
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