eip-181.md
1 --- 2 eip: 181 3 title: ENS support for reverse resolution of Ethereum addresses 4 author: Nick Johnson <arachnid@notdot.net> 5 status: Final 6 type: Standards Track 7 category: ERC 8 created: 2016-12-01 9 --- 10 11 # Abstract 12 This EIP specifies a TLD, registrar, and resolver interface for reverse resolution of Ethereum addresses using ENS. This permits associating a human-readable name with any Ethereum blockchain address. Resolvers can be certain that the reverse record was published by the owner of the Ethereum address in question. 13 14 # Motivation 15 While name services are mostly used for forward resolution - going from human-readable identifiers to machine-readable ones - there are many use-cases in which reverse resolution is useful as well: 16 17 - Applications that allow users to monitor accounts benefit from showing the name of an account instead of its address, even if it was originally added by address. 18 - Attaching metadata such as descriptive information to an address allows retrieving this information regardless of how the address was originally discovered. 19 - Anyone can configure a name to resolve to an address, regardless of ownership of that address. Reverse records allow the owner of an address to claim a name as authoritative for that address. 20 21 # Specification 22 Reverse ENS records are stored in the ENS hierarchy in the same fashion as regular records, under a reserved domain, `addr.reverse`. To generate the ENS name for a given account's reverse records, convert the account to hexadecimal representation in lower-case, and append `addr.reverse`. For instance, the ENS registry's address at `0x112234455c3a32fd11230c42e7bccd4a84e02010` has any reverse records stored at `112234455c3a32fd11230c42e7bccd4a84e02010.addr.reverse`. 23 24 Note that this means that contracts wanting to do dynamic reverse resolution of addresses will need to perform hex encoding in the contract. 25 26 ## Registrar 27 The owner of the `addr.reverse` domain will be a registrar that permits the caller to take ownership of 28 the reverse record for their own address. It provides the following methods: 29 30 ### function claim(address owner) returns (bytes32 node) 31 32 When called by account `x`, instructs the ENS registry to transfer ownership of the name `hex(x) + '.addr.reverse'` to the provided address, and return the namehash of the ENS record thus transferred. 33 34 Allowing the caller to specify an owner other than themselves for the relevant node facilitates contracts that need accurate reverse ENS entries delegating this to their creators with a minimum of code inside their constructor: 35 36 reverseRegistrar.claim(msg.sender) 37 38 ### function claimWithResolver(address owner, address resolver) returns (bytes32 node) 39 40 When called by account `x`, instructs the ENS registry to set the resolver of the name `hex(x) + '.addr.reverse'` to the specified resolver, then transfer ownership of the name to the provided address, and return the namehash of the ENS record thus transferred. This method facilitates setting up a custom resolver and owner in fewer transactions than would be required if calling `claim`. 41 42 ### function setName(string name) returns (bytes32 node) 43 44 When called by account `x`, sets the resolver for the name `hex(x) + '.addr.reverse'` to a default resolver, and sets the name record on that name to the specified name. This method facilitates setting up simple reverse records for users in a single transaction. 45 46 ## Resolver interface 47 A new resolver interface is defined, consisting of the following method: 48 49 function name(bytes32 node) constant returns (string); 50 51 Resolvers that implement this interface must return a valid ENS name for the requested node, or the empty string if no name is defined for the requested node. 52 53 The interface ID of this interface is 0x691f3431. 54 55 Future EIPs may specify more record types appropriate to reverse ENS records. 56 57 # Appendix 1: Registrar implementation 58 59 This registrar, written in Solidity, implements the specifications outlined above. 60 61 pragma solidity ^0.4.10; 62 63 import "./AbstractENS.sol"; 64 65 contract Resolver { 66 function setName(bytes32 node, string name) public; 67 } 68 69 /** 70 * @dev Provides a default implementation of a resolver for reverse records, 71 * which permits only the owner to update it. 72 */ 73 contract DefaultReverseResolver is Resolver { 74 AbstractENS public ens; 75 mapping(bytes32=>string) public name; 76 77 /** 78 * @dev Constructor 79 * @param ensAddr The address of the ENS registry. 80 */ 81 function DefaultReverseResolver(AbstractENS ensAddr) { 82 ens = ensAddr; 83 } 84 85 /** 86 * @dev Only permits calls by the reverse registrar. 87 * @param node The node permission is required for. 88 */ 89 modifier owner_only(bytes32 node) { 90 require(msg.sender == ens.owner(node)); 91 _; 92 } 93 94 /** 95 * @dev Sets the name for a node. 96 * @param node The node to update. 97 * @param _name The name to set. 98 */ 99 function setName(bytes32 node, string _name) public owner_only(node) { 100 name[node] = _name; 101 } 102 } 103 104 contract ReverseRegistrar { 105 // namehash('addr.reverse') 106 bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; 107 108 AbstractENS public ens; 109 Resolver public defaultResolver; 110 111 /** 112 * @dev Constructor 113 * @param ensAddr The address of the ENS registry. 114 * @param resolverAddr The address of the default reverse resolver. 115 */ 116 function ReverseRegistrar(AbstractENS ensAddr, Resolver resolverAddr) { 117 ens = ensAddr; 118 defaultResolver = resolverAddr; 119 } 120 121 /** 122 * @dev Transfers ownership of the reverse ENS record associated with the 123 * calling account. 124 * @param owner The address to set as the owner of the reverse record in ENS. 125 * @return The ENS node hash of the reverse record. 126 */ 127 function claim(address owner) returns (bytes32 node) { 128 return claimWithResolver(owner, 0); 129 } 130 131 /** 132 * @dev Transfers ownership of the reverse ENS record associated with the 133 * calling account. 134 * @param owner The address to set as the owner of the reverse record in ENS. 135 * @param resolver The address of the resolver to set; 0 to leave unchanged. 136 * @return The ENS node hash of the reverse record. 137 */ 138 function claimWithResolver(address owner, address resolver) returns (bytes32 node) { 139 var label = sha3HexAddress(msg.sender); 140 node = sha3(ADDR_REVERSE_NODE, label); 141 var currentOwner = ens.owner(node); 142 143 // Update the resolver if required 144 if(resolver != 0 && resolver != ens.resolver(node)) { 145 // Transfer the name to us first if it's not already 146 if(currentOwner != address(this)) { 147 ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, this); 148 currentOwner = address(this); 149 } 150 ens.setResolver(node, resolver); 151 } 152 153 // Update the owner if required 154 if(currentOwner != owner) { 155 ens.setSubnodeOwner(ADDR_REVERSE_NODE, label, owner); 156 } 157 158 return node; 159 } 160 161 /** 162 * @dev Sets the `name()` record for the reverse ENS record associated with 163 * the calling account. First updates the resolver to the default reverse 164 * resolver if necessary. 165 * @param name The name to set for this address. 166 * @return The ENS node hash of the reverse record. 167 */ 168 function setName(string name) returns (bytes32 node) { 169 node = claimWithResolver(this, defaultResolver); 170 defaultResolver.setName(node, name); 171 return node; 172 } 173 174 /** 175 * @dev Returns the node hash for a given account's reverse records. 176 * @param addr The address to hash 177 * @return The ENS node hash. 178 */ 179 function node(address addr) constant returns (bytes32 ret) { 180 return sha3(ADDR_REVERSE_NODE, sha3HexAddress(addr)); 181 } 182 183 /** 184 * @dev An optimised function to compute the sha3 of the lower-case 185 * hexadecimal representation of an Ethereum address. 186 * @param addr The address to hash 187 * @return The SHA3 hash of the lower-case hexadecimal encoding of the 188 * input address. 189 */ 190 function sha3HexAddress(address addr) private returns (bytes32 ret) { 191 addr; ret; // Stop warning us about unused variables 192 assembly { 193 let lookup := 0x3031323334353637383961626364656600000000000000000000000000000000 194 let i := 40 195 loop: 196 i := sub(i, 1) 197 mstore8(i, byte(and(addr, 0xf), lookup)) 198 addr := div(addr, 0x10) 199 i := sub(i, 1) 200 mstore8(i, byte(and(addr, 0xf), lookup)) 201 addr := div(addr, 0x10) 202 jumpi(loop, i) 203 ret := sha3(0, 40) 204 } 205 } 206 } 207 208 ## Copyright 209 Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).