add ens contracts
Signed-off-by: VoR0220 <catalanor0220@gmail.com>
This commit is contained in:
parent
31a3fbef66
commit
6efb5eee9d
|
@ -0,0 +1,27 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
contract DNSResolver {
|
||||||
|
address public owner;
|
||||||
|
mapping (bytes32 => bytes) zones;
|
||||||
|
|
||||||
|
modifier owner_only {
|
||||||
|
require(msg.sender == owner);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DNSResolver() public {
|
||||||
|
owner = msg.sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDnsrr(bytes32 node, bytes data) public owner_only {
|
||||||
|
zones[node] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dnsrr(bytes32 node) public view returns (bytes) {
|
||||||
|
return zones[node];
|
||||||
|
}
|
||||||
|
|
||||||
|
function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
|
||||||
|
return interfaceID == 0x126a710e;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Deed to hold ether in exchange for ownership of a node
|
||||||
|
* @dev The deed can be controlled only by the registrar and can only send ether back to the owner.
|
||||||
|
*/
|
||||||
|
contract Deed {
|
||||||
|
|
||||||
|
address constant burn = 0xdead;
|
||||||
|
|
||||||
|
address public registrar;
|
||||||
|
address public owner;
|
||||||
|
address public previousOwner;
|
||||||
|
|
||||||
|
uint public creationDate;
|
||||||
|
uint public value;
|
||||||
|
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
event OwnerChanged(address newOwner);
|
||||||
|
event DeedClosed();
|
||||||
|
|
||||||
|
modifier onlyRegistrar {
|
||||||
|
require(msg.sender == registrar);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyActive {
|
||||||
|
require(active);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Deed(address _owner) public payable {
|
||||||
|
owner = _owner;
|
||||||
|
registrar = msg.sender;
|
||||||
|
creationDate = now;
|
||||||
|
active = true;
|
||||||
|
value = msg.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOwner(address newOwner) public onlyRegistrar {
|
||||||
|
require(newOwner != 0);
|
||||||
|
previousOwner = owner; // This allows contracts to check who sent them the ownership
|
||||||
|
owner = newOwner;
|
||||||
|
OwnerChanged(newOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRegistrar(address newRegistrar) public onlyRegistrar {
|
||||||
|
registrar = newRegistrar;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBalance(uint newValue, bool throwOnFailure) public onlyRegistrar onlyActive {
|
||||||
|
// Check if it has enough balance to set the value
|
||||||
|
require(value >= newValue);
|
||||||
|
value = newValue;
|
||||||
|
// Send the difference to the owner
|
||||||
|
require(owner.send(this.balance - newValue) || !throwOnFailure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Close a deed and refund a specified fraction of the bid value
|
||||||
|
*
|
||||||
|
* @param refundRatio The amount*1/1000 to refund
|
||||||
|
*/
|
||||||
|
function closeDeed(uint refundRatio) public onlyRegistrar onlyActive {
|
||||||
|
active = false;
|
||||||
|
require(burn.send(((1000 - refundRatio) * this.balance)/1000));
|
||||||
|
DeedClosed();
|
||||||
|
destroyDeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Close a deed and refund a specified fraction of the bid value
|
||||||
|
*/
|
||||||
|
function destroyDeed() public {
|
||||||
|
require(!active);
|
||||||
|
|
||||||
|
// Instead of selfdestruct(owner), invoke owner fallback function to allow
|
||||||
|
// owner to log an event if desired; but owner should also be aware that
|
||||||
|
// its fallback function can also be invoked by setBalance
|
||||||
|
if (owner.send(this.balance)) {
|
||||||
|
selfdestruct(burn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
import "./ReverseRegistrar.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Provides a default implementation of a resolver for reverse records,
|
||||||
|
* which permits only the owner to update it.
|
||||||
|
*/
|
||||||
|
contract DefaultReverseResolver is Resolver {
|
||||||
|
// namehash('addr.reverse')
|
||||||
|
bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
|
||||||
|
|
||||||
|
ENS public ens;
|
||||||
|
mapping (bytes32 => string) public name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Only permits calls by the reverse registrar.
|
||||||
|
* @param node The node permission is required for.
|
||||||
|
*/
|
||||||
|
modifier owner_only(bytes32 node) {
|
||||||
|
require(msg.sender == ens.owner(node));
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Constructor
|
||||||
|
* @param ensAddr The address of the ENS registry.
|
||||||
|
*/
|
||||||
|
function DefaultReverseResolver(ENS ensAddr) public {
|
||||||
|
ens = ensAddr;
|
||||||
|
|
||||||
|
// Assign ownership of the reverse record to our deployer
|
||||||
|
ReverseRegistrar registrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE));
|
||||||
|
if (address(registrar) != 0) {
|
||||||
|
registrar.claim(msg.sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sets the name for a node.
|
||||||
|
* @param node The node to update.
|
||||||
|
* @param _name The name to set.
|
||||||
|
*/
|
||||||
|
function setName(bytes32 node, string _name) public owner_only(node) {
|
||||||
|
name[node] = _name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,535 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Temporary Hash Registrar
|
||||||
|
========================
|
||||||
|
|
||||||
|
This is a simplified version of a hash registrar. It is purporsefully limited:
|
||||||
|
names cannot be six letters or shorter, new auctions will stop after 4 years.
|
||||||
|
|
||||||
|
The plan is to test the basic features and then move to a new contract in at most
|
||||||
|
2 years, when some sort of renewal mechanism will be enabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import './ENS.sol';
|
||||||
|
import './Deed.sol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Registrar
|
||||||
|
* @dev The registrar handles the auction process for each subnode of the node it owns.
|
||||||
|
*/
|
||||||
|
contract Registrar {
|
||||||
|
ENS public ens;
|
||||||
|
bytes32 public rootNode;
|
||||||
|
|
||||||
|
mapping (bytes32 => Entry) _entries;
|
||||||
|
mapping (address => mapping (bytes32 => Deed)) public sealedBids;
|
||||||
|
|
||||||
|
enum Mode { Open, Auction, Owned, Forbidden, Reveal, NotYetAvailable }
|
||||||
|
|
||||||
|
uint32 constant totalAuctionLength = 5 days;
|
||||||
|
uint32 constant revealPeriod = 48 hours;
|
||||||
|
uint32 public constant launchLength = 8 weeks;
|
||||||
|
|
||||||
|
uint constant minPrice = 0.01 ether;
|
||||||
|
uint public registryStarted;
|
||||||
|
|
||||||
|
event AuctionStarted(bytes32 indexed hash, uint registrationDate);
|
||||||
|
event NewBid(bytes32 indexed hash, address indexed bidder, uint deposit);
|
||||||
|
event BidRevealed(bytes32 indexed hash, address indexed owner, uint value, uint8 status);
|
||||||
|
event HashRegistered(bytes32 indexed hash, address indexed owner, uint value, uint registrationDate);
|
||||||
|
event HashReleased(bytes32 indexed hash, uint value);
|
||||||
|
event HashInvalidated(bytes32 indexed hash, string indexed name, uint value, uint registrationDate);
|
||||||
|
|
||||||
|
struct Entry {
|
||||||
|
Deed deed;
|
||||||
|
uint registrationDate;
|
||||||
|
uint value;
|
||||||
|
uint highestBid;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier inState(bytes32 _hash, Mode _state) {
|
||||||
|
require(state(_hash) == _state);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyOwner(bytes32 _hash) {
|
||||||
|
require(state(_hash) == Mode.Owned && msg.sender == _entries[_hash].deed.owner());
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier registryOpen() {
|
||||||
|
require(now >= registryStarted && now <= registryStarted + 4 years && ens.owner(rootNode) == address(this));
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Constructs a new Registrar, with the provided address as the owner of the root node.
|
||||||
|
*
|
||||||
|
* @param _ens The address of the ENS
|
||||||
|
* @param _rootNode The hash of the rootnode.
|
||||||
|
*/
|
||||||
|
function Registrar(ENS _ens, bytes32 _rootNode, uint _startDate) public {
|
||||||
|
ens = _ens;
|
||||||
|
rootNode = _rootNode;
|
||||||
|
registryStarted = _startDate > 0 ? _startDate : now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Start an auction for an available hash
|
||||||
|
*
|
||||||
|
* @param _hash The hash to start an auction on
|
||||||
|
*/
|
||||||
|
function startAuction(bytes32 _hash) public registryOpen() {
|
||||||
|
Mode mode = state(_hash);
|
||||||
|
if (mode == Mode.Auction) return;
|
||||||
|
require(mode == Mode.Open);
|
||||||
|
|
||||||
|
Entry storage newAuction = _entries[_hash];
|
||||||
|
newAuction.registrationDate = now + totalAuctionLength;
|
||||||
|
newAuction.value = 0;
|
||||||
|
newAuction.highestBid = 0;
|
||||||
|
AuctionStarted(_hash, newAuction.registrationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Start multiple auctions for better anonymity
|
||||||
|
*
|
||||||
|
* Anyone can start an auction by sending an array of hashes that they want to bid for.
|
||||||
|
* Arrays are sent so that someone can open up an auction for X dummy hashes when they
|
||||||
|
* are only really interested in bidding for one. This will increase the cost for an
|
||||||
|
* attacker to simply bid blindly on all new auctions. Dummy auctions that are
|
||||||
|
* open but not bid on are closed after a week.
|
||||||
|
*
|
||||||
|
* @param _hashes An array of hashes, at least one of which you presumably want to bid on
|
||||||
|
*/
|
||||||
|
function startAuctions(bytes32[] _hashes) public {
|
||||||
|
for (uint i = 0; i < _hashes.length; i ++) {
|
||||||
|
startAuction(_hashes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Submit a new sealed bid on a desired hash in a blind auction
|
||||||
|
*
|
||||||
|
* Bids are sent by sending a message to the main contract with a hash and an amount. The hash
|
||||||
|
* contains information about the bid, including the bidded hash, the bid amount, and a random
|
||||||
|
* salt. Bids are not tied to any one auction until they are revealed. The value of the bid
|
||||||
|
* itself can be masqueraded by sending more than the value of your actual bid. This is
|
||||||
|
* followed by a 48h reveal period. Bids revealed after this period will be burned and the ether unrecoverable.
|
||||||
|
* Since this is an auction, it is expected that most public hashes, like known domains and common dictionary
|
||||||
|
* words, will have multiple bidders pushing the price up.
|
||||||
|
*
|
||||||
|
* @param sealedBid A sealedBid, created by the shaBid function
|
||||||
|
*/
|
||||||
|
function newBid(bytes32 sealedBid) public payable {
|
||||||
|
require(address(sealedBids[msg.sender][sealedBid]) == 0x0);
|
||||||
|
require(msg.value >= minPrice);
|
||||||
|
|
||||||
|
// Creates a new hash contract with the owner
|
||||||
|
Deed newBid = (new Deed).value(msg.value)(msg.sender);
|
||||||
|
sealedBids[msg.sender][sealedBid] = newBid;
|
||||||
|
NewBid(sealedBid, msg.sender, msg.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Start a set of auctions and bid on one of them
|
||||||
|
*
|
||||||
|
* This method functions identically to calling `startAuctions` followed by `newBid`,
|
||||||
|
* but all in one transaction.
|
||||||
|
*
|
||||||
|
* @param hashes A list of hashes to start auctions on.
|
||||||
|
* @param sealedBid A sealed bid for one of the auctions.
|
||||||
|
*/
|
||||||
|
function startAuctionsAndBid(bytes32[] hashes, bytes32 sealedBid) public payable {
|
||||||
|
startAuctions(hashes);
|
||||||
|
newBid(sealedBid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Submit the properties of a bid to reveal them
|
||||||
|
*
|
||||||
|
* @param _hash The node in the sealedBid
|
||||||
|
* @param _value The bid amount in the sealedBid
|
||||||
|
* @param _salt The sale in the sealedBid
|
||||||
|
*/
|
||||||
|
function unsealBid(bytes32 _hash, uint _value, bytes32 _salt) public {
|
||||||
|
bytes32 seal = shaBid(_hash, msg.sender, _value, _salt);
|
||||||
|
Deed bid = sealedBids[msg.sender][seal];
|
||||||
|
require(address(bid) != 0);
|
||||||
|
|
||||||
|
sealedBids[msg.sender][seal] = Deed(0);
|
||||||
|
Entry storage h = _entries[_hash];
|
||||||
|
uint value = min(_value, bid.value());
|
||||||
|
bid.setBalance(value, true);
|
||||||
|
|
||||||
|
var auctionState = state(_hash);
|
||||||
|
if (auctionState == Mode.Owned) {
|
||||||
|
// Too late! Bidder loses their bid. Gets 0.5% back.
|
||||||
|
bid.closeDeed(5);
|
||||||
|
BidRevealed(_hash, msg.sender, value, 1);
|
||||||
|
} else if (auctionState != Mode.Reveal) {
|
||||||
|
// Invalid phase
|
||||||
|
revert();
|
||||||
|
} else if (value < minPrice || bid.creationDate() > h.registrationDate - revealPeriod) {
|
||||||
|
// Bid too low or too late, refund 99.5%
|
||||||
|
bid.closeDeed(995);
|
||||||
|
BidRevealed(_hash, msg.sender, value, 0);
|
||||||
|
} else if (value > h.highestBid) {
|
||||||
|
// New winner
|
||||||
|
// Cancel the other bid, refund 99.5%
|
||||||
|
if (address(h.deed) != 0) {
|
||||||
|
Deed previousWinner = h.deed;
|
||||||
|
previousWinner.closeDeed(995);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new winner
|
||||||
|
// Per the rules of a vickery auction, the value becomes the previous highestBid
|
||||||
|
h.value = h.highestBid; // will be zero if there's only 1 bidder
|
||||||
|
h.highestBid = value;
|
||||||
|
h.deed = bid;
|
||||||
|
BidRevealed(_hash, msg.sender, value, 2);
|
||||||
|
} else if (value > h.value) {
|
||||||
|
// Not winner, but affects second place
|
||||||
|
h.value = value;
|
||||||
|
bid.closeDeed(995);
|
||||||
|
BidRevealed(_hash, msg.sender, value, 3);
|
||||||
|
} else {
|
||||||
|
// Bid doesn't affect auction
|
||||||
|
bid.closeDeed(995);
|
||||||
|
BidRevealed(_hash, msg.sender, value, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Cancel a bid
|
||||||
|
*
|
||||||
|
* @param seal The value returned by the shaBid function
|
||||||
|
*/
|
||||||
|
function cancelBid(address bidder, bytes32 seal) public {
|
||||||
|
Deed bid = sealedBids[bidder][seal];
|
||||||
|
|
||||||
|
// If a sole bidder does not `unsealBid` in time, they have a few more days
|
||||||
|
// where they can call `startAuction` (again) and then `unsealBid` during
|
||||||
|
// the revealPeriod to get back their bid value.
|
||||||
|
// For simplicity, they should call `startAuction` within
|
||||||
|
// 9 days (2 weeks - totalAuctionLength), otherwise their bid will be
|
||||||
|
// cancellable by anyone.
|
||||||
|
require(address(bid) != 0 && now >= bid.creationDate() + totalAuctionLength + 2 weeks);
|
||||||
|
|
||||||
|
// Send the canceller 0.5% of the bid, and burn the rest.
|
||||||
|
bid.setOwner(msg.sender);
|
||||||
|
bid.closeDeed(5);
|
||||||
|
sealedBids[bidder][seal] = Deed(0);
|
||||||
|
BidRevealed(seal, bidder, 0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Finalize an auction after the registration date has passed
|
||||||
|
*
|
||||||
|
* @param _hash The hash of the name the auction is for
|
||||||
|
*/
|
||||||
|
function finalizeAuction(bytes32 _hash) public onlyOwner(_hash) {
|
||||||
|
Entry storage h = _entries[_hash];
|
||||||
|
|
||||||
|
// Handles the case when there's only a single bidder (h.value is zero)
|
||||||
|
h.value = max(h.value, minPrice);
|
||||||
|
h.deed.setBalance(h.value, true);
|
||||||
|
|
||||||
|
trySetSubnodeOwner(_hash, h.deed.owner());
|
||||||
|
HashRegistered(_hash, h.deed.owner(), h.value, h.registrationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev The owner of a domain may transfer it to someone else at any time.
|
||||||
|
*
|
||||||
|
* @param _hash The node to transfer
|
||||||
|
* @param newOwner The address to transfer ownership to
|
||||||
|
*/
|
||||||
|
function transfer(bytes32 _hash, address newOwner) public onlyOwner(_hash) {
|
||||||
|
require(newOwner != 0);
|
||||||
|
|
||||||
|
Entry storage h = _entries[_hash];
|
||||||
|
h.deed.setOwner(newOwner);
|
||||||
|
trySetSubnodeOwner(_hash, newOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev After some time, or if we're no longer the registrar, the owner can release
|
||||||
|
* the name and get their ether back.
|
||||||
|
*
|
||||||
|
* @param _hash The node to release
|
||||||
|
*/
|
||||||
|
function releaseDeed(bytes32 _hash) public onlyOwner(_hash) {
|
||||||
|
Entry storage h = _entries[_hash];
|
||||||
|
Deed deedContract = h.deed;
|
||||||
|
|
||||||
|
require(now >= h.registrationDate + 1 years || ens.owner(rootNode) != address(this));
|
||||||
|
|
||||||
|
h.value = 0;
|
||||||
|
h.highestBid = 0;
|
||||||
|
h.deed = Deed(0);
|
||||||
|
|
||||||
|
_tryEraseSingleNode(_hash);
|
||||||
|
deedContract.closeDeed(1000);
|
||||||
|
HashReleased(_hash, h.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Submit a name 6 characters long or less. If it has been registered,
|
||||||
|
* the submitter will earn 50% of the deed value.
|
||||||
|
*
|
||||||
|
* We are purposefully handicapping the simplified registrar as a way
|
||||||
|
* to force it into being restructured in a few years.
|
||||||
|
*
|
||||||
|
* @param unhashedName An invalid name to search for in the registry.
|
||||||
|
*/
|
||||||
|
function invalidateName(string unhashedName) public inState(keccak256(unhashedName), Mode.Owned) {
|
||||||
|
require(strlen(unhashedName) <= 6);
|
||||||
|
bytes32 hash = keccak256(unhashedName);
|
||||||
|
|
||||||
|
Entry storage h = _entries[hash];
|
||||||
|
|
||||||
|
_tryEraseSingleNode(hash);
|
||||||
|
|
||||||
|
if (address(h.deed) != 0) {
|
||||||
|
// Reward the discoverer with 50% of the deed
|
||||||
|
// The previous owner gets 50%
|
||||||
|
h.value = max(h.value, minPrice);
|
||||||
|
h.deed.setBalance(h.value/2, false);
|
||||||
|
h.deed.setOwner(msg.sender);
|
||||||
|
h.deed.closeDeed(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashInvalidated(hash, unhashedName, h.value, h.registrationDate);
|
||||||
|
|
||||||
|
h.value = 0;
|
||||||
|
h.highestBid = 0;
|
||||||
|
h.deed = Deed(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Allows anyone to delete the owner and resolver records for a (subdomain of) a
|
||||||
|
* name that is not currently owned in the registrar. If passing, eg, 'foo.bar.eth',
|
||||||
|
* the owner and resolver fields on 'foo.bar.eth' and 'bar.eth' will all be cleared.
|
||||||
|
*
|
||||||
|
* @param labels A series of label hashes identifying the name to zero out, rooted at the
|
||||||
|
* registrar's root. Must contain at least one element. For instance, to zero
|
||||||
|
* 'foo.bar.eth' on a registrar that owns '.eth', pass an array containing
|
||||||
|
* [keccak256('foo'), keccak256('bar')].
|
||||||
|
*/
|
||||||
|
function eraseNode(bytes32[] labels) public {
|
||||||
|
require(labels.length != 0);
|
||||||
|
require(state(labels[labels.length - 1]) != Mode.Owned);
|
||||||
|
|
||||||
|
_eraseNodeHierarchy(labels.length - 1, labels, rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfers the deed to the current registrar, if different from this one.
|
||||||
|
*
|
||||||
|
* Used during the upgrade process to a permanent registrar.
|
||||||
|
*
|
||||||
|
* @param _hash The name hash to transfer.
|
||||||
|
*/
|
||||||
|
function transferRegistrars(bytes32 _hash) public onlyOwner(_hash) {
|
||||||
|
address registrar = ens.owner(rootNode);
|
||||||
|
require(registrar != address(this));
|
||||||
|
|
||||||
|
// Migrate the deed
|
||||||
|
Entry storage h = _entries[_hash];
|
||||||
|
h.deed.setRegistrar(registrar);
|
||||||
|
|
||||||
|
// Call the new registrar to accept the transfer
|
||||||
|
Registrar(registrar).acceptRegistrarTransfer(_hash, h.deed, h.registrationDate);
|
||||||
|
|
||||||
|
// Zero out the Entry
|
||||||
|
h.deed = Deed(0);
|
||||||
|
h.registrationDate = 0;
|
||||||
|
h.value = 0;
|
||||||
|
h.highestBid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Accepts a transfer from a previous registrar; stubbed out here since there
|
||||||
|
* is no previous registrar implementing this interface.
|
||||||
|
*
|
||||||
|
* @param hash The sha3 hash of the label to transfer.
|
||||||
|
* @param deed The Deed object for the name being transferred in.
|
||||||
|
* @param registrationDate The date at which the name was originally registered.
|
||||||
|
*/
|
||||||
|
function acceptRegistrarTransfer(bytes32 hash, Deed deed, uint registrationDate) public {
|
||||||
|
hash; deed; registrationDate; // Don't warn about unused variables
|
||||||
|
}
|
||||||
|
|
||||||
|
// State transitions for names:
|
||||||
|
// Open -> Auction (startAuction)
|
||||||
|
// Auction -> Reveal
|
||||||
|
// Reveal -> Owned
|
||||||
|
// Reveal -> Open (if nobody bid)
|
||||||
|
// Owned -> Open (releaseDeed or invalidateName)
|
||||||
|
function state(bytes32 _hash) public view returns (Mode) {
|
||||||
|
Entry storage entry = _entries[_hash];
|
||||||
|
|
||||||
|
if (!isAllowed(_hash, now)) {
|
||||||
|
return Mode.NotYetAvailable;
|
||||||
|
} else if (now < entry.registrationDate) {
|
||||||
|
if (now < entry.registrationDate - revealPeriod) {
|
||||||
|
return Mode.Auction;
|
||||||
|
} else {
|
||||||
|
return Mode.Reveal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (entry.highestBid == 0) {
|
||||||
|
return Mode.Open;
|
||||||
|
} else {
|
||||||
|
return Mode.Owned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function entries(bytes32 _hash) public view returns (Mode, address, uint, uint, uint) {
|
||||||
|
Entry storage h = _entries[_hash];
|
||||||
|
return (state(_hash), h.deed, h.registrationDate, h.value, h.highestBid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Determines if a name is available for registration yet
|
||||||
|
*
|
||||||
|
* Each name will be assigned a random date in which its auction
|
||||||
|
* can be started, from 0 to 8 weeks
|
||||||
|
*
|
||||||
|
* @param _hash The hash to start an auction on
|
||||||
|
* @param _timestamp The timestamp to query about
|
||||||
|
*/
|
||||||
|
function isAllowed(bytes32 _hash, uint _timestamp) public view returns (bool allowed) {
|
||||||
|
return _timestamp > getAllowedTime(_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns available date for hash
|
||||||
|
*
|
||||||
|
* The available time from the `registryStarted` for a hash is proportional
|
||||||
|
* to its numeric value.
|
||||||
|
*
|
||||||
|
* @param _hash The hash to start an auction on
|
||||||
|
*/
|
||||||
|
function getAllowedTime(bytes32 _hash) public view returns (uint) {
|
||||||
|
return registryStarted + ((launchLength * (uint(_hash) >> 128)) >> 128);
|
||||||
|
// Right shift operator: a >> b == a / 2**b
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Hash the values required for a secret bid
|
||||||
|
*
|
||||||
|
* @param hash The node corresponding to the desired namehash
|
||||||
|
* @param value The bid amount
|
||||||
|
* @param salt A random value to ensure secrecy of the bid
|
||||||
|
* @return The hash of the bid values
|
||||||
|
*/
|
||||||
|
function shaBid(bytes32 hash, address owner, uint value, bytes32 salt) public pure returns (bytes32) {
|
||||||
|
return keccak256(hash, owner, value, salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tryEraseSingleNode(bytes32 label) internal {
|
||||||
|
if (ens.owner(rootNode) == address(this)) {
|
||||||
|
ens.setSubnodeOwner(rootNode, label, address(this));
|
||||||
|
bytes32 node = keccak256(rootNode, label);
|
||||||
|
ens.setResolver(node, 0);
|
||||||
|
ens.setOwner(node, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _eraseNodeHierarchy(uint idx, bytes32[] labels, bytes32 node) internal {
|
||||||
|
// Take ownership of the node
|
||||||
|
ens.setSubnodeOwner(node, labels[idx], address(this));
|
||||||
|
node = keccak256(node, labels[idx]);
|
||||||
|
|
||||||
|
// Recurse if there are more labels
|
||||||
|
if (idx > 0) {
|
||||||
|
_eraseNodeHierarchy(idx - 1, labels, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase the resolver and owner records
|
||||||
|
ens.setResolver(node, 0);
|
||||||
|
ens.setOwner(node, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Assign the owner in ENS, if we're still the registrar
|
||||||
|
*
|
||||||
|
* @param _hash hash to change owner
|
||||||
|
* @param _newOwner new owner to transfer to
|
||||||
|
*/
|
||||||
|
function trySetSubnodeOwner(bytes32 _hash, address _newOwner) internal {
|
||||||
|
if (ens.owner(rootNode) == address(this))
|
||||||
|
ens.setSubnodeOwner(rootNode, _hash, _newOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the maximum of two unsigned integers
|
||||||
|
*
|
||||||
|
* @param a A number to compare
|
||||||
|
* @param b A number to compare
|
||||||
|
* @return The maximum of two unsigned integers
|
||||||
|
*/
|
||||||
|
function max(uint a, uint b) internal pure returns (uint) {
|
||||||
|
if (a > b)
|
||||||
|
return a;
|
||||||
|
else
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the minimum of two unsigned integers
|
||||||
|
*
|
||||||
|
* @param a A number to compare
|
||||||
|
* @param b A number to compare
|
||||||
|
* @return The minimum of two unsigned integers
|
||||||
|
*/
|
||||||
|
function min(uint a, uint b) internal pure returns (uint) {
|
||||||
|
if (a < b)
|
||||||
|
return a;
|
||||||
|
else
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the length of a given string
|
||||||
|
*
|
||||||
|
* @param s The string to measure the length of
|
||||||
|
* @return The length of the input string
|
||||||
|
*/
|
||||||
|
function strlen(string s) internal pure returns (uint) {
|
||||||
|
s; // Don't warn about unused variables
|
||||||
|
// Starting here means the LSB will be the byte we care about
|
||||||
|
uint ptr;
|
||||||
|
uint end;
|
||||||
|
assembly {
|
||||||
|
ptr := add(s, 1)
|
||||||
|
end := add(mload(s), ptr)
|
||||||
|
}
|
||||||
|
for (uint len = 0; ptr < end; len++) {
|
||||||
|
uint8 b;
|
||||||
|
assembly { b := and(mload(ptr), 0xFF) }
|
||||||
|
if (b < 0x80) {
|
||||||
|
ptr += 1;
|
||||||
|
} else if (b < 0xE0) {
|
||||||
|
ptr += 2;
|
||||||
|
} else if (b < 0xF0) {
|
||||||
|
ptr += 3;
|
||||||
|
} else if (b < 0xF8) {
|
||||||
|
ptr += 4;
|
||||||
|
} else if (b < 0xFC) {
|
||||||
|
ptr += 5;
|
||||||
|
} else {
|
||||||
|
ptr += 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
import './ENS.sol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple resolver anyone can use; only allows the owner of a node to set its
|
||||||
|
* address.
|
||||||
|
*/
|
||||||
|
contract PublicResolver {
|
||||||
|
|
||||||
|
bytes4 constant INTERFACE_META_ID = 0x01ffc9a7;
|
||||||
|
bytes4 constant ADDR_INTERFACE_ID = 0x3b3b57de;
|
||||||
|
bytes4 constant CONTENT_INTERFACE_ID = 0xd8389dc5;
|
||||||
|
bytes4 constant NAME_INTERFACE_ID = 0x691f3431;
|
||||||
|
bytes4 constant ABI_INTERFACE_ID = 0x2203ab56;
|
||||||
|
bytes4 constant PUBKEY_INTERFACE_ID = 0xc8690233;
|
||||||
|
bytes4 constant TEXT_INTERFACE_ID = 0x59d1d43c;
|
||||||
|
bytes4 constant MULTIHASH_INTERFACE_ID = 0xe89401a1;
|
||||||
|
|
||||||
|
event AddrChanged(bytes32 indexed node, address a);
|
||||||
|
event ContentChanged(bytes32 indexed node, bytes32 hash);
|
||||||
|
event NameChanged(bytes32 indexed node, string name);
|
||||||
|
event ABIChanged(bytes32 indexed node, uint256 indexed contentType);
|
||||||
|
event PubkeyChanged(bytes32 indexed node, bytes32 x, bytes32 y);
|
||||||
|
event TextChanged(bytes32 indexed node, string indexedKey, string key);
|
||||||
|
event MultihashChanged(bytes32 indexed node, bytes hash);
|
||||||
|
|
||||||
|
struct PublicKey {
|
||||||
|
bytes32 x;
|
||||||
|
bytes32 y;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Record {
|
||||||
|
address addr;
|
||||||
|
bytes32 content;
|
||||||
|
string name;
|
||||||
|
PublicKey pubkey;
|
||||||
|
mapping(string=>string) text;
|
||||||
|
mapping(uint256=>bytes) abis;
|
||||||
|
bytes multihash;
|
||||||
|
}
|
||||||
|
|
||||||
|
ENS ens;
|
||||||
|
|
||||||
|
mapping (bytes32 => Record) records;
|
||||||
|
|
||||||
|
modifier only_owner(bytes32 node) {
|
||||||
|
require(ens.owner(node) == msg.sender);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param ensAddr The ENS registrar contract.
|
||||||
|
*/
|
||||||
|
function PublicResolver(ENS ensAddr) public {
|
||||||
|
ens = ensAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the address associated with an ENS node.
|
||||||
|
* May only be called by the owner of that node in the ENS registry.
|
||||||
|
* @param node The node to update.
|
||||||
|
* @param addr The address to set.
|
||||||
|
*/
|
||||||
|
function setAddr(bytes32 node, address addr) public only_owner(node) {
|
||||||
|
records[node].addr = addr;
|
||||||
|
AddrChanged(node, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content hash associated with an ENS node.
|
||||||
|
* May only be called by the owner of that node in the ENS registry.
|
||||||
|
* Note that this resource type is not standardized, and will likely change
|
||||||
|
* in future to a resource type based on multihash.
|
||||||
|
* @param node The node to update.
|
||||||
|
* @param hash The content hash to set
|
||||||
|
*/
|
||||||
|
function setContent(bytes32 node, bytes32 hash) public only_owner(node) {
|
||||||
|
records[node].content = hash;
|
||||||
|
ContentChanged(node, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the multihash associated with an ENS node.
|
||||||
|
* May only be called by the owner of that node in the ENS registry.
|
||||||
|
* @param node The node to update.
|
||||||
|
* @param hash The multihash to set
|
||||||
|
*/
|
||||||
|
function setMultihash(bytes32 node, bytes hash) public only_owner(node) {
|
||||||
|
records[node].multihash = hash;
|
||||||
|
MultihashChanged(node, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 name) public only_owner(node) {
|
||||||
|
records[node].name = name;
|
||||||
|
NameChanged(node, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ABI associated with an ENS node.
|
||||||
|
* Nodes may have one ABI of each content type. To remove an ABI, set it to
|
||||||
|
* the empty string.
|
||||||
|
* @param node The node to update.
|
||||||
|
* @param contentType The content type of the ABI
|
||||||
|
* @param data The ABI data.
|
||||||
|
*/
|
||||||
|
function setABI(bytes32 node, uint256 contentType, bytes data) public only_owner(node) {
|
||||||
|
// Content types must be powers of 2
|
||||||
|
require(((contentType - 1) & contentType) == 0);
|
||||||
|
|
||||||
|
records[node].abis[contentType] = data;
|
||||||
|
ABIChanged(node, contentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the SECP256k1 public key associated with an ENS node.
|
||||||
|
* @param node The ENS node to query
|
||||||
|
* @param x the X coordinate of the curve point for the public key.
|
||||||
|
* @param y the Y coordinate of the curve point for the public key.
|
||||||
|
*/
|
||||||
|
function setPubkey(bytes32 node, bytes32 x, bytes32 y) public only_owner(node) {
|
||||||
|
records[node].pubkey = PublicKey(x, y);
|
||||||
|
PubkeyChanged(node, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text data associated with an ENS node and key.
|
||||||
|
* May only be called by the owner of that node in the ENS registry.
|
||||||
|
* @param node The node to update.
|
||||||
|
* @param key The key to set.
|
||||||
|
* @param value The text data value to set.
|
||||||
|
*/
|
||||||
|
function setText(bytes32 node, string key, string value) public only_owner(node) {
|
||||||
|
records[node].text[key] = value;
|
||||||
|
TextChanged(node, key, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text data associated with an ENS node and key.
|
||||||
|
* @param node The ENS node to query.
|
||||||
|
* @param key The text data key to query.
|
||||||
|
* @return The associated text data.
|
||||||
|
*/
|
||||||
|
function text(bytes32 node, string key) public view returns (string) {
|
||||||
|
return records[node].text[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the SECP256k1 public key associated with an ENS node.
|
||||||
|
* Defined in EIP 619.
|
||||||
|
* @param node The ENS node to query
|
||||||
|
* @return x, y the X and Y coordinates of the curve point for the public key.
|
||||||
|
*/
|
||||||
|
function pubkey(bytes32 node) public view returns (bytes32 x, bytes32 y) {
|
||||||
|
return (records[node].pubkey.x, records[node].pubkey.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ABI associated with an ENS node.
|
||||||
|
* Defined in EIP205.
|
||||||
|
* @param node The ENS node to query
|
||||||
|
* @param contentTypes A bitwise OR of the ABI formats accepted by the caller.
|
||||||
|
* @return contentType The content type of the return value
|
||||||
|
* @return data The ABI data
|
||||||
|
*/
|
||||||
|
function ABI(bytes32 node, uint256 contentTypes) public view returns (uint256 contentType, bytes data) {
|
||||||
|
Record storage record = records[node];
|
||||||
|
for (contentType = 1; contentType <= contentTypes; contentType <<= 1) {
|
||||||
|
if ((contentType & contentTypes) != 0 && record.abis[contentType].length > 0) {
|
||||||
|
data = record.abis[contentType];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentType = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) public view returns (string) {
|
||||||
|
return records[node].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content hash associated with an ENS node.
|
||||||
|
* Note that this resource type is not standardized, and will likely change
|
||||||
|
* in future to a resource type based on multihash.
|
||||||
|
* @param node The ENS node to query.
|
||||||
|
* @return The associated content hash.
|
||||||
|
*/
|
||||||
|
function content(bytes32 node) public view returns (bytes32) {
|
||||||
|
return records[node].content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the multihash associated with an ENS node.
|
||||||
|
* @param node The ENS node to query.
|
||||||
|
* @return The associated multihash.
|
||||||
|
*/
|
||||||
|
function multihash(bytes32 node) public view returns (bytes) {
|
||||||
|
return records[node].multihash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the address associated with an ENS node.
|
||||||
|
* @param node The ENS node to query.
|
||||||
|
* @return The associated address.
|
||||||
|
*/
|
||||||
|
function addr(bytes32 node) public view returns (address) {
|
||||||
|
return records[node].addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the resolver implements the interface specified by the provided hash.
|
||||||
|
* @param interfaceID The ID of the interface to check for.
|
||||||
|
* @return True if the contract implements the requested interface.
|
||||||
|
*/
|
||||||
|
function supportsInterface(bytes4 interfaceID) public pure returns (bool) {
|
||||||
|
return interfaceID == ADDR_INTERFACE_ID ||
|
||||||
|
interfaceID == CONTENT_INTERFACE_ID ||
|
||||||
|
interfaceID == NAME_INTERFACE_ID ||
|
||||||
|
interfaceID == ABI_INTERFACE_ID ||
|
||||||
|
interfaceID == PUBKEY_INTERFACE_ID ||
|
||||||
|
interfaceID == TEXT_INTERFACE_ID ||
|
||||||
|
interfaceID == MULTIHASH_INTERFACE_ID ||
|
||||||
|
interfaceID == INTERFACE_META_ID;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
contract ResolverInterface {
|
||||||
|
function PublicResolver(address ensAddr) public;
|
||||||
|
function setAddr(bytes32 node, address addr) public;
|
||||||
|
function setHash(bytes32 node, bytes32 hash) public;
|
||||||
|
function addr(bytes32 node) public view returns (address);
|
||||||
|
function hash(bytes32 node) public view returns (bytes32);
|
||||||
|
function supportsInterface(bytes4 interfaceID) public pure returns (bool);
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
pragma solidity ^0.4.10;
|
||||||
|
|
||||||
|
import "./ENS.sol";
|
||||||
|
|
||||||
|
contract Resolver {
|
||||||
|
function setName(bytes32 node, string name) public;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ReverseRegistrar {
|
||||||
|
// namehash('addr.reverse')
|
||||||
|
bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
|
||||||
|
|
||||||
|
ENS public ens;
|
||||||
|
Resolver public defaultResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Constructor
|
||||||
|
* @param ensAddr The address of the ENS registry.
|
||||||
|
* @param resolverAddr The address of the default reverse resolver.
|
||||||
|
*/
|
||||||
|
function ReverseRegistrar(ENS ensAddr, Resolver resolverAddr) public {
|
||||||
|
ens = ensAddr;
|
||||||
|
defaultResolver = resolverAddr;
|
||||||
|
|
||||||
|
// Assign ownership of the reverse record to our deployer
|
||||||
|
ReverseRegistrar oldRegistrar = ReverseRegistrar(ens.owner(ADDR_REVERSE_NODE));
|
||||||
|
if (address(oldRegistrar) != 0) {
|
||||||
|
oldRegistrar.claim(msg.sender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfers ownership of the reverse ENS record associated with the
|
||||||
|
* calling account.
|
||||||
|
* @param owner The address to set as the owner of the reverse record in ENS.
|
||||||
|
* @return The ENS node hash of the reverse record.
|
||||||
|
*/
|
||||||
|
function claim(address owner) public returns (bytes32) {
|
||||||
|
return claimWithResolver(owner, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Transfers ownership of the reverse ENS record associated with the
|
||||||
|
* calling account.
|
||||||
|
* @param owner The address to set as the owner of the reverse record in ENS.
|
||||||
|
* @param resolver The address of the resolver to set; 0 to leave unchanged.
|
||||||
|
* @return The ENS node hash of the reverse record.
|
||||||
|
*/
|
||||||
|
function claimWithResolver(address owner, address resolver) public returns (bytes32) {
|
||||||
|
var label = sha3HexAddress(msg.sender);
|
||||||
|
bytes32 node = keccak256(ADDR_REVERSE_NODE, label);
|
||||||
|
var currentOwner = ens.owner(node);
|
||||||
|
|
||||||
|
// Update the resolver if required
|
||||||
|
if (resolver != 0 && resolver != ens.resolver(node)) {
|
||||||
|
// Transfer the name to us first if it's not already
|
||||||
|
if (currentOwner != address(this)) {
|
||||||
|
ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, this);
|
||||||
|
currentOwner = address(this);
|
||||||
|
}
|
||||||
|
ens.setResolver(node, resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the owner if required
|
||||||
|
if (currentOwner != owner) {
|
||||||
|
ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sets the `name()` record for the reverse ENS record associated with
|
||||||
|
* the calling account. First updates the resolver to the default reverse
|
||||||
|
* resolver if necessary.
|
||||||
|
* @param name The name to set for this address.
|
||||||
|
* @return The ENS node hash of the reverse record.
|
||||||
|
*/
|
||||||
|
function setName(string name) public returns (bytes32) {
|
||||||
|
bytes32 node = claimWithResolver(this, defaultResolver);
|
||||||
|
defaultResolver.setName(node, name);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the node hash for a given account's reverse records.
|
||||||
|
* @param addr The address to hash
|
||||||
|
* @return The ENS node hash.
|
||||||
|
*/
|
||||||
|
function node(address addr) public view returns (bytes32) {
|
||||||
|
return keccak256(ADDR_REVERSE_NODE, sha3HexAddress(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev An optimised function to compute the sha3 of the lower-case
|
||||||
|
* hexadecimal representation of an Ethereum address.
|
||||||
|
* @param addr The address to hash
|
||||||
|
* @return The SHA3 hash of the lower-case hexadecimal encoding of the
|
||||||
|
* input address.
|
||||||
|
*/
|
||||||
|
function sha3HexAddress(address addr) private returns (bytes32 ret) {
|
||||||
|
addr; ret; // Stop warning us about unused variables
|
||||||
|
assembly {
|
||||||
|
let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000
|
||||||
|
let i := 40
|
||||||
|
loop:
|
||||||
|
i := sub(i, 1)
|
||||||
|
mstore8(i, byte(and(addr, 0xf), lookup))
|
||||||
|
addr := div(addr, 0x10)
|
||||||
|
i := sub(i, 1)
|
||||||
|
mstore8(i, byte(and(addr, 0xf), lookup))
|
||||||
|
addr := div(addr, 0x10)
|
||||||
|
jumpi(loop, i)
|
||||||
|
ret := keccak256(0, 40)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
pragma solidity ^0.4.18;
|
||||||
|
|
||||||
|
import './ENS.sol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A registrar that allocates subdomains to the first person to claim them, but
|
||||||
|
* expires registrations a fixed period after they're initially claimed.
|
||||||
|
*/
|
||||||
|
contract TestRegistrar {
|
||||||
|
uint constant registrationPeriod = 4 weeks;
|
||||||
|
|
||||||
|
ENS public ens;
|
||||||
|
bytes32 public rootNode;
|
||||||
|
mapping (bytes32 => uint) public expiryTimes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param ensAddr The address of the ENS registry.
|
||||||
|
* @param node The node that this registrar administers.
|
||||||
|
*/
|
||||||
|
function TestRegistrar(ENS ensAddr, bytes32 node) public {
|
||||||
|
ens = ensAddr;
|
||||||
|
rootNode = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a name that's not currently registered
|
||||||
|
* @param subnode The hash of the label to register.
|
||||||
|
* @param owner The address of the new owner.
|
||||||
|
*/
|
||||||
|
function register(bytes32 subnode, address owner) public {
|
||||||
|
require(expiryTimes[subnode] < now);
|
||||||
|
|
||||||
|
expiryTimes[subnode] = now + registrationPeriod;
|
||||||
|
ens.setSubnodeOwner(rootNode, subnode, owner);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue