Abstract

Motivation

Many Ethereum clients contain hard-coded bootstrap node lists. Updating those
lists requires a software update. The current lists are small, giving the client
little choice of initial entry point into the Ethereum network. We would like to
maintain larger node lists containing hundreds of nodes, and update them
regularly.

The scheme described here is a replacement for client bootstrap node lists with
equivalent security and many additional benefits. DNS node lists may also be
useful to Ethereum peering providers because their customers can configure the
client to use the provider's list. Finally, the scheme serves as a fallback
option for nodes which can't join the node discovery DHT.

Specification

DNS Record Structure

Node lists are encoded as TXT records. The records form a merkle tree. The root
of the tree is a record with content:

enrtree-root=v1 hash=<roothash> seq=<seqnum> sig=<signature>

roothash is the abbreviated root hash of the tree in base32 encoding. seqnum
is the tree's update sequence number, a decimal integer. signature is a
65-byte secp256k1 EC signature over the keccak256 hash of the record content,
encoded as URL-safe base64.

Further TXT records on subdomains map hashes to one of three entry types. The
subdomain name of any entry is the base32 encoding of the abbreviated keccak256
hash of its text content.

enrtree=<h₁>,<h₂>,...,<hₙ> is an intermediate tree containing further hash
subdomains.

enrtree-link=<key>@<fqdn> is a leaf pointing to a different list located at
another fully qualified domain name. The key is the expected signer of the
remote list, a base32 encoded secp256k1 public key,

enr=<node-record> is a leaf containing a node record as defined in EIP-778.
The node record is encoded as a URL-safe base64 string.

No particular ordering or structure is defined for the tree. Whenever the tree
is updated, its sequence number should increase. The content of any TXT record
should be small enough to fit into the 512 byte limit imposed on UDP DNS
packets. This limits the number of hashes that can be placed into a enrtree=
entry.

Referencing Trees by URL

When referencing a record tree, e.g. in source code, the preferred form is a
URL. References should use the scheme enrtree:// and encode the DNS domain in
the hostname. The expected public key that signs the tree should be encoded in
33-byte compressed form as a base32 string in the username portion of the URL.

Client Protocol

Resolve the TXT record of the name and check whether it contains a valid
"enrtree-root=v1" entry. Let's say the root hash contained in the entry is
"CFZUWDU7JNQR4VTCZVOJZ5ROV4".

Optionally verify the signature on the root against a known public key and
check whether the sequence number is larger than or equal to any previous
number seen for that name.

Resolve the TXT record of the hash subdomain, e.g. "CFZUWDU7JNQR4VTCZVOJZ5ROV4.mynodes.org"
and verify whether the content matches the hash.

The next step depends on the entry type found:

for enrtree: parse the list of hashes and continue resolving those (step 3).

for enrtree-link: continue traversal on the linked domain (step 1).

for enr: decode, verify the node record and import it to local node storage.

During traversal, the client should track hashes and domains which are already
resolved to avoid going into an infinite loop.

Rationale

Why DNS?

We have chosen DNS as the distribution medium because it is always available,
even under restrictive network conditions. The protocol provides low latency and
answers to DNS queries can be cached by intermediate resolvers. No custom server
software is needed. Node lists can be deployed to any DNS provider such as
CloudFlare DNS, dnsimple, Amazon Route 53 using their respective client
libraries.

Why is this a merkle tree?

Being a merkle tree, any node list can be authenticated by a single signature on
the root. Hash subdomains protect the integrity of the list. At worst
intermediate resolvers can block access to the list or disallow updates to it,
but cannot corrupt its content. The sequence number prevents replacing the root
with an older version.

Synchronizing updates on the client side can be done incrementally, which
matters for large lists. Individual entries of the tree are small enough to fit
into a single UDP packet, ensuring compatibility with environments where only
basic UDP DNS is available. The tree format also works well with caching
resolvers: only the root of the tree needs a short TTL. Intermediate entries and
leaves can be cached for days.

Why does enrtree-link exist?

Links between lists enable federation and web-of-trust functionality. The
operator of a large list can delegate maintenance to other list providers. If
two node lists link to each other, users can use either list and get nodes from
both.