Skip to content

Latest commit

 

History

History
233 lines (139 loc) · 14.9 KB

ens.asciidoc

File metadata and controls

233 lines (139 loc) · 14.9 KB

Ethereum Name Service (ENS)

You can design the best smart contract in the world, but if you don’t provide a good interface for users, they won’t be able to access it.

Contracts on the blockchain can be considered the back end of your application, and you can write the front end using any language or platform you like. Most of the apps described in this book use a traditional server model for the front end, but if you want to truly decentralize your application, you can also use decentralized hosting services like BitTorrent, Swarm or IPFS to host the front end.

On the traditional web, you ultimately access content by the IP address of its server. For instance, 91.198.174.192 is the IP address of wikipedia.org. That number has no information on what sort of content you will find, only the address of a computer that will serve you Wikipedia’s content.

Content-addressed systems are different, in that their addresses reflect the content itself, not its location. For instance, QmfXeApFP1mCrMTT1JX5XLtUvLxS5eNsegqDpBYUZPhXEQ is the address of an image that you can download from any computer on the IPFS network: the address describes the image, not where to get it. On a different system, e.g. Swarm, the address of the same image may be different, but in each case the address describes the content, not its location. This allows greater flexibility and decentralization, because as long as you can connect to a network node that has a copy of the image (or is connected to a node that has a copy of it), you can access it.

This process allows accessing any files that will make up your interface (HTML, JavaScript, CSS, images, etc.) in a decentralized manner, but has a huge drawback: hex addresses are not very human friendly. They consist of large mumbles of letters and numbers, which are neither memorable nor easy to type.

On the traditional internet, this was solved by the Domain Name system (DNS), a hierarchical system ultimately run by ICANN, a US-based non-profit that controls the distribution of top level domains, like .com or .org. On the blockchain an attempt to solve the same problem but in a decentralized manner is the Ethereum Naming System (ENS).

Names can be used for other purposes: for example, the Ethereum Foundation donation contract address is 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359; in a wallet that supports ENS, it’s simply ethereum.eth.

History of Ethereum name services

Name registration was the first non-currency application of blockchains, pioneered by Namecoin. The Ethereum whitepaper gave a two-line Namecoin-like registration system as one of its example applications.

Early releases of Geth and the C++ Ethereum client had a built-in namereg contract (not used any more), and many proposals and ERCs for name services were made, but it was only when Nick Johnson started working for Ethereum Foundation in 2016 and took the project under his wing that serious work on a registrar started.

ENS was launched on Star Wars day, May 4, 2017 (after a failed attempt to launch it on Pi Day, March 15).

The ENS specification

ENS is specified mainly in three Ethereum Improvement Proposals (EIPs), namely ERC137, which specifies the basic functions of ENS, ERC162, which describes the auction system for the .eth root, and ERC181, which specifies reverse registration of addresses.

ENS follows a sandwich design philosophy: a very simple layer on the bottom, followed by layers of more complex but replaceable code, with a very simple top layer that keeps all the funds in separate accounts.

Bottom of the sandwich: Name Owners and resolvers

The ENS operates on "nodes" instead of human-readable names: a human-readable name is converted to a node using the "Namehash" algorithm.

The base layer of the sandwich is a cleverly simple contract (less than 50 lines of code) defined by ERC137 that allows only nodes' owners to set information about their names and to create subnodes (the ENS equivalent of DNS subdomains).

The only functions on the base layer are those that enable a node owner to set information about their own node (specifically the resolver, time to live or transferring the ownership) and to create owners of new subnodes.

Namehash algorithm

Namehash is a recursive algorithm that can convert any name into a hash that identifies the name.

"Recursive" means that we solve the problem by solving a sub-problem that is a smaller problem of the same type, and then use the solution to the sub-problem to solve the original problem.

Namehash recursively hashes components of the name, producing a unique, fixed-length string for any valid input domain, or "node".

For example, the Namehash node of subdomain.example.eth is: keccak'example.eth' node) + keccak('subdomain'. The sub-problem we must solve is to compute the node for example.eth, which is: keccak'.eth' node) + keccak('example'. In turn, we must compute the node for eth, which is: keccakRoot Node) + keccak('eth'.

The root node is what we call the "base case" of our recursion, and we obviously can’t define it recursively, or the algorithm will never terminate! The root node is defined as: 0x0000000000000000000000000000000000000000000000000000000000000000 (32 zero bytes).

Putting this all together, the node of subdomain.example.eth is therefore: keccak(keccak(keccak(0x0…0 + keccak('eth')) + keccak('example')) + keccak('subdomain'))

Generalizing, we can define the Namehash function as follows (the base case for the root node, or empty name, followed by the recursive step):

namehash([]) = 0x0000000000000000000000000000000000000000000000000000000000000000
namehash([label, …]) = keccak256(namehash(…) + keccak256(label))

In Python this becomes:

def namehash(name):
  if name == '':
    return '\0' * 32
  else:
    label, _, remainder = name.partition('.')
    return sha3(namehash(remainder) + sha3(label))

Thus, mastering-ethereum.eth will be processed as follows:

namehash('mastering-ethereum.eth')
⇒ sha3(namehash('eth') + sha3('mastering-ethereum'))
⇒ sha3(sha3(namehash('') + sha3('eth')) + sha3('mastering-ethereum'))
⇒ sha3(sha3(('\0' * 32) + sha3('eth')) + sha3('mastering-ethereum'))

Of course, subdomains can themselves have subdomains: after subdomain.example.eth there could be a sub.subdomain.example.eth, then a sub.sub.subdomain.example.eth and so on. To avoid expensive recomputation, since Namehash depends only on the name itself, the node for a given name can be precomputed and inserted into a contract, removing the need for string manipulation, and permitting immediate lookup of ENS records regardless of the number of components in the raw name.

How to choose a valid name

Names consist of a series of dot-separated labels. Although upper and lower case letters are allowed, all labels should follow a UTS46 normalization process that case-folds labels before hashing them, so names with different case but identical spelling will end up with the same Namehash.

You could use labels and domains of any length, but for the sake of compatibility with legacy DNS, the following rules are recommended:

  • Labels should be no more than 64 characters each.

  • Complete ENS names should be no more than 255 characters.

  • Labels should not start or end with hyphens, or start with digits.

Root node ownership

One of the results of this hierarchical system is that it relies on the owners of the root node, who are able to create top level domains.

While the eventual goal is to adopt a decentralized decision-making process for new top level domains, right now the root node is controlled by a 4 out of 7 multisig, held by people in different countries (built as a reflection of the seven keyholders of the DNS system). As a result, a majority of at least 4 of the 7 keyholders is required to effect any change.

Currently the purpose and goal of these keyholders is to work in consensus with the community to:

  • Migrate and upgrade the temporary ownership of the .eth TLD (Top Level Domain) to a more permanent contract once the system is evaluated.

  • Allow adding new TLDs, if the community agrees they are needed.

  • Migrate the ownership of the root multisig to a more decentralized contract, when such a system is agreed upon, tested and implemented.

  • Serve as a last resort way to deal with any bugs or vulnerabilities in the top-level registries.

Resolvers

The basic ENS contract can’t add metadata to names; that is the job of "resolver contracts". These are user-created contracts that can answer questions about the name, such as "What’s the Swarm address associated with this app?", "What is the Ethereum address that receives payments in ether and tokens?", or "What’s the hash of the app?" (to verify its integrity).

Middle layer: the ".eth" nodes

At the moment, the only top level domain that is uniquely registrable in a smart contract is .eth.

There’s work on enabling traditional DNS domain owners to claim ENS ownership. While in theory this could work for .com the only domain that this has been implemented for so far is [.xyz, and only on Ropsten testnet](https://medium.com/the-ethereum-name-service/how-to-claim-your-dns-domain-on-ens-e600ef2d92ca).

.eth domains are distributed via an auction system. There is no reserved list or priority, and the only way to acquire a name is to use the system. The auction system is a complex piece of code (over 500 lines); most of the early development efforts (and bugs!) in ENS were in this part of the system, but it’s also replaceable and upgradeable (without risk to the funds—more on that later).

Vickrey Auctions

Names are distributed via a modified Vickrey Auction. In a traditional Vickrey auction, every bidder submits a sealed bid, and all of them are revealed simultaneously, at which point the highest bidder wins the auction, but only pays the second-highest bid. Therefore bidders are incentivized not to bid less than the true value of the name to them, since bidding their true value increases the chance they will win but does not affect the price they will eventually pay.

On a blockchain, some changes are required:

  • To ensure bidders don’t submit bids they have no intention of paying, they must lock up a value equal or higher than their bid beforehand, to guarantee the bid is valid.

  • Because you can’t hide secrets on a blockchain, bidders must execute at least two transactions (a commit–reveal process), in order to hide the original value and name they bid on.

  • Since you can’t reveal all bids simultaneously in a decentralized system, bidders must reveal their own bids themselves; if they don’t, they forfeit their locked-up funds. Without this forfeit, one could make many bids and choose to reveal only one or two, turning a sealed-bid auction into a traditional increasing price auction.

Therefore, the auction is a four-step process:

  1. Start the auction. This is required to broadcast the intent to register a name. This creates all auction deadlines. The names are hashed, so that only those who have the name in their dictionary will know which auction was opened. This allows some privacy, useful if you are creating a new project and don’t want to share details about it. You can open multiple dummy auctions at the same time, so if someone is following you they cannot simply bid on all auctions you open.

  2. Make a sealed bid: you must do this before the bidding deadline, by tying a given amount of ether to the hash of a secret message (containing, among others, the hash of the name, the actual amount of the bid, and a salt). You can lock up more ether than you are actually bidding in order to mask your true valuation.

  3. Reveal the bid: during the reveal period, you must make a transaction that reveals the bid, which will then calculate the highest bid, the second higher bid and send ether back to unsuccessful bidders. Every time the bid is revealed the current winner is recalculated, therefore the last one to be set before the revealing deadline expires becomes the overall winner.

  4. Clean up after: if you are the winner, you can finalize the auction in order to get back the difference between your bid and the second highest. If you forgot to reveal you can make a late reveal and recover a little of your bid.

Top layer of the sandwich: the Deeds

The top layer of the sandwich is yet another super-simple contract with a single purpose: to hold the funds.

When you win a name, the funds are not actually sent anywhere, but are just locked up for the period you want to hold the name (at least a year). This works like a guaranteed buy-back: if the owner does not want the name any more they can sell it back to the system and recover their ether (so the cost of holding the name is the opportunity cost of doing something with a return greater than zero).

Of course, having a single contract hold millions of dollars in ether has proven to be very risky, so instead ENS creates a Deed Contract for each new name. The Deed Contract is very simple (about 50 lines of code) and it only allows the funds to be transferred back to a single account (the deed owner) and to be called by a single entity (the registrar contract). This approach drastically reduces the attack surface where bugs can put the funds at risk.

Registering a name

ens flow
Figure 1. ENS timeline for registration

Resolving a name

In ENS, resolving a name is a two-step process:

  1. The ENS registry is called with the name to resolve after hashing it. If the record exists, the registry returns the address of its resolver.

  2. The resolver is called, using the method appropriate to the resource being requested. The resolver returns the desired result.

Setting an ENS name resolver

Types of resolvers

Address resolver

Smart contract resolver

Swarm object resolver

ENS subdomains

Creating a subdomains

ENS secondary markets

Buying and selling names

Renting a name