以 ENS 为例深度分析 Web3 域名系统的技术设计

编排:@Coucou @满意my

注:文章仅代表作者个人观点,不构成任何投资建议。

以ENS为例剖析web3域名系统设计

注册地址:https://app.ens.domains/

web3域名系统,简而言之就是基于区块链的分布式、去中心化的命名系统,与DNS(互联网名称服务)类似,将地址(钱包地址或智能合约地址)解析成可读性的名称,本文以ENS为例从整体架构到合约细节,深度剖析web3域名系统的设计

  • ENS前置概念

  • ENS模块概念

  • ENS模块解析

  • ENS存在的问题与解决

  • ENS的DNS模块

  • 部署自己的web3域名

  • 总结

ENS前置概念

  • 域名层级:类似DNS,ENS域名层级同样分为

    • 根域名,即""域名

    • 一级域名,类似.com,.cn,在ens中有.eth.reverse(用来记录反向解析,后面会提到)

    • 二级域名,即用户注册的域名,比如axtrur.eth

    • 三级域名,用户注册了二级域名之后,可以创建或修改该二级域名下的三级域名,比如app.axtrur.eth

  • NameHash算法: 由于智能合约直接与可读的域名进行交互效率低,因此ENS采用固定长度的256位加密哈希作为域名记录。namehash是一个递归算法,可从任意域名的NameHash值推导出任意子域名的值,而无需知道原域名的真实文本字符串,同时符合域名的多层级特性

    Namehash("") = "0x0000000000000000000000000000000000000000000000000000000000000000"
    Namehash("eth") = keccak256(Namehash(""), keccak256("eth"))
    Namehash("axtrur.eth") = keccak256(Namehash("eth"), keccak256("axtrur"))
    Namehash("app.axtrur.eth") = keccak256(Namehash("axtrur.eth"), keccak256("app"))
    
  • node: 在ens中,用户注册的域名比如axtrur.eth,会采用namehash算法生成的哈希node去记录链上数据,后文中我们将提到的node理解为一个域名名称比如axtrur即可,关于ENS名称的处理请参考 https://ensuser.com/docs/contract-api-reference/name-processing.html

ENS模块概念

  • 注册器合约(绿色部分):负责分配名称的合约,有正向注册器,反向注册器,DNS注册器

  • 解析器合约(蓝色部分):负责记录域名映射关系的合约,分正向、反向解析器,其中正向解析器合约也可自定义实现

    • 正向解析(主网上有默认的公共解析器合约):负责记录域名所绑定的内容,即域名的nameNode(比如axtrur.ethapp.axtrur.eth)到(包括不限于name, addr, txt, contenthash等内容)的解析,可设置各类币种的钱包地址,还可以设置IPFS的内容哈希,甚至记录邮箱等第三方账号作为文本记录。

    • 反向解析(主网上有默认的反向解析器合约):负责记录钱包地址所绑定的域名,即反向域名的nameNode(比如{{钱包地址}}.addr.reverse) 到 域名名称的解析

  • 根合约:是根域名的owner,拥有一级域名的管理权限

  • 控制器合约:官网的注册入口合约(如果需要实现不同的玩法合约,则统一归为控制器合约模块,需要将对应注册器合约地址设置给注册器,才有权限操作注册器进行域名NFT注册与记录反向解析)

  • 价格预言机:ENS定价采用的是U本位(usdt),所以需要USDT预言机来计算某一时刻的注册费的eth换算值,ENS主网上的注册费为:

    • 长度为 5+个字符的域名:每年支付 5 美元

    • 长度为 4 个字符的域名:每年支付 160 美元

    • 长度为 3 个字符的域名:每年支付 640 美元

  • DNSSEC预言机:DNS安全扩展预言机合约,负责校验证明web3域名的所有权和有效性


ENS模块解析

注册表合约(EnsRegistry.sol)

注册表是ENS最核心的合约,上图为注册表合约内部的records结构,维护着域名层级node对应的owner、解析器、ttl信息注册表是ENS最核心的合约,上图为注册表合约内部的records结构,维护着域名层级node对应的owner、解析器、ttl信息

除了注册表信息records维护,合约还维护owner的委托管理者信息operators,owner可以通过添加设置委托管理者地址(可以是用户地址,也可以是合约地址)来共同管理域名信息 合约中相关管理设置接口(比如设置解析器,ttl,以及创建和修改子域名),都会通过修饰器`authorised(node)`来限制调用权限;该修饰器将判断该接口的交易请求者是否为当前域名的owner,或者委托管理者地址,保证了仅有域名的owner或委托者才有创建下一级子域名的权限。 同时这里部署初始化的时候将`""`根域名的node的owner设置为部署者,只有这样,部署者才能将根域名的owner设置给Root合约根合约

(Root.sol)根合约是根域名的owner,同时根合约作为根域名的owner,有权限调用注册表合约的setSubnodeOwner接口,将域名.eth的owner指向基础注册器合约;

基础注册器合约(BaseRegistrarImplementation.sol)

由于Root合约将域名.eth的owner指向基础注册器合约(又称正向注册器合约),从而基础注册器拥有.eth底下的二级域名的设置权限,使得用户可以通过基础注册器合约进行域名注册;同时该注册器合约继承了ERC721协议标准,这也就是为什么ENS域名可以作为NFT在交易市场比如opensea上买卖的原因。除此之外,基础注册器合约还维护着每个域名的过期时间expiries,注册器为每个域名设置了90天的保护期,当域名过期后且在保护期内,域名拥有者可以通过调用续期renew接口进行续期,如果超过了保护期,则需要重新注册(这里重新注册会先销毁NFT在重新mint)。 同时在ENS设计中,注册器合约(不管是正向注册器还是反向注册器)基本上都有controllers结构,维护着可信的controller注册器合约,只有可信合约才可进行调用。

控制器合约(ETHRegsiterController.sol)

用户在官网中,将要注册的域名等注册信息传给控制器合约,控制器合约通过预言机计算该域名的价格,同时将域名通过namehash转成node后传给基础注册器进行域名NFT的注册,同时将域名相关注册表信息写入注册表合约完成注册,同时域名的owner可以在官网通过注册表合约进行管理操作,官网中的注册页面如下:

核心注册流程:

ENS注册采用“请求-提交”两阶段注册模式ENS注册采用“请求-提交”两阶段注册模式,为什么需要两阶段提交?我们知道以太坊节点从交易池pool中捞取交易是会按照交易给的gas费进行优先级排序;在注册者携带待注册域名构造的交易提交上链前,在整个网络是公开透明的,恶意的攻击者可以监听并解析此类待上链交易,并构造相同域名的注册交易,通过提高gas费的方式抢先上链注册控制器合约注册。

为了防止此类域名抢注问题,ENS采用了先请求,后提交的注册模式。在第一阶段并不直接提交域名,而是先调用

makeCommitment接口根据待申请域名name、待申请地址owner、随机值secret进行哈希后生成一条特殊的commitment后,通过commit提交上链

提交阶段的commitment记录着当前时间戳,同时ENS设置commitment的有效期为60s到86400s之间;第二阶段注册的时候合约会重新计算commitment,判断是否与第一阶段提交的一致,同时检查Commitment的有效期,保证跟第一阶段的链上处理时间间隔1分钟以上,保证记录了第一阶段交易的区块经过了至少5个后续区块的确认。(此时攻击者虽然可以获取域名值,但由于只有第一阶段的owner需要根第二阶段的owner一致才能生成一致的commitment,从而避免了被抢注的风险)

用户在官网的第二阶段注册流程实际上是代码中的resolver != address(0)逻辑分支,因为ENS默认会将注册的resolver解析器设置为默认的公共正向解析器(publicResolver后面会提到),这里为什么需要将域名注册给合约本身然后在转移给用户呢?因为上文中我们提到注册表合约中只有owner或者委托管理者才有权限设置解析器或更新owner,所以为了帮用户设置好解析器,需要通过基础注册器注册(register)给合约自身,再通过注册表合约设置解析器(setResolver),然后声明所有权(reclaim),最后才转移给注册者(transferFrom)

解析器(Resolver)

ENS中的解析器合约分为正向解析和反向解析,解析记录是ENS比较重要的内容,只有定义好规范,生态才能方便的即成ENS这类web3域名系统

正向解析(ENS默认的正向解析器合约PublicResolver.sol 或者自定义解析器合约

负责将域名映射为对应用户设置的内容(包括币种地址,ipfs内容hash,通用text记录等等

首先metamask会通过注册表合约获取域名node设置的解析器地址(默认的公共解析器,也可以是用户自定义的解析器合约地址),然后与该解析器地址交互,获取用户设置的eth的币种地址(官网注册默认会设置成注册者,注册者后续可自由更改)进行转账操作

反向解析(ENS默认反向解析器合约DefaultReverseResolver.sol)

负责将用户钱包地址映射为对应的域名

反向解析实际上是对用户不透明的,用户也无法像正向解析器合约那样可以自定义。用户也可以通过反向注册器(ReverseRegistrar.sol)的setName方法设置当前钱包地址要绑定的域名,反向记录同样在ENS注册表合约维护,用户注册的反向记录在三级域名记录中,格式为:具体用户地址.addr.reverse

设置反向解析之后,opensea用户界面会将用户钱包地址展示为可读的ENS域名,则是反向解析的过程解析器结构以及node对应的注册表信息

根域名的owner是根域名

  • .eth一级域名的owner是正向注册器也就是(BaseRegistrarImplement.sol)

  • 用户注册的二级、三级域名的owner是用户本身,同时可以自由设置解析器合约地址

  • .reserve一级域名owner是ENS的多签钱包地址

  • .addr.reserve二级域名owner是反向注册器,ens当前主网版本控制器合约注册的时候默认通过反向注册器(ReverseRegistrar.sol)设置反向解析记录(比如具体用户地址.addr.reverse指向axtrur.eth),用户无需提供gas之外的反向注册费。

  • 用户注册域名的反向三级域名的owner都默认指向反向注册器合约,同时resolver默认指向反向解析器合约

上面我们已经把ENS域名合约设计以及主要的模块梳理完了,ENS在设计上比如模块拆分,权限拆分方面都是值得我们借鉴的,但是目前主网上的ENS也存在一些问题

ENS存在问题与解决

  1. 零宽问题:这是ENS目前比较麻烦的问题,因为合约设计之初并没有限制零宽字符(关于零宽问题解释 https://mirror.xyz/0xc952fE149b640097054CFa53cAf7aC2bfd0162C5/RW6psQ2mnxyzmQx08PUXgLXOZc0kjvfXm8RGRVu8s0Y ),比如可以官网注册某个域名的时候,如果该域名已被注册,此时用 https://unicode-table.com/en/200B/ 拷贝对应某种零宽字符串插入到要注册的域名中间某个位置,则可以注册对应的域名了

  2. 特殊字符:ENS合约并没有限制.,emoji表情等特殊字符过滤,以致于目前交易市场存在太多冗杂非规范的域名

目前ENS官网已经对特殊字符进行过滤,并给予必要的警告提示(但是合约本身并没有限制,所以科学家一样可以通过合约进行注册)

3.transfer问题:ENS目前有个比较麻烦的问题就是域名NFT在转移的时候,owner没有同步转移,所以当你在交易市场买了一个ENS域名NFT的时候,你需要通过基础注册器合约的reclaim接口,消耗一定的gas费声明NFT所有权后,才能到ens官网上看到自己拥有的域

4.tokenURI问题:ENS的基础注册器合约并没有即成ERC721标准的tokenURI,可能是设计之初没有考虑好,所以目前我们在交易市场比如opensea上的ENS的NFT的metadata,是交易市场特殊对ENS即成了ENS中心化的metaservice的API,比如(https://metadata.ens.domains/mainnet/0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85/92165218023603606815515740961699344403389102980529428548045197994533319339809

5.保留字:这是我觉得.bit这方面做得比较好的方面,.bit官方会把web2世界中的机构或公司名称保留下来,便于后续web2与web3之间的连接(https://github.com/dotbitHQ/Documents/blob/main/Reserved_DAS/Reserved_DAS_List.md )这对于web3域名生态发展是有意义

6.基础合约可升级,ENS目前对于基础模块并没有采用代理模式支持合约可升级,这样当未来需要对基础模块升级的时候是比较麻烦的,一种是fallback一种是迁移数据,但这都只能解决部分问题,这个方面ENS是有改善空间的。

我们可以怎么解决上面那些问题呢

1.字符问题,我们可以在控制器合约的valid函数修改逻辑,一种实现方式是限制零宽等特殊字符比如spaceid( https://github.com/Space-ID/SpaceIDContract-Audit/blob/main/contracts/bnbregistrar/BNBRegistrarControllerV9.sol#L88 ),另一种就是只允许符合规范的字符

2.transfer问题:我们可以在基础注册器里复写transferFrom和saveTransferFrom函数,在转移nft的同时调用setSubnodeOwner转移owner

3.tokenURI问题:这个比较简单我们只要继承ECR721的tokenURI标准呢就可以了,那怎么实现在图片中动态的域名的展示呢?我们可以采用svg上链(可以看文章后面改造后的合约代码的TokenURIBuilder.sol)

4.保留字问题:可以将保留字以及对应要保留的钱包地址上链,先保留给合约本身,后面可以通过apply接口申请给某个特定地址

5.基础合约可升级:我们可以采用代理模式(eip-1967)对基础模块合约进行改造,感兴趣可以参考lens-protocol的合约设计(

DNS模块

ENS的DNS能力并不是我们说的web2域名系统比如.com可以实现在浏览器里访问域名来访问你的ens域名,ENS的dns注册实际上只是基于DNS安全扩展,通过相关的证明,校验算法证明你对于该web2的域名的所有权,然后在链上做一个(web2域名到钱包地址)的记录,使得我们可以用web2域名进行链上转账。详见(https://ensuser.com/docs/dns-registrar-guide.html

但是本文为什么我们没有详细讲ENS的DNS模块呢?是因为ENS虽然花了大部分精力在实现DNS,但是这个功能放在ENS比较鸡肋,用的人很少。其实这实际上是一个did聚合的范畴,类似的能力个人觉得更适合放到聚合DID中去实现,比如像mask network的nextid,cloak network的zkid。可以把proof做好,向即成twitter等web2的handler一样,去集成web2域名

部署自己的web3域名

本文最后给大家提供一个改造后的ENS域名合约版本( https://github.com/axtrur/xens-contracts 改造内容以及部署方式详见readme),方便大家自行部署自己的web3域名,深入理解web3域名系统的设计原理 部署goerli测试网命令

OWNER_KEY={{account private key}} INFURA_ID=c03713652e3c4ef6a3c09ea7dbf58711 npx hardhat deploy --network goerli (INFURA_ID可以替换成自己的infuraid,执行前删除deployment/goerli文件夹以及deployment/goerli_result.json)

部署测试网goerli后,执行注册脚本 ens.js 注册域名

OWNKEY={{account private key}} INFURA=https://goerli.infura.io/v3/c03713652e3c4ef6a3c09ea7dbf58711  node ens.js

就可以到opensea测试网查看已经部署的nft了,比如我部署的.buidlerdao后缀的域名就可以到opensea测试网查看已经部署的nft了,比如我部署的.buidlerdao后缀的域名

总结

ENS域名作为web3域名的先行者,在设计上有很多值得借鉴的地方,我们看到的.bnb,.nft也都是基于ens合约基础上搭建的。希望通过本文大家对ENS的设计从整体到细节有个深入的深入,web3域名不仅仅是一个NFT,他有着更深远的意义。同时web域名只是一个很小的开始,相信随之普及、生态集成以及大家对did的探索,原生链上的可读的web3域名将会被聚合起来,使得每个用户在加密世界里都有个统一的名片描述,更好地去连接多链生态,连接用户。

ENS域名系统相关EIP标准

  • EIP 137 - 注册表 https://eips.ethereum.org/EIPS/eip-137

  • EIP 181 - 反向注册器 https://eips.ethereum.org/EIPS/eip-181

  • EIP 205 - ABI解析 (ABI()).

  • EIP 619 - SECP256k1公钥解析 (pubkey()).

  • EIP 634 - 文本记录解析 (text()).

  • EIP 1577 - 内容hash解析(contenthash()).

  • EIP 2304 - 多代币地址解析 (addr()). 新的记录类型可以随时通过 EIP 标准化程序进行定义

主网部署的ENS合约

  • 注册表合约:0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e

  • 根合约:0xab528d626ec275e3fad363ff1393a41f581c5897

  • 基础正向注册器合约:0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85

  • 反向注册器合约:0x084b1c3c81545d370f3634392de611caabff8148

  • 默认正向解析器合约:0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41

  • 默认反向解析器合约:0xa2c122be93b0074270ebee7f6b7292c7deb45047

  • 控制器合约:0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5

ENS域名资料

其他web3域名系统资料

MOVE OVER HODL,

IT'S TIME TO BUIDL!

Buidler DAO 聚集投研、技术、运营等领域的 Web3 实干家,以 DAO 的方式组织内容输出和产品实践,建立 Web3 人才网络和项目加速器;以自身实践打造 SocialDAO 治理范式和 DAO Tools 解决方案。

五大公会共同协作:孵化、技术公会整合资源加速内外项目,构建产品生态;投研、教育公会输出深度研报、课程、播客等原创内容,建立行业影响力;运营公会塑造治理体系、文化氛围与合作生态,驱动社区增长与迭代。

官方链接:https://linktr.ee/buidlerdao
深度参与:https://tally.so/r/wA7LlN

Subscribe to Buidler DAO
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.