ENS(以太坊域名服務)智能合約源碼分析二
0、簡介
本次分享直接使用線上實際註冊流程來分析最新註冊以太坊域名的相關代碼。本次主要分析最新的關於普通域名註冊合約和普通域名遷移合約,短域名競拍合約不再本次分析範圍內。
1、實際註冊過程
先看張時序圖來了解下域名註冊和使用的流程:
① 在app.ens.domains網站挑選自己要註冊的域名,點擊註冊後第一個交易詳情分析。
首先第一個交易詳情如上圖,實際調用了ETHRegistrarController
的commit(byte32)
方法,下面對此方法進行分析。
// ETHRegistrarController成員變量介紹
// 最小注冊時長
uint constant public MIN_REGISTRATION_DURATION = 28 days;
// 最短和最常commitment生存時間,一個commitment在>=min && < max時候纔可用
uint public minCommitmentAge; //86400 uint256
uint public maxCommitmentAge; //60 uint256
// commitment=>time
mapping(bytes32=>uint) public commitments;
// 在ETHRegistrarController合約裏面實際調用了下面這個方法
function commit(bytes32 commitment) public {
require(commitments[commitment] + maxCommitmentAge < now);
commitments[commitment] = now;
}
// ETHRegistrarController爲用戶提供了一個工具方法用來生成commitment
// 當然用戶可以自己生成,這樣免得和智能合約交互,速度更快。
// 在網站上註冊域名時候顯然是網站直接幫用戶根據相關數據直接生成了commitment
function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) {
return makeCommitmentWithConfig(name, owner, secret, address(0), address(0));
}
function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) {
bytes32 label = keccak256(bytes(name));
if (resolver == address(0) && addr == address(0)) {
return keccak256(abi.encodePacked(label, owner, secret));
}
require(resolver != address(0));
// 此方法相當於 web3.util.keccak256(label, owner, resolver, addr, secret);
return keccak256(abi.encodePacked(label, owner, resolver, addr, secret));
}
上面貼出了ETHRegistrarController的屬性變量和註冊域名第一個交易調用的方法,下面進行邏輯分析。
1、commitment機制主要是爲了使用戶在提交一個域名註冊請求的時候,在maxCommitmentAge時間段內不能再重複提交相同請求。
2、commitment會在註冊時候被作爲一個預定憑證消耗掉。
下面看看第二個交易的內容:
從交易中可以看出來調用了ETHRegistrarController的registerWithConfig方法,下面分析registerWithConfig方法源碼內容。
function registerWithConfig(string memory name, address owner, uint duration, bytes32 secret, address resolver, address addr) public payable {
// 內部調用有推薦者的註冊方法
registerWithReferrer(name, owner, duration, secret, address(0), resolver, addr);
}
function registerWithReferrer(string memory name, address owner, uint duration, bytes32 secret, address payable referrer, address resolver, address addr) public payable {
// 先使用原參數進行commitment生成
bytes32 commitment = makeCommitmentWithConfig(name, owner, secret, resolver, addr);
// 通過_consumeCommitment對commitment進行消費,返回註冊費用或者回滾
uint cost = _consumeCommitment(name, duration, commitment);
// 對name進行keccak256編碼爲一個label
bytes32 label = keccak256(bytes(name));
// 將label轉化爲uint256類型作爲ERC721的tokenId
uint256 tokenId = uint256(label);
uint expires;
// 如果用戶註冊時候指定了解析器
if(resolver != address(0)) {
// 調用baseRegistrar的registrer方法
expires = base.register(tokenId, address(this), duration);
// 對label進行hash操作
bytes32 nodehash = keccak256(abi.encodePacked(base.baseNode(), label));
// 調用baseRegistrar的ens合約設置域名對應的解析器
base.ens().setResolver(nodehash, resolver);
// 配置解析器對應的地址
if (addr != address(0)) {
Resolver(resolver).setAddr(nodehash, addr);
}
// 將擁有者從本合約轉給註冊人
base.reclaim(tokenId, owner);
base.transferFrom(address(this), owner, tokenId);
} else {
// 如果註冊時候沒設置解析器,那麼直接調用baseRegistrar的註冊方法
require(addr == address(0));
expires = base.register(tokenId, owner, duration);
}
emit NameRegistered(name, label, owner, cost, expires);
// 如果註冊消耗費用小於註冊人轉賬的金額,那麼退費
// Refund any extra payment
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}
// 如果有推薦人,那麼給推薦人獎勵
_sendReferralFee(referrer, cost);
}
以上就是ETHRegistrarController的registerWithConfig方法邏輯介紹,下面開始對其中涉及到的方法進行展開分析。
①_consumeCommitment()方法
function _consumeCommitment(string memory name, uint duration, bytes32 commitment) internal returns (uint256) {
// commitment在有效期內
require(commitments[commitment] + minCommitmentAge <= now);
require(commitments[commitment] + maxCommitmentAge > now);
// 判斷name是否可註冊,即長度是否符合條件、是否被別人已經註冊了
require(available(name));
// 從commitments的map中刪除此commitment,相當於已經消耗
delete(commitments[commitment]);
// 小於rentPrice根據name和註冊時長估算註冊費用
uint cost = rentPrice(name, duration);
require(duration >= MIN_REGISTRATION_DURATION);
require(msg.value >= cost);
return cost;
}
function available(string memory name) public view returns(bool) {
bytes32 label = keccak256(bytes(name));
return valid(name) && base.available(uint256(label));
}
function valid(string memory name) public pure returns(bool) {
return name.strlen() >= 3;
}
// BaseRegistrarImplementation.available()方法
function available(uint256 id) public view returns(bool) {
// 如果被註冊或者在其域名寬限期內都是不可註冊狀態
// 過期時間+寬限期(90day)
return expiries[id] + GRACE_PERIOD < now;
}
② base.register()方法
function register(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, true);
}
function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {
// 判斷tokenid是否可用
require(available(id));
// 防止溢出
require(now + duration + GRACE_PERIOD > now + GRACE_PERIOD);
// 更新id對應的過期時間
expiries[id] = now + duration;
// 判斷是否域名以前存在,若存在說明過期了。下面都是ERC721相關方法
if(_exists(id)) {
// 先銷燬id,其實這步主要清除一下前一個owner的遺留數據(如approval等等)
_burn(id);
}
// 再重新鑄出id給owner
_mint(owner, id);
// 判斷是否需要更新註冊器,如需要更新id對應子節點的owner爲當前註冊者
if(updateRegistry) {
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
emit NameRegistered(id, owner, now + duration);
// 返回過期時間
return now + duration;
}
其中涉及到的ERC721相關方法可以參考ERC721.sol
③ reclaim()
function reclaim(uint256 id, address owner) external live {
require(_isApprovedOrOwner(msg.sender, id));
ens.setSubnodeOwner(baseNode, bytes32(id), owner);
}
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
小結
至此爲止一個域名就完整的註冊下來了,但是還沒設置解析器等組件,後面我們全局整體分析下ENS的新合約。
3、合約全覽
3.1 ETHRegistrarController
先看此合約的類繼承圖:
源碼:
/**
*Submitted for verification at Etherscan.io on 2020-01-29
*/
contract ETHRegistrarController is Ownable {
using StringUtils for *;
uint constant public MIN_REGISTRATION_DURATION = 28 days;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private COMMITMENT_CONTROLLER_ID = bytes4(
keccak256("rentPrice(string,uint256)") ^
keccak256("available(string)") ^
keccak256("makeCommitment(string,address,bytes32)") ^
keccak256("commit(bytes32)") ^
keccak256("register(string,address,uint256,bytes32)") ^
keccak256("renew(string,uint256)")
);
bytes4 constant private COMMITMENT_WITH_CONFIG_CONTROLLER_ID = bytes4(
keccak256("registerWithConfig(string,address,uint256,bytes32,address,address)") ^
keccak256("makeCommitmentWithConfig(string,address,bytes32,address,address)")
);
// 組合了BaseRegistrar 合約對象
BaseRegistrar base;
// 組合了PriceOracle 價格預估對象
PriceOracle prices;
// 下面對象已經介紹過,不再贅述
uint public minCommitmentAge;
uint public maxCommitmentAge;
mapping(bytes32=>uint) public commitments;
event NameRegistered(string name, bytes32 indexed label, address indexed owner, uint cost, uint expires);
event NameRenewed(string name, bytes32 indexed label, uint cost, uint expires);
event NewPriceOracle(address indexed oracle);
// 構造函數
constructor(BaseRegistrar _base, PriceOracle _prices, uint _minCommitmentAge, uint _maxCommitmentAge) public {
require(_maxCommitmentAge > _minCommitmentAge);
base = _base;
prices = _prices;
minCommitmentAge = _minCommitmentAge;
maxCommitmentAge = _maxCommitmentAge;
}
// 此方法調用price合約進行估算註冊費用
function rentPrice(string memory name, uint duration) view public returns(uint) {
bytes32 hash = keccak256(bytes(name));
return prices.price(name, base.nameExpires(uint256(hash)), duration);
}
// 域名續期
function renew(string calldata name, uint duration) external payable {
uint cost = rentPrice(name, duration);
require(msg.value >= cost);
bytes32 label = keccak256(bytes(name));
uint expires = base.renew(uint256(label), duration);
if(msg.value > cost) {
msg.sender.transfer(msg.value - cost);
}
emit NameRenewed(name, label, cost, expires);
}
// 以下方法上面已經介紹過,在這裏只放方法的摘要
function valid(string memory name) public pure returns(bool) {}
function available(string memory name) public view returns(bool) {}
function makeCommitment(string memory name, address owner, bytes32 secret) pure public returns(bytes32) {}
function makeCommitmentWithConfig(string memory name, address owner, bytes32 secret, address resolver, address addr) pure public returns(bytes32) {}
function commit(bytes32 commitment) public {}
function registerWithConfig(string memory name, address owner, uint duration, bytes32 secret, address resolver, address addr) public payable {}
function setPriceOracle(PriceOracle _prices) public onlyOwner {}
function setCommitmentAges(uint _minCommitmentAge, uint _maxCommitmentAge) public onlyOwner {}
function withdraw() public onlyOwner {}
function supportsInterface(bytes4 interfaceID) external pure returns (bool) {}
function _consumeCommitment(string memory name, uint duration, bytes32 commitment) internal returns (uint256) {}
}
上面就是ETHRegistrarController對外暴露的方法。可以看出此合約主要是一個外觀合約,其底層是大多調用BaseRegistrar完成。下面開始分析BaseRegistrar合約的實現合約。
3.2BaseRegistrarImplementation
先來看張繼承關係圖:
源碼分析:
// File: @ensdomains/ethregistrar/contracts/BaseRegistrarImplementation.sol
pragma solidity ^0.5.0;
// 合約繼承自ERC721(主要記得在這裏可以使用ERC721內部方法)和BaseRegistrar
contract BaseRegistrarImplementation is BaseRegistrar, ERC721 {
// 一個map記錄着域名的過期時間
mapping(uint256=>uint) expiries;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private ERC721_ID = bytes4(
keccak256("balanceOf(address)") ^
keccak256("ownerOf(uint256)") ^
keccak256("approve(address,uint256)") ^
keccak256("getApproved(uint256)") ^
keccak256("setApprovalForAll(address,bool)") ^
keccak256("isApprovedForAll(address,address)") ^
keccak256("transferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256,bytes)")
);
bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
// 構造函數,傳入ens合約和跟域名.eth
constructor(ENS _ens, bytes32 _baseNode) public {
ens = _ens;
baseNode = _baseNode;
}
modifier live {
require(ens.owner(baseNode) == address(this));
_;
}
modifier onlyController {
require(controllers[msg.sender]);
_;
}
function ownerOf(uint256 tokenId) public view returns (address) {}
// 添加一個Controller作爲外觀合約
function addController(address controller) external onlyOwner {
controllers[controller] = true;
emit ControllerAdded(controller);
}
// Revoke controller permission for an address.
function removeController(address controller) external onlyOwner {
controllers[controller] = false;
emit ControllerRemoved(controller);
}
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external onlyOwner {
ens.setResolver(baseNode, resolver);
}
// Returns the expiration timestamp of the specified id.
function nameExpires(uint256 id) external view returns(uint) {
return expiries[id];
}
// Returns true iff the specified name is available for registration.
function available(uint256 id) public view returns(bool) {}
/**
* @dev Register a name.
* @param id The token ID (keccak256 of the label).
* @param owner The address that should own the registration.
* @param duration Duration in seconds for the registration.
*/
function register(uint256 id, address owner, uint duration) external returns(uint) {}
/**
* @dev Register a name, without modifying the registry.
* @param id The token ID (keccak256 of the label).
* @param owner The address that should own the registration.
* @param duration Duration in seconds for the registration.
*/
function registerOnly(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, false);
}
function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {}
function renew(uint256 id, uint duration) external live onlyController returns(uint) {
require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
expiries[id] += duration;
emit NameRenewed(id, expiries[id]);
return expiries[id];
}
/**
* @dev Reclaim ownership of a name in ENS, if you own it in the registrar.
*/
function reclaim(uint256 id, address owner) external live {}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {}
}
可以看出,在外觀合約即Controller中調用的續期、註冊等操作實際是調用BaseRegistrarImplementation合約的相關方法進行實際修改數據。
3.2.1OldBaseRegistrarImplment
以太坊域名服務器大致到現在分爲三代,這個合約可以稱之爲第二代註冊器。
源碼如下:
pragma solidity ^0.5.0;
import "@ensdomains/ens/contracts/ENS.sol";
import "@ensdomains/ens/contracts/Registrar.sol";
import "@ensdomains/ens/contracts/HashRegistrar.sol";
import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol";
import "./BaseRegistrar.sol";
contract OldBaseRegistrarImplementation is BaseRegistrar, ERC721 {
// 遷移域名結束時期
uint public transferPeriodEnds;
// 前一代的註冊器
Registrar public previousRegistrar;
// token的過期時間
mapping(uint256=>uint) expiries;
// 遷移鎖定時期
uint constant public MIGRATION_LOCK_PERIOD = 28 days;
bytes4 constant private INTERFACE_META_ID = bytes4(keccak256("supportsInterface(bytes4)"));
bytes4 constant private ERC721_ID = bytes4(
keccak256("balanceOf(uint256)") ^
keccak256("ownerOf(uint256)") ^
keccak256("approve(address,uint256)") ^
keccak256("getApproved(uint256)") ^
keccak256("setApprovalForAll(address,bool)") ^
keccak256("isApprovedForAll(address,address)") ^
keccak256("transferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256)") ^
keccak256("safeTransferFrom(address,address,uint256,bytes)")
);
bytes4 constant private RECLAIM_ID = bytes4(keccak256("reclaim(uint256,address)"));
// 構造函數
constructor(ENS _ens, HashRegistrar _previousRegistrar, bytes32 _baseNode, uint _transferPeriodEnds) public {
// Require that people have time to transfer names over.
require(_transferPeriodEnds > now + 2 * MIGRATION_LOCK_PERIOD);
ens = _ens;
baseNode = _baseNode;
previousRegistrar = _previousRegistrar;
transferPeriodEnds = _transferPeriodEnds;
}
modifier live {
require(ens.owner(baseNode) == address(this));
_;
}
modifier onlyController {
require(controllers[msg.sender]);
_;
}
// 方法和BaseRegistrarImplement相同的在下面省略函數體
function ownerOf(uint256 tokenId) public view returns (address) {}
function addController(address controller) external onlyOwner {}
function removeController(address controller) external onlyOwner {}
// Set the resolver for the TLD this registrar manages.
function setResolver(address resolver) external onlyOwner {
ens.setResolver(baseNode, resolver);
}
// 查詢指定id的過期時間
function nameExpires(uint256 id) external view returns(uint) {}
function available(uint256 id) public view returns(bool) {}
/**
* @dev Register a name.
*/
function register(uint256 id, address owner, uint duration) external returns(uint) {
return _register(id, owner, duration, true);
}
/**
* @dev Register a name.
*/
function registerOnly(uint256 id, address owner, uint duration) external returns(uint) {}
function _register(uint256 id, address owner, uint duration, bool updateRegistry) internal live onlyController returns(uint) {}
function renew(uint256 id, uint duration) external live onlyController returns(uint) {
require(expiries[id] + GRACE_PERIOD >= now); // Name must be registered here or in grace period
require(expiries[id] + duration + GRACE_PERIOD > duration + GRACE_PERIOD); // Prevent future overflow
expiries[id] += duration;
emit NameRenewed(id, expiries[id]);
return expiries[id];
}
function reclaim(uint256 id, address owner) external live {}
// 這個方法是HashRegistrar合約調用用來轉換各個entry的註冊器
function acceptRegistrarTransfer(bytes32 label, Deed deed, uint) external live {
uint256 id = uint256(label);
require(msg.sender == address(previousRegistrar));
require(expiries[id] == 0);
require(transferPeriodEnds > now);
uint registrationDate;
(,,registrationDate,,) = previousRegistrar.entries(label);
require(registrationDate < now - MIGRATION_LOCK_PERIOD);
address owner = deed.owner();
// Destroy the deed and transfer the funds back to the registrant.
deed.closeDeed(1000);
// Register the name
expiries[id] = transferPeriodEnds;
_mint(owner, id);
ens.setSubnodeOwner(baseNode, label, owner);
emit NameMigrated(id, owner, transferPeriodEnds);
emit NameRegistered(id, owner, transferPeriodEnds);
}
function supportsInterface(bytes4 interfaceID) external view returns (bool) {
return interfaceID == INTERFACE_META_ID ||
interfaceID == ERC721_ID ||
interfaceID == RECLAIM_ID;
}
}
小結
以上就是一個新的普通域名註冊器新舊合約,合約中將一個域名作爲一個ERC721 token發放給所有註冊者。下面再來看看解析器和ens合約。
3.3 Resolver合約
contract ResolverBase {
bytes4 private constant INTERFACE_META_ID = 0x01ffc9a7;
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == INTERFACE_META_ID;
}
function isAuthorised(bytes32 node) internal view returns(bool);
modifier authorised(bytes32 node) {
require(isAuthorised(node));
_;
}
// 將域名轉換爲實際地址一般使用這個方法
function bytesToAddress(bytes memory b) internal pure returns(address payable a) {
require(b.length == 20);
assembly {
a := div(mload(add(b, 32)), exp(256, 12))
}
}
function addressToBytes(address a) internal pure returns(bytes memory b) {
b = new bytes(20);
assembly {
mstore(add(b, 32), mul(a, exp(256, 12)))
}
}
}
contract NameResolver is ResolverBase {
bytes4 constant private NAME_INTERFACE_ID = 0x691f3431;
event NameChanged(bytes32 indexed node, string name);
mapping(bytes32=>string) names;
/**
* Sets the name associated with an ENS node, for reverse records.
* May only be called by the owner of that node in the ENS registry.
* @param node The node to update.
* @param name The name to set.
*/
function setName(bytes32 node, string calldata name) external authorised(node) {
names[node] = name;
emit NameChanged(node, name);
}
/**
* Returns the name associated with an ENS node, for reverse records.
* Defined in EIP181.
* @param node The ENS node to query.
* @return The associated name.
*/
function name(bytes32 node) external view returns (string memory) {
return names[node];
}
function supportsInterface(bytes4 interfaceID) public pure returns(bool) {
return interfaceID == NAME_INTERFACE_ID || super.supportsInterface(interfaceID);
}
}
3.4 ENS合約
先來看張類繼承關係圖:
源碼分析:
/**
*Submitted for verification at Etherscan.io on 2020-01-29
*/
pragma solidity ^0.5.0;
contract ENSRegistry is ENS {
// 每一條記錄包含owner和解析器和ttl
struct Record {
address owner;
address resolver;
uint64 ttl;
}
// name與record的映射關係
mapping (bytes32 => Record) records;
// 地址對應操作員映射
mapping (address => mapping(address => bool)) operators;
// Permits modifications only by the owner of the specified node.
modifier authorised(bytes32 node) {
address owner = records[node].owner;
require(owner == msg.sender || operators[owner][msg.sender]);
_;
}
/**
* @dev Constructs a new ENS registrar.
*/
constructor() public {
records[0x0].owner = msg.sender;
}
// 給一個域名創建記錄
function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external {
setOwner(node, owner);
_setResolverAndTTL(node, resolver, ttl);
}
// 給子節點創建記錄
function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external {
bytes32 subnode = setSubnodeOwner(node, label, owner);
_setResolverAndTTL(subnode, resolver, ttl);
}
// 設置新的owner
function setOwner(bytes32 node, address owner) public authorised(node) {
_setOwner(node, owner);
emit Transfer(node, owner);
}
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public authorised(node) returns(bytes32) {
bytes32 subnode = keccak256(abi.encodePacked(node, label));
_setOwner(subnode, owner);
emit NewOwner(node, label, owner);
return subnode;
}
// 給域名設置解析器
function setResolver(bytes32 node, address resolver) public authorised(node) {
emit NewResolver(node, resolver);
records[node].resolver = resolver;
}
function setTTL(bytes32 node, uint64 ttl) public authorised(node) {
emit NewTTL(node, ttl);
records[node].ttl = ttl;
}
// 方便openSea類似應用售賣域名
function setApprovalForAll(address operator, bool approved) external {
operators[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function owner(bytes32 node) public view returns (address) {
address addr = records[node].owner;
if (addr == address(this)) {
return address(0x0);
}
return addr;
}
function resolver(bytes32 node) public view returns (address) {
return records[node].resolver;
}
function ttl(bytes32 node) public view returns (uint64) {
return records[node].ttl;
}
function recordExists(bytes32 node) public view returns (bool) {
return records[node].owner != address(0x0);
}
function isApprovedForAll(address owner, address operator) external view returns (bool) {
return operators[owner][operator];
}
function _setOwner(bytes32 node, address owner) internal {
records[node].owner = owner;
}
function _setResolverAndTTL(bytes32 node, address resolver, uint64 ttl) internal {
if(resolver != records[node].resolver) {
records[node].resolver = resolver;
emit NewResolver(node, resolver);
}
if(ttl != records[node].ttl) {
records[node].ttl = ttl;
emit NewTTL(node, ttl);
}
}
}
// fallBack合約
contract ENSRegistryWithFallback is ENSRegistry {
// 集成老的ens合約,比如一代的ens合約
ENS public old;
/**
* @dev Constructs a new ENS registrar.
*/
constructor(ENS _old) public ENSRegistry() {
old = _old;
}
// 以下方法大多都是判斷在新的ens中有沒有對應記錄,沒有就是舊ens合約中的信息
function resolver(bytes32 node) public view returns (address) {
if (!recordExists(node)) {
return old.resolver(node);
}
return super.resolver(node);
}
function owner(bytes32 node) public view returns (address) {
if (!recordExists(node)) {
return old.owner(node);
}
return super.owner(node);
}
function ttl(bytes32 node) public view returns (uint64) {
if (!recordExists(node)) {
return old.ttl(node);
}
return super.ttl(node);
}
function _setOwner(bytes32 node, address owner) internal {
address addr = owner;
if (addr == address(0x0)) {
addr = address(this);
}
super._setOwner(node, addr);
}
}
3.5 RegistrarMigration
contract RegistrarMigration {
using SafeMath for uint;
bytes constant private UNUSED_SUBDOMAIN = hex'ffffffffffffffff';
// 遺留的註冊器,如HashRegistrar
Registrar public legacyRegistrar;
// 轉移結束時期
uint transferPeriodEnds;
// 第二代註冊器
OldBaseRegistrarImplementation public oldRegistrar;
// 最新註冊器
BaseRegistrarImplementation public newRegistrar;
// 第一代ens實現類
OldENS public oldENS;
// 最新ens實現類
ENS public newENS;
// 新舊子域名註冊器
AbstractSubdomainRegistrar public oldSubdomainRegistrar;
AbstractSubdomainRegistrar public newSubdomainRegistrar;
// .eth
bytes32 public baseNode;
// 構造函數
constructor(OldBaseRegistrarImplementation _old, BaseRegistrarImplementation _new, AbstractSubdomainRegistrar _oldSubdomainRegistrar, AbstractSubdomainRegistrar _newSubdomainRegistrar) public {
oldRegistrar = _old;
oldENS = OldENS(address(_old.ens()));
baseNode = _old.baseNode();
legacyRegistrar = _old.previousRegistrar();
transferPeriodEnds = _old.transferPeriodEnds();
oldSubdomainRegistrar = _oldSubdomainRegistrar;
newRegistrar = _new;
newENS = _new.ens();
require(_new.baseNode() == baseNode);
newSubdomainRegistrar = _newSubdomainRegistrar;
}
// 內部調用做遷移,參數爲 域名 域名所有者 域名過期時間
function doMigration(uint256 tokenId, address registrant, uint expires) internal {
// 計算域名對應的node
bytes32 node = keccak256(abi.encodePacked(baseNode, bytes32(tokenId)));
// 從oldEns獲取一次owner
address controller = oldENS.owner(node);
// 如果註冊者不等於舊子域名註冊器且擁有者是個合約
if(address(registrant) != address(oldSubdomainRegistrar) && hasCode(controller)) {
// 只將其傳遞的域名遷移,不遷移對應子域名
newRegistrar.registerOnly(tokenId, registrant, expires.sub(now));
return;
}
// 在新的註冊器中註冊,並先設置擁有者爲當前合約
newRegistrar.register(tokenId, address(this), expires.sub(now));
// 如果老的域名的解析器不爲空,則在新ens合約設置其解析器
address resolver = oldENS.resolver(node);
if(resolver != address(0)) {
newENS.setResolver(node, resolver);
}
// 同上
uint64 ttl = oldENS.ttl(node);
if(ttl != 0) {
newENS.setTTL(node, ttl);
}
// 如果域名註冊者是舊子域名註冊器
if(address(registrant) == address(oldSubdomainRegistrar) && address(registrant) != address(0)) {
// 從舊子域名註冊器獲取相關信息
(string memory label, uint price,, uint referralFeePPM) = oldSubdomainRegistrar.query(bytes32(tokenId), string(UNUSED_SUBDOMAIN));
address owner = oldSubdomainRegistrar.owner(bytes32(tokenId));
if(bytes(label).length == 0) {
revert("Unable to migrate domain on subdomain registrar");
}
// 批准新的子域名註冊器爲其操作者
newRegistrar.approve(address(newSubdomainRegistrar), tokenId);
// 新的子域名註冊器配置域名相關信息
newSubdomainRegistrar.configureDomainFor(label, price, referralFeePPM, address(uint160(owner)), address(0));
} else {
// 要不然就是普通註冊者的域名
// 在新的ens中設置node的擁有者爲controller
newENS.setOwner(node, controller);
// 新註冊器將擁域名轉給註冊者
newRegistrar.transferFrom(address(this), registrant, tokenId);
}
// 使用當前合約從舊的ens合約中接管域名的權利,防止註冊者在舊ens合約中再次操作
oldENS.setSubnodeOwner(baseNode, bytes32(tokenId), address(this));
}
// 遷移第二代註冊器的域名到最新域名註冊器,任何人都可以調用
function migrate(uint256 tokenId) public {
address registrant = oldRegistrar.ownerOf(tokenId);
doMigration(tokenId, registrant, oldRegistrar.nameExpires(tokenId));
}
// 批量遷移第二代註冊器中的域名
function migrateAll(uint256[] calldata tokenIds) external {
for(uint i = 0; i < tokenIds.length; i++) {
migrate(tokenIds[i]);
}
}
// 遷移第一代註冊器中的域名到最新域名註冊器中
function migrateLegacy(bytes32 label) public {
(Registrar.Mode mode, address deed, , ,) = legacyRegistrar.entries(label);
require(mode == Registrar.Mode.Owned);
address owner = Deed(deed).owner();
doMigration(uint256(label), owner, transferPeriodEnds);
}
// 批量遷移第一代註冊器中的域名到最新域名註冊器中
function migrateAllLegacy(bytes32[] calldata labels) external {
for(uint i = 0; i < labels.length; i++) {
migrateLegacy(labels[i]);
}
}
// 判斷一個地址是否包含代碼
function hasCode(address addr) private view returns(bool ret) {
assembly {
ret := not(not(extcodesize(addr)))
}
}
}