Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 223 additions & 83 deletions discv5/discv5-rationale.md

Large diffs are not rendered by default.

1,020 changes: 638 additions & 382 deletions discv5/discv5-theory.md

Large diffs are not rendered by default.

142 changes: 96 additions & 46 deletions discv5/discv5-wire.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,19 @@ the result set. The recommended result limit for FINDNODE queries is 16 nodes.
message-type = 0x04
total = total number of responses to the request

NODES is the response to a FINDNODE or TOPICQUERY message. Multiple NODES messages may be
sent as responses to a single query. Implementations may place a limit on the allowed
maximum for `total`. If exceeded, additional responses may be ignored.
NODES is sent as a response to FINDNODE, REGTOPIC, or TOPICQUERY. Multiple NODES messages
may be sent as responses to a single query. Implementations may place a limit on the
allowed maximum for `total`. If exceeded, additional responses may be ignored.

When handling NODES as a response to FINDNODE, the recipient should verify that the
received nodes match the requested distances.

When NODES appears as a response to REGTOPIC or TOPICQUERY, it carries auxiliary ENRs
selected from the responder's view of the service table for the requested topic. These
ENRs are routing information for the requester to populate or refresh its own service
table `B(s)`. They are not themselves topic registrants; the actual registered nodes are
returned via TOPICNODES.

### TALKREQ Request (0x05)

message-data = [request-id, protocol, request]
Expand All @@ -234,62 +240,102 @@ response data.

### REGTOPIC Request (0x07)

**NOTE: the content and semantics of this message are not final.**
**Implementations should not respond to or send these messages.**
message-data = [request-id, topic, ENR, ticket,
[topic-distance₁, topic-distance₂, ..., topic-distanceₙ]]
message-type = 0x07
topic = 32-byte service / topic identifier
ENR = current node record of sender
ticket = opaque byte array containing a ticket previously issued by the
recipient registrar; empty (`0x80`) on first attempt
topic-distanceₙ = positive integer log2 distance from `topic` where the sender's
service table `B(topic)` still has space

message-data = [request-id, topic, ENR, ticket]
message-type = 0x07
node-record = current node record of sender
ticket = byte array containing ticket content
REGTOPIC asks the recipient registrar to register the sender (identified by `ENR`) for
service `topic`. If the sender has a ticket from a previous registration attempt with this
registrar, it must present the ticket; otherwise `ticket` is the empty byte array.

REGTOPIC attempts to register the sender for the given topic. If the requesting node has a
ticket from a previous registration attempt, it must present the ticket. Otherwise
`ticket` is the empty byte array (RLP: `0x80`). The ticket must be valid and its waiting
time must have elapsed before using the ticket.
The `topic-distance` list carries the sender's "send me ENRs at these distances" hint to
the recipient: when returning auxiliary ENRs, the recipient should prefer ENRs whose log2
distance to `topic` matches one of the listed values, so the response helps the sender
populate its service table.

REGTOPIC is always answered by a TICKET response. The requesting node may also receive a
REGCONFIRMATION response when registration is successful. It may take up to 10s for the
confirmation to be sent.
REGTOPIC is always answered with a single REGCONFIRMATION response. The recipient may
additionally send zero or more NODES responses carrying auxiliary ENRs selected from its
view of the service table.

### TICKET Response (0x08)
See the [theory section on tickets] and [theory section on registrar admission control]
for the registrar's waiting-time semantics.

**NOTE: the content and semantics of this message are not final.**
**Implementations should not respond to or send these messages.**
### REGCONFIRMATION Response (0x08)

message-data = [request-id, ticket, wait-time]
message-data = [request-id, total, ticket, wait-time]
message-type = 0x08
ticket = an opaque byte array representing the ticket
wait-time = time to wait before registering, in seconds

TICKET is the response to REGTOPIC. It contains a ticket which can be used to register for
the requested topic after `wait-time` has elapsed. See the [theory section on tickets] for
more information.

### REGCONFIRMATION Response (0x09)

**NOTE: the content and semantics of this message are not final.**
**Implementations should not respond to or send these messages.**

message-data = [request-id, topic]
message-type = 0x09
request-id = request-id of REGTOPIC
total = total number of responses (REGCONFIRMATION + NODES) to the request
ticket = ticket issued by the registrar for the next attempt;
empty byte array (RLP: `0x80`) when the registration was admitted
wait-time = milliseconds to wait before submitting the next REGTOPIC attempt
with the returned `ticket`. When `ticket` is empty, `wait-time` carries
the advertisement lifetime instead.

REGCONFIRMATION is the response to REGTOPIC. It is sent immediately by the registrar and
plays two roles, distinguished by the length of `ticket`:

- If `ticket` is the empty byte array, the advertisement has been admitted to the
registrar's ad cache. `wait-time` indicates the advertisement lifetime; the advertiser
should renew before that lifetime elapses to remain in the cache.
- If `ticket` is non-empty, the advertisement was not admitted on this attempt. The
sender must wait at least `wait-time` milliseconds and re-attempt the registration with
the returned `ticket`. See the [theory section on tickets] and the [theory section on
the waiting-time function].

The `total` field announces the total number of responses (this REGCONFIRMATION plus any
NODES messages carrying auxiliary ENRs) that the registrar will send for this request.

### TOPICQUERY Request (0x09)

message-data = [request-id, topic,
[topic-distance₁, topic-distance₂, ..., topic-distanceₙ]]
message-type = 0x09
topic = 32-byte service / topic identifier
topic-distanceₙ = positive integer log2 distance from `topic` where the sender's
service table `B(topic)` still has space

TOPICQUERY asks the recipient to return registered advertisers for the given `topic` from
its ad cache. The recipient sends zero or more TOPICNODES responses containing matching
advertiser ENRs, and may additionally send zero or more NODES responses carrying
auxiliary ENRs selected from its service-table view (see NODES).

The `topic-distance` list serves the same purpose as in REGTOPIC: it tells the recipient
which log2 distances from `topic` the sender's service table still has room for, so the
recipient can choose useful auxiliary ENRs to include in its NODES responses.

See the [theory section on lookup responses] for the discoverer-side termination semantics
(distinct-advertisers count) and the [theory section on parameters] for `Freturn`.

### TOPICNODES Response (0x0A)

REGCONFIRMATION notifies the recipient about a successful registration for the given
topic. This call is sent by the advertisement medium after the time window for
registration has elapsed on a topic queue.
message-data = [request-id, total, [ENR, ...]]
message-type = 0x0a
request-id = request-id of TOPICQUERY
total = total number of responses (NODES + TOPICNODES) to the request

### TOPICQUERY Request (0x0A)
TOPICNODES is the dedicated response to TOPICQUERY carrying advertiser ENRs that are
currently registered for the requested topic in the recipient's ad cache. Multiple
TOPICNODES messages may be sent for a single TOPICQUERY.

**NOTE: the content and semantics of this message are not final.**
**Implementations should not respond to or send these messages.**
The `total` field announces the total number of responses (TOPICNODES messages plus any
NODES messages carrying auxiliary ENRs) the recipient will send for this request.
Implementations may place a limit on the allowed maximum for `total`; if exceeded,
additional responses may be ignored.

message-data = [request-id, topic]
message-type = 0x0a
topic = 32-byte topic hash
The recipient should return only non-expired advertisements from its ad cache. When the
ad cache contains more than `Freturn` advertisements for the topic, the recipient
selects which advertisements to return; the exact selection policy is implementation
defined.

TOPICQUERY requests nodes in the [topic queue] of the given topic. The recipient of this
request must send one or more NODES messages containing node records registered for the
topic.
TOPICNODES carries only registered advertisers. Auxiliary routing information for the
sender's service table is carried separately via NODES responses.

## Test Vectors

Expand All @@ -299,5 +345,9 @@ A collection of test vectors for this specification can be found at
[handshake section]: ./discv5-theory.md#handshake-steps
[topic queue]: ./discv5-theory.md#topic-table
[theory section on tickets]: ./discv5-theory.md#tickets
[theory section on registrar admission control]: ./discv5-theory.md#admission-control
[theory section on the waiting-time function]: ./discv5-theory.md#waiting-time-function
[theory section on lookup responses]: ./discv5-theory.md#lookup-responses
[theory section on parameters]: ./discv5-theory.md#parameters
[EIP-778]: ../enr.md
[discv5 wire test vectors]: ./discv5-wire-test-vectors.md
59 changes: 37 additions & 22 deletions discv5/discv5.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,67 @@ entry point into the network.
The system's design is loosely inspired by the Kademlia DHT, but unlike most DHTs no
arbitrary keys and values are stored. Instead, the DHT stores and relays 'node records',
which are signed documents providing information about nodes in the network. Node
Discovery acts as a database of all live nodes in the network and performs three basic
Discovery acts as a database of live nodes in the network and performs two basic
functions:

- Sampling the set of all live participants: by walking the DHT, the network can be
- Sampling the set of live participants: by walking the DHT, the network can be
enumerated.
- Searching for participants providing a certain service: Node Discovery v5 includes a
scalable facility for registering 'topic advertisements'. These advertisements can be
queried and nodes advertising a topic found.
- Authoritative resolution of node records: if a node's ID is known, the most recent
version of its record can be retrieved.

Node Discovery v5 also supports topic-based service discovery through TopDisc. TopDisc is
an extension layered on top of the ordinary node discovery network. It allows nodes to
advertise participation in a topic or service, and allows other nodes to discover those
advertisements while reusing the existing node table, ENR mechanism, packet format, and
authenticated session machinery.

TopDisc is intended for discovering participants in higher-level services or overlays
without requiring each service to operate a separate discovery network. Nodes that support
TopDisc advertise this capability in their ENR. Nodes that do not support TopDisc remain
ordinary Node Discovery v5 participants and continue to contribute to the global discovery
network.

## Specification Overview

The specification has three parts:

- [discv5-wire.md] defines the wire protocol.
- [discv5-theory.md] describes the algorithms and data structures.
- [discv5-rationale.md] contains the design rationale.
- [discv5-theory.md] describes the algorithms and data structures for ordinary node
discovery and topic-based service discovery.
- [discv5-rationale.md] contains the design rationale for ordinary node discovery and
topic-based service discovery.

## Comparison With Other Discovery Mechanisms

Systems such as MDNS/Bonjour allow finding hosts in a local-area network. The Node
Discovery Protocol is designed to work on the Internet and is most useful for applications
with a large number of participants spread across the Internet.

Systems using a rendezvous server: these systems are commonly used by desktop applications
or cloud services to connect participants to each other. While undoubtedly efficient, this
requires trust in the operator of the rendezvous server and these systems are prone to
censorship. Compared to a rendezvous server, The Node Discovery Protocol doesn't rely on a
single operator and places a small amount of trust in every participant. It becomes more
resistant to censorship as the size of the network increases and participants of multiple
distinct peer-to-peer networks can share the discovery network to further increase its
resilience.
Systems using a rendezvous server are commonly used by desktop applications or cloud
services to connect participants to each other. While efficient, this requires trust in
the operator of the rendezvous server and these systems are prone to censorship. Compared
to a rendezvous server, the Node Discovery Protocol does not rely on a single operator and
places a small amount of trust in every participant. It becomes more resistant to
censorship as the size of the network increases, and participants of multiple distinct
peer-to-peer networks can share the discovery network to further increase its resilience.

TopDisc provides topic-based service discovery without introducing a central rendezvous
server or requiring every service to maintain a separate discovery network. It reuses the
ordinary Node Discovery v5 network as a shared discovery substrate, while adding
registrar-side admission control and bounded advertisement storage for service discovery.

The Achilles heel of the Node Discovery Protocol is the process of joining the network:
while any other node may be used as an entry point, such a node must first be located
through some other mechanism. Several approaches including scalable listing of initial
entry points in DNS or discovery of participants in the local network can be used for
reasonable secure entry into the network.
through some other mechanism. Several approaches, including scalable listing of initial
entry points in DNS or discovery of participants in the local network, can be used for
reasonably secure entry into the network.

## Comparison With Node Discovery v4

- Topic advertisement was added.
- Arbitrary node metadata can be stored/relayed.
- Node identity crypto is extensible, use of secp256k1 keys isn't strictly required.
- The protocol no longer relies on the system clock.
- Topic-based service discovery through TopDisc was added.
- Arbitrary node metadata can be stored/relayed through ENRs.
- Node identity crypto is extensible; use of secp256k1 keys is not strictly required.
- The protocol no longer relies on the system clock for replay prevention.
- Communication is encrypted, protecting topic searches and record lookups against passive
observers.

Expand Down
44 changes: 44 additions & 0 deletions enr-entries/topic-discovery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# The "topic-discovery" ENR entry

This specification defines the "topic-discovery" ENR entry, which signals that a node
implements the [TopDisc][topdisc] topic discovery capability for [Node Discovery v5][discv5]
and is willing to participate in topic service tables, registrations, and lookups.

## Entry Format

entry-key = "topic-discovery"
entry-value = version

Where `version` is an unsigned integer identifying the supported TopDisc protocol version.
A node implementing the version described in the [TopDisc theory][topdisc] document sets:

topic-discovery = 1

## Semantics

A node MUST publish the `topic-discovery` entry in its ENR before it can be selected as a
registrar or as a target of topic-discovery queries by other nodes. Nodes whose ENR does
not contain `topic-discovery`, or whose `topic-discovery` value is not understood by the
local implementation, MUST NOT be inserted into local TopDisc service tables and MUST NOT
be selected for topic registration or lookup requests.

The entry does not by itself indicate which services or topics a node advertises. Service
membership is established through TopDisc registrations and observed at runtime; the ENR
entry only signals capability and protocol version compatibility.

Future, non-backwards-compatible revisions of the topic discovery capability MUST bump the
`version` value. Implementations that encounter an unknown version SHOULD treat the node
as if it did not publish the entry at all. Implementations MAY support multiple versions
simultaneously; the matching rule between local and remote versions is defined by local
policy.

## Change Log

### Initial version (2026)

The initial version of the "topic-discovery" entry is proposed in this document, alongside
the [Discv5 wire][discv5-wire] and [TopDisc theory][topdisc] specifications.

[discv5]: ../discv5/discv5.md
[topdisc]: ../discv5/discv5-theory.md
[discv5-wire]: ../discv5/discv5-wire.md
23 changes: 13 additions & 10 deletions enr.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@ The key/value pairs must be sorted by key and must be unique, i.e. any key may b
only once. The keys can technically be any byte sequence, but ASCII text is preferred. Key
names in the table below have pre-defined meaning.

| Key | Value |
|:------------|:-------------------------------------------|
| `id` | name of identity scheme, e.g. "v4" |
| `secp256k1` | compressed secp256k1 public key, 33 bytes |
| `ip` | IPv4 address, 4 bytes |
| `tcp` | TCP port, big endian integer |
| `udp` | UDP port, big endian integer |
| `ip6` | IPv6 address, 16 bytes |
| `tcp6` | IPv6-specific TCP port, big endian integer |
| `udp6` | IPv6-specific UDP port, big endian integer |
| Key | Value |
|:------------------|:---------------------------------------------------|
| `id` | name of identity scheme, e.g. "v4" |
| `secp256k1` | compressed secp256k1 public key, 33 bytes |
| `ip` | IPv4 address, 4 bytes |
| `tcp` | TCP port, big endian integer |
| `udp` | UDP port, big endian integer |
| `ip6` | IPv6 address, 16 bytes |
| `tcp6` | IPv6-specific TCP port, big endian integer |
| `udp6` | IPv6-specific UDP port, big endian integer |
| `topic-discovery` | [TopDisc] capability version, unsigned integer |

[TopDisc]: enr-entries/topic-discovery.md

All keys except `id` are optional, including IP addresses and ports. A record without
endpoint information is still valid as long as its signature is valid. If no `tcp6` /
Expand Down