编排:@Coucou @满意my
注:文章仅代表作者个人观点,不构成任何投资建议。
web3域名系统,简而言之就是基于区块链的分布式、去中心化的命名系统,与DNS(互联网名称服务)类似,将地址(钱包地址或智能合约地址)解析成可读性的名称,本文以ENS为例从整体架构到合约细节,深度剖析web3域名系统的设计
ENS前置概念
ENS模块概念
ENS模块解析
ENS存在的问题与解决
ENS的DNS模块
部署自己的web3域名
总结
域名层级:类似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
注册器合约(绿色部分):负责分配名称的合约,有正向注册器,反向注册器,DNS注册器
解析器合约(蓝色部分):负责记录域名映射关系的合约,分正向、反向解析器,其中正向解析器合约也可自定义实现
正向解析(主网上有默认的公共解析器合约):负责记录域名所绑定的内容,即域名的nameNode(比如axtrur.eth
,app.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最核心的合约,上图为注册表合约内部的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指向基础注册器合约;
由于Root合约将域名.eth
的owner指向基础注册器合约(又称正向注册器合约),从而基础注册器拥有.eth
底下的二级域名的设置权限,使得用户可以通过基础注册器合约进行域名注册;同时该注册器合约继承了ERC721协议标准,这也就是为什么ENS域名可以作为NFT在交易市场比如opensea上买卖的原因。除此之外,基础注册器合约还维护着每个域名的过期时间expiries,注册器为每个域名设置了90天的保护期,当域名过期后且在保护期内,域名拥有者可以通过调用续期renew接口进行续期,如果超过了保护期,则需要重新注册(这里重新注册会先销毁NFT在重新mint)。 同时在ENS设计中,注册器合约(不管是正向注册器还是反向注册器)基本上都有controllers结构,维护着可信的controller注册器合约,只有可信合约才可进行调用。
用户在官网中,将要注册的域名等注册信息传给控制器合约,控制器合约通过预言机计算该域名的价格,同时将域名通过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)
ENS中的解析器合约分为正向解析和反向解析,解析记录是ENS比较重要的内容,只有定义好规范,生态才能方便的即成ENS这类web3域名系统
负责将域名映射为对应用户设置的内容(包括币种地址,ipfs内容hash,通用text记录等等
首先metamask会通过注册表合约获取域名node设置的解析器地址(默认的公共解析器,也可以是用户自定义的解析器合约地址),然后与该解析器地址交互,获取用户设置的eth的币种地址(官网注册默认会设置成注册者,注册者后续可自由更改)进行转账操作
负责将用户钱包地址映射为对应的域名
反向解析实际上是对用户不透明的,用户也无法像正向解析器合约那样可以自定义。用户也可以通过反向注册器(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目前比较麻烦的问题,因为合约设计之初并没有限制零宽字符(关于零宽问题解释 https://mirror.xyz/0xc952fE149b640097054CFa53cAf7aC2bfd0162C5/RW6psQ2mnxyzmQx08PUXgLXOZc0kjvfXm8RGRVu8s0Y ),比如可以官网注册某个域名的时候,如果该域名已被注册,此时用 https://unicode-table.com/en/200B/ 拷贝对应某种零宽字符串插入到要注册的域名中间某个位置,则可以注册对应的域名了
特殊字符: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的合约设计(
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域名
本文最后给大家提供一个改造后的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域名将会被聚合起来,使得每个用户在加密世界里都有个统一的名片描述,更好地去连接多链生态,连接用户。
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 标准化程序进行定义
注册表合约:0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e
根合约:0xab528d626ec275e3fad363ff1393a41f581c5897
基础正向注册器合约:0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85
反向注册器合约:0x084b1c3c81545d370f3634392de611caabff8148
默认正向解析器合约:0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41
默认反向解析器合约:0xa2c122be93b0074270ebee7f6b7292c7deb45047
控制器合约:0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5
github: https://github.com/ensdomains
合约代码:https://github.com/ensdomains/ens-contracts/tree/master/contracts
文档:https://ensuser.com/docs/contract-api-reference/ens-contracts-overview.html
.bnb
.bit
.nft
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