ERC-181 ENS support for reverse resolution of Ethereum addresses
Final · Names & metadata · created 2016-12-01
Specifies how ENS performs reverse resolution, finding a human-readable name from an Ethereum address. Only the owner of the address can register this name, so whoever looks it up can trust it.
Why it is needed
Name services are usually used for forward resolution, which finds the machine-readable address from a human-readable name. But the opposite direction is often needed too. An app that watches accounts is easier to read if it shows a name even for an account that was first added by address. If extra information such as a description is attached to an address, that information can be retrieved however the address came to be known. Trust is the more important issue. Anyone can set their own name to point to someone else's address, so forward records alone cannot tell which name is the real name of that address. Only the address owner can create a reverse record, so it becomes a way for the owner to declare directly which name represents the address.
What to implement
function claim(address owner) returns (bytes32 node): Transfers ownership of the calling account's reverse name to the specified owner and returns the namehash of that name.function claimWithResolver(address owner, address resolver) returns (bytes32 node): Sets the resolver on the calling account's reverse name, then transfers ownership to owner and returns the namehash.function setName(string name) returns (bytes32 node): Sets the default resolver on the calling account's reverse name and writes the name record to that resolver, creating the reverse record in a single transaction.function name(bytes32 node) constant returns (string);: Returns the ENS name of the node the resolver was asked about. If there is no name, it returns an empty string.
In depth
Where reverse records are stored: addr.reverse
Reverse records are stored in the ENS hierarchy in the same way as ordinary ENS records, under the reserved domain addr.reverse. The reverse name of an account is formed by converting the account address to lowercase hex and appending addr.reverse. In the original text's example, the reverse record for the ENS registry address 0x112234455c3a32fd11230c42e7bccd4a84e02010 is stored at 112234455c3a32fd11230c42e7bccd4a84e02010.addr.reverse. Therefore, to perform a reverse lookup of an address dynamically inside a contract, the contract has to do the hex encoding itself. The sha3HexAddress function in the appendix implementation handles this conversion and the hash calculation in one pass using assembly.
The registrar and claim(address owner)
The owner of the addr.reverse domain is the registrar contract, and it lets a caller take ownership of the reverse record for their own address. When account x calls claim(owner), the registrar instructs the ENS registry to transfer ownership of the name hex(x) + '.addr.reverse' to owner, and returns the namehash of the record it handed over. Allowing an address other than the caller to be specified as owner is a design choice made for contracts. A contract that needs an accurate reverse record can delegate management of the record to its deployer by putting just the single line reverseRegistrar.claim(msg.sender) in its constructor. In the appendix implementation, claim is equivalent to calling claimWithResolver(owner, 0).
claimWithResolver and setName
claimWithResolver(owner, resolver) first sets the specified resolver on the name hex(x) + '.addr.reverse' of account x, then transfers ownership to owner and returns the namehash. It exists to set a custom resolver and an owner together with fewer transactions than using claim alone. setName(name) sets the default resolver on the reverse name of account x and writes the name record to that resolver. It lets a user create a simple reverse record in a single transaction. In the appendix implementation, setName calls claimWithResolver(this, defaultResolver) so that the registrar itself holds ownership of the name, and then calls defaultResolver.setName(node, name). In other words, the ENS owner of a record created through setName is the registrar, not the caller.
Order of operations in the appendix implementation of claimWithResolver
The appendix implementation of claimWithResolver first builds the label with sha3HexAddress(msg.sender) and computes the node with sha3(ADDR_REVERSE_NODE, label). If resolver is nonzero and differs from the current resolver, then when the name's owner is not the registrar itself, it first takes ownership for itself with ens.setSubnodeOwner and then calls ens.setResolver. Finally, if the current owner differs from the requested owner, it transfers ownership with ens.setSubnodeOwner. If resolver is given as 0, it leaves the resolver as it is and changes only the ownership. ADDR_REVERSE_NODE is a constant with the value of namehash('addr.reverse'), 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2, and the node(address addr) function can compute the reverse node hash for any address.
The resolver interface name(bytes32)
Defines a new resolver interface for reverse records. It has a single function, name(bytes32 node), which returns a string. A resolver that implements this interface must return a valid ENS name for the requested node, or return an empty string if no name is defined for that node. The ID of this interface is 0x691f3431. The original text says that other record types suited to reverse records can be specified in later EIPs.
Owner check in the default resolver
The appendix's DefaultReverseResolver stores the name for each node in mapping(bytes32=>string) public name. setName(bytes32 node, string _name) carries the owner_only(node) modifier, so it runs only when msg.sender equals ens.owner(node). That is, only the address recorded as the owner of that node in the ENS registry can change the name. The registrar's setName first takes ownership for itself and then calls the default resolver precisely to pass this check. The code is written for pragma solidity ^0.4.10.