首页
/ Bitcoin Core 的 CJDNS 支持:加密 IPv6 网络配置、参数详解与源码实现

Bitcoin Core 的 CJDNS 支持:加密 IPv6 网络配置、参数详解与源码实现

2026-09-04 09:29:08作者:邓越浪Henry

本文为面向 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_netssrc/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 地址只有在其前缀字节为 0xFCHasCJDNSPrefix(),定义于 src/netaddress.h)且 CJDNS 被列入可达网络时,才会被当作 CJDNS 地址处理。该函数被大量调用于网络层:ConnectNode 的连接目标解析(src/net.cppsrc/net.cpp)、监听 socket 绑定地址的判断(src/net.cpp)、-addnode 参数解析(src/net.cpp),以及 RPC 层的地址处理(src/rpc/net.cppsrc/rpc/net.cpp)。测试用例 src/test/net_tests.cpp 中也有针对 CJDNS 地址的断言验证。

网络枚举本身在 src/netbase.cpp 中定义:ParseNetwork 把字符串 "cjdns" 解析为 NET_CJDNSGetNetworkName 反向映射为 "cjdns",与 ipv4ipv6onioni2p 并列。

附加配置选项:-onlynet=cjdns

-onlynet=cjdns

该选项使自动出站连接只建立到 CJDNS 地址。入站连接与手动连接(如 -addnode)不受此选项影响。它可以指定多次以允许多种网络并存,例如:

onlynet=cjdns
onlynet=i2p
onlynet=onion

网络参数的解析入口在 src/netbase.cppParseNetwork 函数中,支持 ipv4ipv6onioni2pcjdns 五种取值,其余值会被判为 NET_UNROUTABLE 并在启动时报错(见上文 src/init.cpp)。

官方文档中给出了明确的安全警告:CJDNS 支持自 Bitcoin Core 23.0 引入,网络中的 CJDNS 对等节点数量可能少于 Tor 或 IP 节点,因此不建议单独使用 CJDNS 而不叠加其他网络。原因有二:

  1. 节点可能无法填满出站连接槽位,只能反复尝试其已知的少量地址;
  2. 节点更容易受到 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 是"本机可信网络"的常规用法;一旦启用 -cjdnsreachablefc00::/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
  • getnetworkinfo RPC 输出的 localaddresses 字段。

查看当前连接的 CJDNS 对等节点

  • bitcoin-cli -netinfo 4(按网络类型过滤的节点列表);
  • getpeerinfo RPC,即 bitcoin-cli getpeerinfo

获取节点已知的 CJDNS 对等地址

  • 使用 getnodeaddresses RPC 拉取节点已知的若干 CJDNS 地址,具体用法见 bitcoin-cli help getnodeaddresses

文档中还提到:上述命令中的 bitcoin-cli 均可替换为 bitcoin rpc 子命令形式使用。这些命令的输出可用于验证 -cjdnsreachable 是否生效:若本地地址中出现 fc00::/8 段地址且 -netinfo 4 能列出 CJDNS 对等节点,则说明节点已在 CJDNS 网络上正常收发。

小结

Bitcoin Core 的 CJDNS 支持由文档 doc/cjdns.md 与源码共同构成一条清晰的配置链路:

  1. 在操作系统层面安装并接入 CJDNS(cjdnstool peers show 确认 ESTABLISHED 状态);
  2. 在 Bitcoin Core 中启用 -cjdnsreachable,让节点将 fc00::/8 入站连接识别为 CJDNS 流量、并愿意广播自己的 CJDNS 地址;
  3. 视需要叠加 -onlynet=cjdns(注意启动时会强制校验与 -cjdnsreachable 的一致性),不建议单独依赖 CJDNS;
  4. 通过 -netinfogetnetworkinfogetpeerinfogetnodeaddresses 等 CLI/RPC 接口验证地址与连接。

从源码结构看,整条链路的枢纽是 g_reachable_nets 集合与 MaybeFlipIPv6toCJDNS 转换函数:前者在启动时依据 -cjdnsreachable-onlynet 计算(src/init.cpp),后者在连接、绑定、RPC 地址解析等各处被调用,将 0xFC 前缀的 IPv6 地址归一到 NET_CJDNS 网络(src/netbase.cpp)。理解这两处实现,即可准确预测各类 CJDNS 配置组合下节点的实际行为。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
903
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341