一个名字为什么要先算成哈希:ERC-137 的登记表、解析器与注册方三分工
看到一个带后缀的人名式地址,很多人以为链上真有一张“名字到地址”的表可以直接查。ERC-137 的答案不太一样:链上那张表存的不是名字,而是名字算出来的一个三十二字节编号,而且要查到最终记录,得跑两趟查询。这份规范的正文把自己叫作以太坊域名服务规范,文件头记录创建于 2016 年 4 月 4 日,仓库记录状态为 Final,也就是说它是这份体系最早定稿的那一份文本。
三层角色各自管什么
标准开篇就把系统切成三部分。第一部分是登记表,一个单独合约,只维护一件事:某个节点归谁、由哪个解析器负责、这条记录最多能被缓存多久。第二部分是解析器,负责真正做查询,可能返回合约地址、内容哈希或地址组。第三部分是注册方,负责把名字分配给用户,也是唯一能更新登记表的角色——登记表里某个节点的 owner,就是它的注册方。
这个分工里最容易被忽略的是第三条:注册规则不归这份标准管。原文明确说注册是各个注册方的责任,而注册属于治理问题,不同顶级域的规则本来就该不一样。所以“这个名字怎么买、多久续一次、过期怎么收回”都不在 ERC-137 里,读任何一份名字服务的说明时,这一层要去对应注册方的条款里找。

namehash:把点分名字压成一个节点
名字不能直接当键用,标准给它定了一套递归哈希。做法是把名字按点切成若干段,从最右边的段开始,先哈希该段,再和上一轮结果拼起来重新哈希;起点是三十二个零字节。原文给的测试向量可以直接核对:空字符串对应六十四个零,eth 对应 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae,foo.eth 对应 0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f。
两套规则值得单独记住。其一是名字里允许大小写字母,但规范化过程会先做大小写折叠再去哈希,所以拼写相同、大小写不同的两个名字算出同一个节点。其二是节点只取决于名字本身,因此可以预先算好写进合约,让查找次数和名字里段数无关。
登记表暴露的函数与两个陷阱
标准给登记表列了六个函数:owner(bytes32) 回答节点归谁,resolver(bytes32) 回答该问哪个解析器,ttl(bytes32) 返回这条记录最多能被缓存多久,另有 setOwner、setSubnodeOwner、setResolver、setTTL 四个写操作,且都只有当前 owner 有权调用。子节点由 sha3(node, label) 派生,这就是子域名可以交给别人管理的机制来源。
求解于是固定为两步:先拿节点去登记表问解析器地址,再拿同一个节点去那个解析器问具体记录。陷阱藏在第二步的返回值上。规范对地址记录写得很硬:解析器支持地址查询但某个节点没有记录时,必须返回零地址;而调用方必须检查零值,并且像对待“根本没设解析器”的名字一样拒绝交互,否则用户就可能把钱打到零地址。把“查不到”和“查到零”当成同一件事,是很多工具出错的原因。
缓存时间这个字段容易被当成次要信息
ttl 在实现里是六十四位整数,规范说它同时约束登记表里的 owner 与 resolver 两条信息,也约束关联解析器返回的内容。读一个名字时,这个值决定你看到的结论有多“新”。一个很长的 TTL 意味着即便链上记录已经改了,客户端仍可能拿着旧缓存办事——尤其是别人临时把收款地址换走又换回来的场景里,缓存窗口是真实存在的风险面。
还有一处常被误读:登记表本身不知道名字长什么样,它只认节点。也就是说单看登记表里的一条记录,你无法反推它是哪个名字,也无法验证别人给你看的名字和链上节点是不是对应关系。真正负责任的做法是在自己这一侧重算一遍哈希再比对,而不是相信别人贴过来的节点字符串。
这份标准不覆盖的边界
原文特意说明,规范部分不规定实现,附录里的合约代码只是示例;也不规定域名该怎么注册、怎么更新,以及系统怎么找到某个名字的责任人。后来的扩展以“接口标识”的形式挂在这份骨架上,正文列出了地址、名字、合约接口、公钥几类记录对应的标识,并允许其他提案继续往里加。读一个解析器时先看它申报了哪些接口,比看宣传语更能说明它能回答哪些问题。
标准还留了一个细节:解析器可以只实现记录类型的任意子集,但一个类型需要的多个函数必须要么全实现要么一个都不实现,并且必须给未定义的调用留下会直接失败的回退。这意味着“同一个解析器对不同问题给出不同态度的答案”是设计内的行为,工具如果不区分记录类型,就会把一个类型上的查不到误当成整个名字没配好。本文为机制说明,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。