EIP-1459 详解:基于 DNS 的以太坊节点发现(enrtree 方案)
【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs
导读
EIP-1459(Node Discovery via DNS)定义了如何通过 DNS 分发、验证与更新以太坊节点列表(node list)的标准化方案。该方案在以太坊 P2P 网络中充当客户端内置 bootstrap 节点列表的替代品:使用enrtreeURL 引用节点列表、以 Merkle 树组织 DNS TXT 记录、并用 secp256k1 签名保证列表真实性与完整性。读完本文,你将掌握enrtree-root/enrtree-branch/enrtree/enr四类 TXT 记录的格式与含义、DNS zone 文件的实际写法、客户端解析与验证的完整流程,以及该设计背后的安全权衡。
背景与动机:为什么要用 DNS 分发节点列表
大多数以太坊客户端内部都硬编码了一批 bootstrap 节点列表,作为新节点加入网络的初始入口。EIP-1459 的作者指出这一做法存在三个痛点:
- 更新需要发版:列表一旦变更,必须通过客户端软件升级才能生效,节奏慢、成本高;
- 列表规模过小:现有硬编码列表只包含少量节点,新节点几乎没有选择初始入口的余地;
- 缺乏弹性:无法支撑包含数百个节点、且可定期维护更新的大型列表。
EIP-1459 提出的方案用 DNS 节点列表替代客户端 bootstrap 列表,具备等价的安全性,同时带来额外收益:通过遍历节点发现 DHT 而构建的大规模列表,可以作为因受限网络策略而无法加入 DHT 的节点的回退选项;DNS 列表对以太坊 peering 服务提供商也有价值——他们的客户可以直接把客户端配置成使用该服务商的列表。
总体设计:认证、可更新的 DNS 节点列表
节点列表与enrtreeURL
一个"节点列表"是由任意数量的"节点记录"(node record,定义于 EIP-778)构成的列表。列表之间可以通过链接(link)互相引用,整个列表使用一把 secp256k1 私钥签名,客户端必须持有对应的公钥才能校验列表。
客户端通过带enrtreescheme 的 URL 引用某个 DNS 节点列表,URL 中包含:
- DNS 名称(该列表所在域名);
- 签署列表的公钥,公钥放在 URL 的 username 部分,是压缩后的 32 字节二进制公钥的 base32 编码(RFC-4648)。
官方示例:
enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@nodes.example.org该 URL 指向 DNS 名称nodes.example.org下的节点列表,由公钥
0x049f88229042fef9200246f49f94d9b77c4e954721442714e85850cb6d9e5daf2d880ea0e53cb3ac1a75f9923c2726a4f941f7d326781baa6380754a360de5c2b6签名。
与 EIP-778 ENR 的关系
列表中的每个节点都是一条 EIP-778 定义的以太坊节点记录(ENR)。ENR 是一种开放的 p2p 连接信息格式,其内容为[signature, seq, k, v, ...]形式的 RLP 列表,键值对包括id(身份方案名,如v4)、secp256k1(33 字节压缩公钥)、ip、tcp、udp、ip6、tcp6、udp6等(详见 EIP-778 的键表)。ENR 的文本编码是 RLP 表示的 URL-safe base64 编码并加enr:前缀,最大编码大小 300 字节——EIP-1459 中 DNS TXT 记录里出现的enr:叶子条目正是这一文本编码。值得注意的是,EIP-778 的作者 Felix Lange 同时也是 EIP-1459 的作者之一,两份规范在设计上是一脉相承的。
DNS 记录结构:把节点列表编码为 Merkle 树
列表中的节点被编码为一棵 Merkle 树,通过 DNS 协议分发。树的条目存放在 DNS TXT 记录中,任意条目的子域名是"其文本内容的(缩写)keccak256 哈希"的 base32 编码——这正是 Merkle 树"子节点哈希即父节点指针"的体现:只有内容与哈希对得上,条目才被认可。
根记录enrtree-root
树的根是一条内容如下的 TXT 记录:
enrtree-root:v1 e=<enr-root> l=<link-root> seq=<sequence-number> sig=<signature>各字段含义:
| 字段 | 说明 |
|---|---|
enr-root | 包含节点(ENR)的子树的根哈希 |
link-root | 包含链接(link)子树的根哈希 |
sequence-number | 树的更新序号,十进制整数 |
signature | 对记录内容(不含sig=部分)的 keccak256 哈希所做的 65 字节 secp256k1 EC 签名,采用 URL-safe base64(RFC-4648)编码 |
三种树条目类型
子域名上的其余 TXT 记录把哈希映射到以下三种条目类型之一:
enrtree-branch:<h₁>,<h₂>,...,<hₙ>:中间树条目,包含若干子树条目的哈希列表;enrtree://<key>@<fqdn>:叶子条目,指向位于另一个完全限定域名(FQDN)上的不同列表。注意此格式与 URL 编码一致,只能出现在link-root指向的子树上;enr:<node-record>:叶子条目,包含一条节点记录,记录以 URL-safe base64 字符串编码。注意此格式与 ENR 的规范文本编码一致,只能出现在enr-root子树中。
树没有规定特定的排列顺序或结构;每当树更新时,其序列号应递增。任何 TXT 记录的内容都应足够小,以适配 UDP DNS 包 512 字节的限制——这同时限制了单个enrtree-branch条目能容纳的哈希数量。
完整的 zone 文件示例
EIP-1459 给出了标准 zone 文件格式的完整示例:
; name ttl class type content @ 60 IN TXT enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA C7HRFPF3BLGF3YR4DY5KX3SMBE 86900 IN TXT enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org JWXYDBPXYWG6FX3GMDIBFA6CJ4 86900 IN TXT enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24 2XS2367YHAXJFGLZHVAWLQD4ZY 86900 IN TXT enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA H4FHT4B454P6UXFD7JCYQ5PWDY 86900 IN TXT enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI MHTDO6TMUBRIA2XWG5LUDACK24 86900 IN TXT enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o逐行解读:
- 根记录
@(即域名本身)TTL 仅为 60 秒,便于列表快速更新;它的sig覆盖除sig=外的整行内容; C7HRFPF3BLGF3YR4DY5KX3SMBE是link-root的根哈希,其内容是一条指向morenodes.example.org列表的enrtree://链接叶子;JWXYDBPXYWG6FX3GMDIBFA6CJ4是enr-root的根哈希,内容是一个含 3 个哈希的enrtree-branch分支条目;- 三个
enr:叶子条目分别存放在各自的哈希子域名下,内容为 EIP-778 的 URL-safe base64 ENR 文本编码,gmlkgnY0iXNlY3AyNTZrMaE前缀对应id=v4与secp256k1键值对。
客户端协议:如何解析并验证列表
以查找域名mynodes.org下的节点为例,客户端按以下步骤执行:
- 解析根记录:查询该名称的 TXT 记录,检查是否包含合法的
enrtree-root=v1条目(假设其中enr-root哈希为CFZUWDU7JNQR4VTCZVOJZ5ROV4); - 验证根签名:用已知公钥验证根上的签名,并检查序列号是否大于或等于该名称此前见过的任何序列号;
- 按哈希解析子条目:解析哈希子域名的 TXT 记录(如
CFZUWDU7JNQR4VTCZVOJZ5ROV4.mynodes.org),并验证内容与哈希匹配; - 按条目类型分派:
- 遇到
enrtree-branch:解析其中的哈希列表,回到第 3 步继续递归解析; - 遇到
enr:解码、验证节点记录,导入本地节点存储。
- 遇到
遍历过程中,客户端必须跟踪已经解析过的哈希与域名,以避免陷入无限循环;出于健壮性考虑,客户端最好以随机顺序遍历树。
按需拉取是推荐的实现方式:客户端实现不应在正常运行期间一次性下载整棵树,更好的做法是在需要寻找 peer 的时候才按需通过 DNS 请求对应条目。
设计理由:为什么是 DNS + Merkle 树 + 链接子树
为什么选 DNS
- DNS 几乎永远可用,即使在受限网络环境下也能工作;
- 查询延迟低,且中间解析器可以缓存 DNS 响应;
- 无需自建服务器软件,节点列表可以部署到任意 DNS 服务商(如 CloudFlare DNS、dnsimple、Amazon Route 53),使用它们各自的客户端库即可。
为什么用 Merkle 树
- 整棵节点列表只需对根做一次签名即可完成认证(单个签名认证任意大小的列表);
- 哈希子域名保护列表完整性——中间解析器最坏只能阻断访问或阻止更新,无法篡改内容;
- 序列号防止根被替换为旧版本(防回滚);
- 客户端可以增量同步更新,这对大列表很重要;
- 单个条目足够小,能放进单个 UDP 包,兼容只支持基础 UDP DNS 的环境;
- 与缓存解析器配合良好:只有根需要短 TTL,中间条目与叶子可以缓存数天。
为什么存在链接子树
链接让列表之间具备**联邦(federation)与信任网络(web-of-trust)**能力:大型列表的运营者可以把维护工作委托给其他列表提供方;如果两个节点列表互相链接,用户使用其中任何一个列表都能同时获得两个列表中的节点。
链接子树与包含 ENR 的树相互独立,目的是让客户端实现可以独立同步这两棵树:希望获得尽可能多节点的客户端会先同步链接树,把所有被链接的名称加入同步视野(sync horizon)。
安全考量
EIP-1459 明确承认:基于 DNS 的发现不如基于 DHT 的发现安全,因为它依赖受信任方定期发布记录。该发布方可以轻易地只发布自己控制的节点记录,从而对 bootstrap 节点实施 eclipse 攻击(隔离/垄断节点对网络的视野)。因此,在使用 DNS 节点列表时,客户端应谨慎选择信任的列表发布方,并把 DNS 发现定位为 DHT 的回退补充,而非唯一入口。
小结
EIP-1459 通过enrtreeURL、Merkle 树化的 TXT 记录与 secp256k1 根签名,为以太坊客户端提供了一条"无需发版即可更新、规模可达数百节点、可联邦可缓存"的节点发现通道。其核心组件——ENR 叶子条目直接复用 EIP-778 的节点记录格式,形成了从节点记录到 DNS 分发再到客户端验证的完整闭环。对于希望自建节点列表或理解以太坊网络引导机制的开发者,掌握根记录、分支条目、链接子树与客户端四步验证流程,是落地实现与排障的基础。
【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考