如何使用 go-ethereum 的 devp2p 命令创建、签名并发布 DNS 发现节点树
【免费下载链接】go-ethereumGo implementation of the Ethereum protocol项目地址: https://gitcode.com/gh_mirrors/go/go-ethereum
这篇文章解决一个具体任务:用 go-ethereum 的devp2p命令行工具,把一个节点集合做成 DNS 发现树(enrtree)——在本地目录中组装节点、用密钥对树根签名,再把整棵树的 DNS TXT 记录发布到 CloudFlare 或 Amazon Route53,最后确认树可以被客户端同步。
devp2p是 go-ethereum 中用于底层 P2P 调试与协议开发的工具(见 cmd/devp2p/README.md 的 "Maintaining DNS Discovery Node Lists" 一节)。DNS 子命令的完整定义在 cmd/devp2p/dnscmd.go,两个云发布器的实现在 cmd/devp2p/dns_cloudflare.go 和 cmd/devp2p/dns_route53.go。
前置条件:你需要什么
- 一个编译好的
devp2p可执行文件(来自cmd/devp2p,本文所有命令都以devp2p指代它)。 - 一个你自己的域名,例如
nodes.example.com(下文的域名都是占位示例,替换成你实际拥有、且 DNS 服务商可控的域名),用于承载这棵树的 TXT 记录。 - 发布渠道二选一:CloudFlare API token,或 Route53 的 AWS 访问密钥(Access Key ID / Secret)。
第一步:生成签名密钥
dns sign使用的签名密钥是 Ethereum keystore 格式的私钥文件。devp2p自带生成命令(见 cmd/devp2p/keycmd.go):
devp2p key generate mytree.key这会按 keystore 格式把新私钥写入mynode.key风格的密钥文件(参数是文件名,这里用mytree.key)。后续每次签名时工具都会交互式提示输入该文件的密码(源码中通过Please enter the password for '<keyfile>':提示读取),所以要妥善保管这个文件。
如果你希望树的初始节点里包含一个可识别的节点记录,可以用同一个密钥生成enode://或 ENR:
devp2p key to-enode mytree.key -ip 203.0.113.10 -tcp 30303-ip(默认127.0.0.1)、-tcp和-udp(默认都是 30303)是文档给出的参数。对 DNS 发现树而言,IP 与端口并不会被发现客户端使用,它们只是 ENR 记录里的字段;你可以用这条命令生成一条记录放进节点集合,也可以跳过它。
第二步:搭建树目录
DNS 发现树在磁盘上是一个目录,里面两个文件(格式说明见 cmd/devp2p/dnscmd.go 的注释和treeDefinitionFiles函数):
mytree/ enrtree-info.json # 树的元数据:url、seq、signature、links nodes.json # 节点集合nodes.json是一个以节点 ID 为键的 JSON 对象(结构定义在 cmd/devp2p/nodeset.go):
{ "<64位十六进制节点ID>": { "seq": <该 ENR 自身的 seq>, "record": "enr:..." } }record字段是签名的 ENR 字符串(enr:开头),seq是该条 ENR 的 seq——注意它与树元数据enrtree-info.json里的seq不是一回事。节点集合可以由人工编写,也可以由 DHT 爬虫(devp2p discv4 crawl/devp2p discv5 crawl)生成;用devp2p key to-enr的输出做record值是最直接的手工来源。
enrtree-info.json的最小形态只需一个字段:
{ "seq": 0 }签名命令会补全url、signature和lastModified,并在目录不存在时自动创建它(writeTreeMetadata会执行os.Mkdir)。
树的结构由 p2p/dnsdisc/tree.go 决定:每个子项是父项内容的 Keccak256 哈希前 16 字节的 base32 编码子域名,一个分支最多 13 个子项(maxChildren),目的是把单条 DNS TXT 响应控制在 UDP 512 字节以内。你不需要手工管理这些子域名,MakeTree会构建整棵树。
第三步:签名树
签名命令把树目录读进来,用密钥对树根签名,并把签名后的元数据写回同一个目录:
devp2p dns sign mytree mytree.key参数是<tree-directory> <key-file>。签名执行时会提示输入mytree.key的密码;成功后mytree/enrtree-info.json会被更新,其中url形如enrtree://<公钥base32>@nodes.example.com(由域名 + 公钥构成,见Sign与newLinkEntry)。
两个可选参数(定义在 cmd/devp2p/dnscmd.go):
-domain <域名>:覆盖树的域名。缺省时,域名取目录名(如上面的mytree),如果enrtree-info.json里已有url字段,则取url中的域名。想让树发布在nodes.example.com上,最简单的方式就是把树目录命名为nodes.example.com。-seq <N>:指定本次的序列号。不提供时,seq自动在旧值基础上加 1(源码注释:Auto-bump sequence number if not supplied via flag)。
第四步:发布 TXT 记录
发布前有一个硬前提:树必须已签名。两个云发布器都会先做这个检查,未签名时会报missing signature, run 'devp2p dns sign' first,签名无效时报invalid signature on tree, run 'devp2p dns sign' to update it。
发布到 CloudFlare
devp2p dns to-cloudflare nodes.example.com -token <你的CloudFlare API token>说明:
-token必填(也可用环境变量CLOUDFLARE_API_TOKEN),缺少时命令直接报错退出。- token 需要对目标 zone 拥有
#zone:read、#zone:edit、#dns_records:read、#dns_records:edit四项权限,否则报wrong permissions on zone <id>: ...。 -zoneid可选;不提供时工具自行按域名查找 zone ID。- 副作用:发布会更新子域名下所有TXT 记录,并把不在新树中的既有记录删除(源码注释原话:All existing records not in the new map will be nuked!)。请确保该子域名只用于这棵 enrtree。
- TTL:根记录 30 分钟,节点记录 1 天(Cloudflare 允许的最大 TTL,见
rootTTL与treeNodeTTLCloudflare常量)。 - 成功时日志依次输出
Retrieving existing TXT records on <域名>、Updating DNS entries(附new/updated/untouched计数)、Deleted stale DNS entries。
发布到 Amazon Route53
devp2p dns to-route53 nodes.example.com \ -access-key-id <AWS Access Key ID> \ -access-key-secret <AWS Secret Access Key>-access-key-id/-access-key-secret必填(也可用环境变量AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)。-zone-id可选,缺省时按域名搜索所属 hosted zone。-aws-region默认是eu-central-1,按需替换。- 提交会按 Route53 变更集限制(RDATA 32000 字节、1000 项,UPSERT 计双倍)自动分批,然后轮询等待每个变更集进入
INSYNC状态(每次等待间隔 30 秒)。 - 副作用与 CloudFlare 相同:只保留本次树中的 TXT 记录,其余删除。
备选:只导出 TXT 记录
不想直接用 API 发布时,to-txt会生成全部 DNS TXT 记录的 JSON(键为记录名,值为记录内容),供你贴到任意 DNS 管理工具里:
devp2p dns to-txt nodes.example.com records.json省略第二个参数时输出到 stdout(-)。注意它只导出、不发布,TTL 需要你在自己的 DNS 服务商侧自行配置。
另外 Route53 还有一个清理命令,用于删除某域名下的整棵树的 TXT 记录(会真正删除线上记录,执行前确认域名写对了):
devp2p dns nuke-route53 nodes.example.com -access-key-id <key> -access-key-secret <secret>结果验证与排查
端到端验证:发布完成后,用一个新目录从线上拉取整棵树:
devp2p dns sync enrtree://<公钥base32>@nodes.example.com mytree-sync<公钥base32>@域名直接取自签名后mytree/enrtree-info.json里的url字段。sync会下载完整树并写出mytree-sync/enrtree-info.json与mytree-sync/nodes.json;目录中enrtree-info.json含有url、seq与signature字段,说明 DNS 上的记录完整且签名校验通过。可选参数-timeout用于调整 DNS 查询超时。
本地检查:签名后打开树目录下的enrtree-info.json,确认url指向你的域名且signature非空。若直接跳到发布,会看到前文提到的 missing/invalid signature 报错——按提示先运行dns sign即可。
发布失败时:CloudFlare 侧先看 zone 权限(wrong permissions on zone)和域名与 zone 是否匹配(CloudFlare zone name ... does not match name ...);Route53 侧看是否能找到 zone(can't find zone ID for <域名>)以及 AWS 凭据是否正确。
收尾
树上线后的日常流程就是:修改nodes.json(增删节点),重新devp2p dns sign(不指定-seq时序列号自动递增),再执行to-cloudflare/to-route53发布。节点记录与根记录的 TTL 是固定的(本地格式下分别为 4 周与 30 分钟,treeNodeTTL/rootTTL),CloudFlare 发布时节点记录 TTL 为 1 天——这是发布器写死的值,不可通过参数修改。命令的更多细节可查 cmd/devp2p/dnscmd.go 与 p2p/dnsdisc/tree.go 中的实现和注释。
【免费下载链接】go-ethereumGo implementation of the Ethereum protocol项目地址: https://gitcode.com/gh_mirrors/go/go-ethereum
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考