Bitcoin Core 的 CJDNS 支持:加密 IPv6 网络配置、参数详解与源码实现
本文为面向 Bitcoin Core 节点运维者和开发者的 CJDNS 接入指南。CJDNS 是一个基于公钥加密地址分配与分布式哈希表路由的加密 IPv6 网络,Bitcoin Core 通过少量配置项即可让节点接入该网络。读完本文,你将了解 CJDNS 的定位与特性、如何在 Bitcoin Core 中启用 -cjdnsreachable、-onlynet=cjdns 等选项,以及这些选项在源码中如何影响地址管理、网络可达性判断和 RPC 行为。
什么是 CJDNS
CJDNS 可以类比为一种去中心化、共享的 VPN,具有多个入口点,任意参与者之间都可以相互到达。所有参与者都使用 fc00::/8(RFC4193 保留的 IPv6 私有地址段)内的地址。其 CJDNS 地址的前缀字节固定为 0xFC,这一点在 src/netaddress.h 中有明确定义:
/// All CJDNS addresses start with 0xFC.
inline constexpr uint8_t CJDNS_PREFIX{0xFC};
与 IPv4/IPv6 相比,CJDNS 提供端到端加密,可以保护节点免受流量分析和过滤攻击。CJDNS 的安装与配置在 Bitcoin Core 之外完成,方式与配置 VPN 类似(在宿主机/操作系统层或网络路由器上完成),Bitcoin Core 本身只负责感知并连接。
与 Tor、I2P 配合使用时,CJDNS 是一个互补选项,可以从以下两个层面增强健壮性:
- 对比特币网络整体:多一条独立的通信路径;
- 对单个节点:某条网络通道出现问题时可以退避到另一条。
三种网络各有所长,特性并不相同:
| 网络 | 特性 |
|---|---|
| Tor | 使用广泛,但相对中心化 |
| I2P | 连接带有源地址信息,速度较慢 |
| CJDNS | 速度快,但无法向中间路由隐藏发送方和接收方 |
安装 CJDNS
CJDNS 的安装与配置完全独立于 Bitcoin Core,需按照 CJDNS 上游项目(cjdelisle/cjdns)的安装文档完成。官方文档 doc/cjdns.md 中明确说明:安装和配置在 Bitcoin Core 之外进行,类似于 VPN 部署。
安装完成后,可以先验证节点是否已经连上 CJDNS 网络。自 CJDNS v22 起,节点会通过 DNS seeding 自动发现并连接对等节点:
cjdnstool peers show
若能看到状态为 ESTABLISHED 的对等节点,说明节点已接入 CJDNS,无需再做任何手动组网。手动组网(peering)只在两种场景下有参考价值:
- 希望保证与某个特定节点建立连接;
- 出于隐私原因禁用了 DNS seeding。
在 Bitcoin Core 中启用 CJDNS:-cjdnsreachable
当节点已经接入 CJDNS 网络后,Bitcoin Core 只需启用一个配置选项即可让 CJDNS 对等节点自动可达:
-cjdnsreachable
该选项在 src/init.cpp 中的注册定义如下:
argsman.AddArg("-cjdnsreachable", "If set, then this host is configured for CJDNS (connecting to fc00::/8 addresses would lead us to the CJDNS network, see doc/cjdns.md) (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
启用后,该选项告诉 Bitcoin Core:本机运行在一个"连接 fc00::/8 地址会到达 CJDNS 网络(而不是某个 RFC4193 IPv6 私有网络)"的环境中。这有助于节点进行更好的地址管理,具体体现在两个方面:
- 节点可以把来自
fc00::/8的入站连接视为来自 CJDNS 网络,而不是 IPv6 私有网络; - 如果节点自身的某个本地地址属于
fc00::/8,节点可以主动选择将该地址广播(gossip)给对等节点。
源码中"可达网络"的判定逻辑
-cjdnsreachable 的核心作用是维护一个全局的"可达网络"集合 g_reachable_nets。src/init.cpp 中的初始化逻辑可以精确说明其语义:
// Configure reachable networks before we start the RPC server.
// This is necessary for -rpcallowip to distinguish CJDNS from other RFC4193
const auto onlynets = args.GetArgs("-onlynet");
if (!onlynets.empty()) {
g_reachable_nets.RemoveAll();
for (const std::string& snet : onlynets) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
g_reachable_nets.Add(net);
}
}
if (!args.IsArgSet("-cjdnsreachable")) {
if (!onlynets.empty() && g_reachable_nets.Contains(NET_CJDNS)) {
return InitError(
_("Outbound connections restricted to CJDNS (-onlynet=cjdns) but "
"-cjdnsreachable is not provided"));
}
g_reachable_nets.Remove(NET_CJDNS);
}
// Now g_reachable_nets.Contains(NET_CJDNS) is true if:
// 1. -cjdnsreachable is given and
// 2.1. -onlynet is not given or
// 2.2. -onlynet=cjdns is given
从源码结构看,g_reachable_nets.Contains(NET_CJDNS) 为真的条件恰好是两条规则的组合:给出了 -cjdnsreachable,并且要么没有给 -onlynet,要么 -onlynet 中包含 cjdns。另外注意启动时的强约束:如果使用了 -onlynet=cjdns 但没有提供 -cjdnsreachable,节点会直接以 InitError 启动失败——这是一个容易踩到的配置陷阱。
g_reachable_nets 还决定了地址如何被"翻转"识别为 CJDNS 地址。src/netbase.cpp 中的实现如下:
CService MaybeFlipIPv6toCJDNS(const CService& service)
{
...
if (ret.IsIPv6() && ret.HasCJDNSPrefix() && g_reachable_nets.Contains(NET_CJDNS)) {
// 将 fc00::/8 的 IPv6 服务地址识别/转换为 CJDNS 地址
}
...
}
即:一个 IPv6 地址只有在其前缀字节为 0xFC(HasCJDNSPrefix(),定义于 src/netaddress.h)且 CJDNS 被列入可达网络时,才会被当作 CJDNS 地址处理。该函数被大量调用于网络层:ConnectNode 的连接目标解析(src/net.cpp、src/net.cpp)、监听 socket 绑定地址的判断(src/net.cpp)、-addnode 参数解析(src/net.cpp),以及 RPC 层的地址处理(src/rpc/net.cpp、src/rpc/net.cpp)。测试用例 src/test/net_tests.cpp 中也有针对 CJDNS 地址的断言验证。
网络枚举本身在 src/netbase.cpp 中定义:ParseNetwork 把字符串 "cjdns" 解析为 NET_CJDNS,GetNetworkName 反向映射为 "cjdns",与 ipv4、ipv6、onion、i2p 并列。
附加配置选项:-onlynet=cjdns
-onlynet=cjdns
该选项使自动出站连接只建立到 CJDNS 地址。入站连接与手动连接(如 -addnode)不受此选项影响。它可以指定多次以允许多种网络并存,例如:
onlynet=cjdns
onlynet=i2p
onlynet=onion
网络参数的解析入口在 src/netbase.cpp 的 ParseNetwork 函数中,支持 ipv4、ipv6、onion、i2p、cjdns 五种取值,其余值会被判为 NET_UNROUTABLE 并在启动时报错(见上文 src/init.cpp)。
官方文档中给出了明确的安全警告:CJDNS 支持自 Bitcoin Core 23.0 引入,网络中的 CJDNS 对等节点数量可能少于 Tor 或 IP 节点,因此不建议单独使用 CJDNS 而不叠加其他网络。原因有二:
- 节点可能无法填满出站连接槽位,只能反复尝试其已知的少量地址;
- 节点更容易受到 Sybil 攻击(女巫攻击)——单一小型网络中的身份伪造者占比更高。
可以用 bitcoin-cli -addrinfo 查看节点当前已知 CJDNS 地址的数量,评估当前网络规模是否满足安全冗余要求。
一般而言,一个节点可以同时启用 onion service 与 CJDNS(甚至 IPv4/IPv6/onion/I2P/CJDNS 全部启用),从而在某条网络出现问题时获得潜在的退避通道,多网络组合配置的细节参见 doc/tor.md。
相关配置:-rpcallowip 与 CJDNS 的交互
一个容易忽视的关联点是 -rpcallowip。其帮助文本(src/init.cpp)明确写道:
RFC4193 is allowed only if -cjdnsreachable=0.
也就是说,只有当 -cjdnsreachable 未启用时,才允许用 RFC4193 网段来放行 RPC 连接——因为此时 fc00::/8 被视为普通 IPv6 私有段,用其放行 RPC 是"本机可信网络"的常规用法;一旦启用 -cjdnsreachable,fc00::/8 即代表公网可达的 CJDNS 网络,放行该段到 RPC 等同于向 CJDNS 网络开放 RPC,节点将拒绝此类配置(对应的错误提示亦出现在 src/httpserver.cpp)。同理,-proxy 也支持按网络分别指定 CJDNS 代理(-proxy=cjdns=...,处理逻辑见 src/init.cpp)。
在 Bitcoin Core 中查看 CJDNS 相关信息
Bitcoin Core 提供了多种途径来查看 CJDNS 地址与连接状态:
查看本机的 CJDNS 地址(两种等价途径):
- CLI 的
-netinfo输出中的 "Local addresses" 部分,即bitcoin-cli -netinfo; getnetworkinfoRPC 输出的localaddresses字段。
查看当前连接的 CJDNS 对等节点:
bitcoin-cli -netinfo 4(按网络类型过滤的节点列表);getpeerinfoRPC,即bitcoin-cli getpeerinfo。
获取节点已知的 CJDNS 对等地址:
- 使用
getnodeaddressesRPC 拉取节点已知的若干 CJDNS 地址,具体用法见bitcoin-cli help getnodeaddresses。
文档中还提到:上述命令中的 bitcoin-cli 均可替换为 bitcoin rpc 子命令形式使用。这些命令的输出可用于验证 -cjdnsreachable 是否生效:若本地地址中出现 fc00::/8 段地址且 -netinfo 4 能列出 CJDNS 对等节点,则说明节点已在 CJDNS 网络上正常收发。
小结
Bitcoin Core 的 CJDNS 支持由文档 doc/cjdns.md 与源码共同构成一条清晰的配置链路:
- 在操作系统层面安装并接入 CJDNS(
cjdnstool peers show确认ESTABLISHED状态); - 在 Bitcoin Core 中启用
-cjdnsreachable,让节点将fc00::/8入站连接识别为 CJDNS 流量、并愿意广播自己的 CJDNS 地址; - 视需要叠加
-onlynet=cjdns(注意启动时会强制校验与-cjdnsreachable的一致性),不建议单独依赖 CJDNS; - 通过
-netinfo、getnetworkinfo、getpeerinfo、getnodeaddresses等 CLI/RPC 接口验证地址与连接。
从源码结构看,整条链路的枢纽是 g_reachable_nets 集合与 MaybeFlipIPv6toCJDNS 转换函数:前者在启动时依据 -cjdnsreachable 与 -onlynet 计算(src/init.cpp),后者在连接、绑定、RPC 地址解析等各处被调用,将 0xFC 前缀的 IPv6 地址归一到 NET_CJDNS 网络(src/netbase.cpp)。理解这两处实现,即可准确预测各类 CJDNS 配置组合下节点的实际行为。
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 StartedRust0622
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