Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Commit

Permalink
Updates to implementer's guide (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arachnid committed Jul 10, 2018
1 parent 0cf4fc6 commit dacf22a
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 152 deletions.
Binary file added docs/img/ens-architecture.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/ens-flow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/img/ens-logo.jpg
Binary file not shown.
Binary file added docs/img/ens-logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
149 changes: 143 additions & 6 deletions docs/implementers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,86 @@ Implementer's Guide

This section is intended to provide guidance for anyone wanting to implement tools and applications that use ENS, or custom resolvers within ENS.

3 steps to add ENS to your application
======================================

ENS integration in an application encompasses several critical features, each of which can be implemented independently. While comprehensive ENS integration is ideal, even basic support can be a huge benefit to users. Below, we outline the three main ways you can add ENS support to your application, starting with the simplest and highest impact.

1. Accept ENS names anywhere your app accepts addresses
-------------------------------------------------------

The first step to supporting ENS in your application is making your application understand ENS names, and accepting them anywhere an address is accepted. To understand how to do this, see :ref:`resolving`.

If possible, when a user enters an ENS name instead of an address, remember the ENS name, not the address it currently resolves to. This makes it possible for users to update their ENS names and have applications they used the name in automatically resolve to the new address, in the same way that you would expect your browser to automatically direct you to the new IP address if a site you use changes servers.

If your application deals with user funds or other critical resources, you may want to keep track of the address a name resolves to and warn them when it changes, to ensure they are aware of the change.

2. Display ENS names anywhere your app shows addresses
------------------------------------------------------

The second step is to display ENS names wherever your app currently displays addresses.

If a user entered an ENS name in step 1, you should retain this name and show it to them whenever you would normally show the address.

If a user entered an address, or the address was obtained from elsewhere, you may still be able to show an ENS name, by doing :ref:`reverse`. This permits you to find the canonical name for an address and display that when possible. If no canonical name is provided, your application can fall back to displaying the address as it did previously.

3. Permit users to register names for their resources
-----------------------------------------------------

The final step for comprehensive ENS integration is to facilitate associating ENS names with resources created by or managed with your application. This can take two forms:

1. Providing users with an easy way to register an ENS name and associate it with their resources (eg, wallet address); see :ref:`registrars`.
2. Providing users with an easy way to update a name they own to point at your application's resources; see :ref:`record-updates`.

ENS libraries
=============

The following sections describe how to interact with ENS directly. Chances are, there is already an ENS library for your language of choice, that will do the heavy lifting for you. To find one, see :ref:`libraries`.

.. _resolving:

Resolving Names
===============

Resolving names in ENS is a three step process:

1. Normalise and hash the name you want to resolve (see :ref:`namehash`).
2. Query the ENS registry for the address of the resolver responsible for the name.
3. Query the resolver for the resource you want to look up.

.. image:: img/ens-flow.png

To query the registry, call the `resolver(bytes32)` method on the ENS registry contract (see :ref:`deployments` for details on where to find the registry), passing it the namehash of the normalised name you are querying for. The registry will return the address of the resolver responsible for that name, or 0 if no resolver is configured or the name does not exist.

If the registry returned a nonzero resolver address, you can then query the resolver for the resource you want to resolve. For instance, to look up the Ethereum address associated with the name, you would call the resolver's `addr(bytes32)` method, again passing in the namehash. The resolver will then return the resource requested, if it's available.

A list of currently recognised resource types are defined in EIP137_, but you can also define your own if you need. For details on how to do that, see :ref:`writing-resolvers`.

.. _reverse:

Reverse Resolution
==================

In addition to mapping names to resources (forward resolution), ENS also supports mapping from addresses to names and other metadata (reverse resolution).

Reverse records are named `<ethereum address>.addr.reverse` - for instance, the official registry would have its reverse records at `314159265dd8dbb310642f98f50c066173c1259b.addr.reverse`.

Reverse resolution follows the same three-step process as forward resolution; the only change is that the name you are resolving has the special format defined above. To find the canonical name for an address, then, you would first query the ENS registry for the resolver responsible for `(address).addr.reverse`, then call `name(bytes32)` on that resolver in order to obtain its canonical name.

`addr.reverse` has a registrar with `claim(address)`, `claimWithResolver(address, address)`, and `setName(string)` functions.

The claim function takes one argument, the Ethereum address that should own the reverse record.

This permits a very simple pattern for contracts that wish to delegate control of their reverse record to their creator; they simply need to add this function call to their constructor:

::

reverseRegistrar.claim(msg.sender)

External accounts can also call this function to claim ownership of their reverse record. For simplicity, the `setName(string)` function permits claiming a record and configuring a canonical name all in one step.

.. _writing-resolvers:

Writing a resolver
==================

Expand Down Expand Up @@ -91,6 +171,8 @@ With these definitions, looking up a name given its node hash is straightforward
While it is possible for a contract to process a human-readable name into a node hash, we highly recommend working with node hashes instead, as they are easier to work with, and allow contracts to leave the complex work of normalising the name to their callers outside the blockchain. Where a contract always resolves the same names, those names may be converted to a node hash and stored in the contract as a constant.

.. _registrars:

Writing a registrar
===================

Expand All @@ -117,22 +199,76 @@ A registrar in ENS is simply any contract that owns a name, and allocates subdom
}
}
Interacting with ENS offchain
=============================
You may wish to set custom rules for the allocation of new names to your users; the rules you set are entirely up to you.

You should also bear in mind that as long as you retain ownership of the parent name - either directly or through another contract - your users have no guarantee that you will not take back ownership of their names and change what they resolve to. You may wish to consider committing ownership of the name to a contract that restricts your ability to control it. For an example of this, see ENSNow_.

.. _record-updates:

Updating ENS records
====================

Your application may wish to provide users with a means of updating names they own to point to resources your application provides or manages. Doing so follows a similar process to :ref:`resolving`:

1. Normalise and hash the name you want to resolve (see :ref:`namehash`).
2. Query the ENS registry for the address of the resolver responsible for the name.
3. Call the appropriate update method on the resolver.

Steps 1 and 2 are the same as in :ref:`resolving`. If step 2 fails to return a valid resolver address, you will need to inform your user and offer them alternatives - for instance, you can check if they own the name in question, and if they do, offer to configure a default resolver for them by calling `setResolver` on the ENS registry.

In step 3, call the method defined by the resolver profile - standard profiles are listed in EIP137_. For instance, to set the address associated with the name, call `setAddr(bytes32, address)`, passing in the name hash of the name to update and the address you wish it to point to.

.. _namehash:

Namehash
========

Names in ENS are represented as 32 byte hashes, rather than as plain text. This simplifies processing and storage, while permitting arbitrary length domain names, and preserves the privacy of names onchain. The algorithm used to translate domain names into hashes is called namehash. The Namehash algorithm is defined in EIP137_.

In order to preserve the hierarchal nature of names, namehash is defined recursively, making it possible to derive the hash of a subdomain from the namehash of the parent domain and the name or hash of the subdomain label.

Terminology
-----------

- **domain** - the complete, human-readable form of a name; eg, `'vitalik.wallet.eth'`.
- **label** - a single component of a domain; eg, `'vitalik'`, `'wallet'`, or `'eth'`. A label may not contain a period ('.').
- **label hash** - the output of the keccak-256 function applied to a label; eg, `keccak256('eth') = 0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0`.
- **node** - the output of the namehash function, used to uniquely identify a name in ENS.

Algorithm
---------

First, a domain is divided into labels by splitting on periods ('.'). So, 'vitalik.wallet.eth' becomes the list ['vitalik', 'wallet', 'eth'].

The namehash function is then defined recursively as follows:

::

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

A sample implementation in Python is provided below.

::

A Javascript library, ethereum-ens_, is available to facilitate reading and writing ENS from offchain. This section will be updated as libraries for more languages become available.
def namehash(name):
if name == '':
return '\0' * 32
else:
label, _, remainder = name.partition('.')
return sha3(namehash(remainder) + sha3(label))

Normalising and validating names
================================
--------------------------------

Before a name can be converted to a node hash using :ref:`namehash`, the name must first be normalised and checked for validity - for instance, converting `fOO.eth` into `foo.eth`, and prohibiting names containing forbidden characters such as underscores. It is crucial that all applications follow the same set of rules for normalisation and validation, as otherwise two users entering the same name on different systems may resolve the same human-readable name into two different ENS names.
Before a name can be converted to a node hash using Namehash, the name must first be normalised and checked for validity - for instance, converting `fOO.eth` into `foo.eth`, and prohibiting names containing forbidden characters such as underscores. It is crucial that all applications follow the same set of rules for normalisation and validation, as otherwise two users entering the same name on different systems may resolve the same human-readable name into two different ENS names.

Applications using ENS and processing human-readable names must follow UTS46_ for normalisation and validation. Processing should be done with non-transitional rules, and with `UseSTD3ASCIIRules=true`.

The ethereum-ens_ Javascript library incorporates compliant preprocessing into its `validate` and `namehash` functions, so users of this library avoid the need to handle this manually.

Handling of ambiguous names
===========================
---------------------------

Because of the large number of characters in unicode, and the wide variety of scripts represented, inevitably there are different Unicode characters that are similar or even identical when shown in common fonts. This can be abused to trick users into thinking they are visiting one site or resource, when in fact they are visiting another. This is known as a `homoglyph attack`_.

Expand All @@ -147,3 +283,4 @@ User agents and other software that display names to users should take counterme
.. _UTS46: http://unicode.org/reports/tr46/
.. _`homoglyph attack`: https://en.wikipedia.org/wiki/Internationalized_domain_name#ASCII_spoofing_concerns
.. _`Chromium's IDNA strategy`: https://www.chromium.org/developers/design-documents/idn-in-google-chrome
.. _ENSNow: https://github.com/ensdomains/subdomain-registrar
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Welcome to ENS's documentation!
quickstart
userguide
implementers
libraries
deploying
dns
faq
Expand Down
59 changes: 9 additions & 50 deletions docs/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Introduction
*******************

.. image:: img/ens-logo.jpg
.. image:: img/ens-logo.png
:height: 351px
:width: 300px
:scale: 50%
Expand All @@ -11,23 +11,20 @@ Introduction

ENS is the Ethereum Name Service, a distributed, open, and extensible naming system based on the Ethereum blockchain.

ENS can be used to resolve a wide variety of resources. The initial standard for ENS defines resolution for Ethereum addresses, but the system is extensible by design, allowing more resource types to be resolved in future without the core components of ENS requiring upgrades.
ENS's job is to map human-readable names like 'alice.eth' to machine-readable identifiers such as Ethereum addresses, content hashes, and metadata. ENS also supports 'reverse resolution', making it possible to associate metadata such as canonical names or interface descriptions with Ethereum addresses.

Overview
========

The primary goal of ENS is to resolve human-readable names, like 'myname.eth', into machine-readable identifiers, including Ethereum addresses, Swarm and IPFS content hashes, and other identifiers. A secondary purpose is to provide metadata about names, such as ABIs for contracts, and whois information for users.

ENS has similar goals to DNS, the Internet's Domain Name Service, but has significantly different architecture, due to the capabilities and constraints provided by the Ethereum blockchain. Like DNS, ENS operates on a system of dot-separated hierarchial names called domains, with the owner of a domain having full control over the distribution of subdomains.
ENS has similar goals to DNS, the Internet's Domain Name Service, but has significantly different architecture, due to the capabilities and constraints provided by the Ethereum blockchain. Like DNS, ENS operates on a system of dot-separated hierarchial names called domains, with the owner of a domain having full control over the allocation of subdomains.

Top-level domains, like '.eth' and '.test' are owned by smart contracts called registrars, which specify rules governing the allocation of their subdomains. Anyone may, by following the rules imposed by these registrar contracts, obtain ownership of a second-level domain for their own use.

Architecture
============
ENS in 5 minutes
================

ENS has two principal components: the registry, and resolvers.

The ENS registry consists of a single central contract that maintains a list of all domains and subdomains, and stores three critical pieces of information about each:
.. image:: img/ens-architecture.png

The ENS registry consists of a single smart contract that maintains a list of all domains and subdomains, and stores three critical pieces of information about each:

- The owner of the domain
- The resolver for the domain
Expand All @@ -47,45 +44,7 @@ Resolvers are responsible for the actual process of translating names into addre

Each record type - Ethereum address, Swarm content hash, and so forth - defines a method or methods that a resolver must implement in order to provide records of that kind. New record types may be defined at any time via the EIP standardisation process, with no need to make changes to the ENS registry or to existing resolvers in order to support them.

.. _namehash:

Namehash
========

Names in ENS are represented as 32 byte hashes, rather than as plain text. This simplifies processing and storage, while permitting arbitrary length domain names, and preserves the privacy of names onchain. The algorithm used to translate domain names into hashes is called namehash. Namehash is defined in EIP137_.

In order to preserve the hierarchal nature of names, namehash is defined recursively, making it possible to derive the hash of a subdomain from the hash of the parent domain and the name or hash of the subdomain.

Terminology
-----------

- **domain** - the complete, human-readable form of a name; eg, `'vitalik.wallet.eth'`.
- **label** - a single component of a domain; eg, `'vitalik'`, `'wallet'`, or `'eth'`. A label may not contain a period ('.').
- **label hash** - the output of the keccak-256 function applied to a label; eg, `keccak256('eth') = 0x4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0`.
- **node** - the output of the namehash function, used to uniquely identify a name in ENS.

Algorithm
---------

First, a domain is divided into labels by splitting on periods ('.'). So, 'vitalik.wallet.eth' becomes the list ['vitalik', 'wallet', 'eth'].

The namehash function is then defined recursively as follows:

::

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

A sample implementation in Python is provided below.

::

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

ENS on Ethereum
===============
Expand Down
31 changes: 31 additions & 0 deletions docs/libraries.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.. _libraries:

*******************
ENS Library support
*******************

This page lists libraries that support ENS in different languages, for your convenience. If you know of a library that is not listed here, please `send us a PR`_.

Javascript
==========

- ethereum-ens_
- ethjs-ens_

Python
======

- ens.py_
- web3.py_

Command-line
============

- ethers-ens_

.. _ethereum-ens: https://www.npmjs.com/package/ethereum-ens
.. _ethjs-ens: https://www.npmjs.com/package/ethjs-ens
.. _ethers-ens: https://github.com/ethers-io/ethers-ens
.. _ens.py: https://github.com/carver/ens.py
.. _web3.py: https://github.com/ethereum/web3.py
.. _`send us a PR`: https://github.com/ensdomains/ens/compare
2 changes: 2 additions & 0 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Quickstart

Just want to get a name and make it resolve to something? Here's how.

**NOTE**: This quickstart uses 'ensutils.js', a simple Javascript file that facilitates getting started with ENS. You should *NOT* use this in production code - it exists purely for interactive experimentation with the console!

First, make sure your client is in **sync with a network** (mainnet, ropsten, rinkeby, etc.). You can use eth.syncing in your Ethereum console to track progress.

You can utilize geth: https://github.com/ethereum/go-ethereum to connect a network and begin the process of syncing the chain.
Expand Down

0 comments on commit dacf22a

Please sign in to comment.