本文以 ENS 為例從整體架構到合約細節,深度剖析 Web3 域名系統的設計。
作者:@axtrur,Buidler DAO 研究員
編排: @Coucou
封面: ens.eth
Web3 域名系統,簡而言之就是基於區塊鏈的分佈式、去中心化的命名系統,與 DNS(互聯網名稱服務)類似,將地址(錢包地址或智能合約地址)解析成可讀性的名稱,本文以 ENS 為例從整體架構到合約細節,深度剖析 Web3 域名系統的設計。
全文 6000 字,預計閱讀時間 15 分鐘
文章速覽:
01/ ENS 前置概念
02/ ENS 模塊概念
03/ ENS 模塊解析
04/ ENS 存在的問題與解決
05/ ENS 的 DNS 模塊
06/ 部署自己的 web3 域名
07/ 總結
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 名稱的處理請參考
ENS 模塊概念
結合 ENS 合約代碼,將 ENS 進行了模塊上的梳理,方便理解。
註冊表合約 (紅色部分): ENS 的核心合約,負責維護名稱以及對應的 (owner) 域名的所有者、resolver (解析器合約地址) 和 ttl (域名下所有記錄的緩存存活時間)
註冊器合約 (綠色部分): 負責分配名稱的合約,有正向註冊器,反向註冊器,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 模塊解析
註冊表合約 (EnsRegistry.sol)
註冊表是 ENS 最核心的合約,上圖為註冊表合約內部的 records 結構,維護著域名層級 node 對應的 owner、解析器、ttl 信息
除了註冊表信息 records 維護,合約還維護 owner 的委託管理者信息 operators,owner 可以通過添加設置委託管理者地址(可以是用戶地址,也可以是合約地址)來共同管理域名信息。
合約中相關管理設置接口(比如設置解析器,ttl,以及創建和修改子域名),都會通過修飾器 authorised(node) 來限制調用權限;該修飾器將判斷該接口的交易請求者是否為當前域名的 owner,或者委託管理者地址,保證了僅有域名的 owner 或委託者才有創建下一級子域名的權限。
同時這裡部署初始化的時候將”” 根域名的 node 的 owner 設置為部署者,只有這樣,部署者才能將根域名的 owner 設置給 Root 合約。
setSubnodeOwner 接口允許 owner 設置子域名的 owner 給某個用戶或者合約,使得該用戶或者合約擁有該子域名下的所有信息管理權限。
根合約(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 註冊採用 “請求-提交” 兩階段註冊模式
為什麼需要兩階段提交?我們知道以太坊節點從交易池 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 轉賬輸入 ens 域名,會解析成對應的 ETH 幣種地址的過程是如何解析的呢?
首先 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://unicode-table.com/en/200B/ 拷貝對應某種零寬字符串插入到要註冊的域名中間某個位置,則可以註冊對應的域名了。
關於零寬問題解釋 :
2. 特殊字符: ENS 合約並沒有限制.,emoji 表情等特殊字符過濾,以致於目前交易市場存在太多冗雜非規範的域名。
目前 EN S 官網已經對特殊字符進行過濾,並給予必要的警告提示(但是合約本身並沒有限制,所以科學家一樣可以通過合約進行註冊)。
3. transfer 問題: ENS 目前有個比較麻煩的問題就是域名 NFT 在轉移的時候,owner 沒有同步轉移,所以當你在交易市場買了一個 ENS 域名 NFT 的時候,你需要通過基礎註冊器合約的 reclaim 接口,消耗一定的 gas 費聲明 NFT 所有權後,才能到 ens 官網上看到自己擁有的域名。
4. tokenURI 問題: ENS 的基礎註冊器合約並沒有即成 ERC721 標準的 tokenURI,可能是設計之初沒有考慮好,所以目前我們在交易市場比如 opensea 上的 ENS 的 NFT 的 metadata,是交易市場特殊對 ENS 即成了 ENS 中心化的 metaservice 的 API,比如
5. 保留字:這是我覺得.bit 這方面做得比較好的方面,.bit 官方會把 Web2 世界中的機構或公司名稱保留下來,便於後續 Web2 與 Web3 之間的連接
這對於 Web3 域名生態發展是有意義的。
6. 基礎合約可升級,ENS 目前對於基礎模塊並沒有採用代理模式支持合約可升級,這樣當未來需要對基礎模塊升級的時候是比較麻煩的,一種是 fallback 一種是遷移數據,但這都只能解決部分問題,這個方面 ENS 是有改善空間的。
如何解決上述問題?
1. 字符問題,我們可以在控制器合約的 valid 函數修改邏輯
- 限制零寬等特殊字符比如 spaceidhttps://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 接口申請給某個特定地址。
基礎合約可升級:我們可以採用代理模式 (eip-1967) 對基礎模塊合約進行改造,感興趣可以參考 lens-protocol 的合約設計
ENS 的 DNS 模塊
ENS 的 DNS 能力並不是我們說的 Web2 域名系統比如.com 可以實現在瀏覽器裡訪問域名來訪問你的 ens 域名,ENS 的 dns 註冊實際上只是基於 DN S 安全擴展,通過相關的證明,校驗算法證明你對於該 Web2 的域名的所有權,然後在鏈上做一個(Web2 域名到錢包地址)的記錄,使得我們可以用 Web2 域名進行鏈上轉賬。詳見
但是本文為什麼我們沒有詳細講 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 後綴的域名
總結
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 域名資料
- 官網: https://app.ens.domains/
- github: https://github.com/ensdomains
- 合約代碼:https://github.com/ensdomains/ens-contracts/tree/master/contracts
- 線網部署版本:https://etherscan.io/accounts/label/ens
- 文檔:https://ensuser.com/docs/contract-api-reference/ens-contracts-overview.html
- 交易市場:https://opensea.io/collection/ens
其他 Web3 域名系統資料
- .bnb
官網:
github:
交易市場:
- .bit
官網:
github:
交易市場:
- .nft
官網:
交易市場:
往期回顧
Buidler DAO 聯合發布:Web3 世界的通行證 — 錢包到底是什麼?
Buidler DAO 一文讀懂 IBC:Cosmos 跨鏈通信協議
免責聲明:作為區塊鏈信息平台,本站所發布文章僅代表作者及嘉賓個人觀點,與 Web3Caff 立場無關。文章內的信息僅供參考,均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。