3.3 Peer Discovery
3.3.1 Introduction
This section defines the Peer Discovery protocol, its logic and the different requests and responses exchanged.
In order to establish connections, an IOTA node needs to discover and maintain a list of the reachable IP addresses of other peers. Moreover, some external modules, such as the Neighbor Selection and the Fast Probabilistic Consensus (FPC) may require an updated list of known peers.
The main goal of the Peer Discovery protocol is to expose an interface providing a list of all the verified peers.
Throughout this section the terms Node
and Peer
are used interchangeably to refer to a Node
device.
The usage of the Ping and Pong mechanism is to be considered as a bidirectional exchange similarly to how described by other standards such as CoAP and WebSocket.
3.3.2 Detailed Design
To bootstrap the peer discovery, a node must be able to reach one or more entry nodes. To achieve this, the implementation of the protocol shall provide a hard-coded list of trusted entry nodes run by the IF or by trusted community members that answer to peer discovery packets coming from new nodes joining the IOTA network. This approach is a common practice of many distributed networks [Neudecker 2018]. Public Key-based Cryptography (PKC) shall be used for uniquely identifying peers and for authenticating each packet. The usage of the Ping and Pong protocols is that Ping are sent to verify a given peer and, upon reception of a valid Pong as a response from that peer, the peer is verified. Once a peer has been verified, it can be queried to discover new peers by sending a DiscoveryRequest. As a response, a DiscoveryResponse shall be returned, containing a list of new peers. The new peer nodes in this list shall be verified by the receiving application.
This process is summarized in the following figure and detailed in the following subsections:
3.3.2.1 Node Identities
Every node has a cryptographic identity, a key on the ed25519 elliptic curve. The blake2b
hash of the public key of the peer serves as its identifier or node ID
.
3.3.2.2 Verification
The verification process aims at both verifying peer identities and checking their online status. Each peer shall maintain a list of all the known peers. This list shall be called known_peer_list
. Elements of any known peer list shall contain a reference to a Peer and a time at which it shall be verified/re-verified.
As such, the known_peer_list
can be seen as a time-priority queue. A newly discovered peer gets added to the list at the current time. Whenever a peer is verified, its time value on the known_peer_list
gets updated to the time at which that peer shall be re-verified.
The intent of this arrangement is to allow the node application to first verify newly discovered (and thus still unverified) peers and then to re-verify older peers (to confirm their online status) by iterating over the known_peer_list
.
It is worthwhile to note that the order in which the known_peer_list
is worked through is important. For example, if the peer is added to the front ('head') of the known_peer_list
, it is possible for an adversary to front-fill the known_peer_list
with a selection of its own nodes. This is resolved by the use of the time-priority queue.
The verification process always initiates from a Ping. Upon reception of a Ping, a peer shall check its validity by:
- verifying that the signature of the Ping is valid and discarding the request otherwise;
- checking that the
version
andnetwork_id
fields match its configuration and discarding the Ping otherwise; - checking that the
timestamp
field is fresh (i.e., not older than a given time) and discarding the packet otherwise; - checking that the
dest_addr
matches its IP address and discarding the Ping otherwise.
Upon successful validation of a received Ping, a peer shall respond with a Pong. In case the sender of the Ping is a new peer from the perspective of the receiving node, the receiver peer shall add it to its known_peer_list
. This enables the verification process to also occur in the reverse direction.
Upon reception of a Pong, a peer shall check its validity by:
- verifying that the signature of the Pong is valid and discarding it otherwise;
- checking that the
req_hash
field matches a request (i.e. Ping) previously sent and not expired (i.e., the difference between the timestamp of the Ping and Pong is not greater than a given threshold) and discarding the associated Ping or Pong otherwise; - checking that the
dest_addr
matches its IP address and discarding the associated Ping or Pong otherwise.
Upon successful validation of a received Pong, a peer shall:
- add the peer sender of the Pong to a list of verified peers called
verified_peer_list
; - move the peer entry of the
known_peer_list
to the tail.
3.3.2.3 Removal
While verifying a new peer, if no or an invalid Pong is received after max_verify_attempts
attempts, that node shall be removed from the known_peer_list
. Each expected reply should have a timeout such that if no answer is received after that, an attempt is considered concluded and counted as failed.
Each peer on the verified_peer_list
shall be re-verified after verification_lifetime
hours; while re-verifying a peer, if no or invalid Pong is received after max_reverify_attempts
attempts, the peer shall be removed from the verified_peer_list
.
3.3.2.4 Discovery
Each peer entry of the verified_peer_list
may be used to discover new peers. This process is initiated by sending a DiscoveryRequest.
Upon reception of a DiscoveryRequest, a peer node shall check its validity by:
- checking that the sender of the DiscoveryRequest is a verified peer (i.e. is stored in the
verified_peer_list
) and discarding the request otherwise; - verifying that the signature of the DiscoveryRequest is valid and discarding the request otherwise;
- checking that the
timestamp
field is fresh (i.e., not older than a given time) and discarding the request otherwise.
Upon successful validation of a received DiscoveryRequest, a peer shall reply with a DiscoveryResponse.
Upon reception of a DiscoveryResponse, a peer shall check its validity by:
- verifying that the signature of the DiscoveryResponse is valid and discarding the response otherwise;
- checking that the
req_hash
field matches a discovery request (i.e. DiscoveryRequest) previously sent and not expired (i.e., the difference between the timestamp of the DiscoveryRequest and DiscoveryResponse is not greater than a given threshold) and discarding the response otherwise.
Upon successful validation of a received DiscoveryResponse, a node shall add the nodes contained in the peers
field to the known_peer_list
.
3.3.2.5 Ping and Pong Layout
Each Ping and Pong shall be encapsulated into a data
field of a generic Request
and Response
. Its type
shall be specified in the type
field. Each request and response shall be signed with the ed25519 private key of the sender's identity and shall contain the related public key, in order to allow the packet receiving node to verify the signature. All the received responses shall be verified and those with invalid signature shall be discarded.
Request and Response Layout
Name | Type | Description |
---|---|---|
type | uint8 | Defines the type of the request or response. |
data | ByteArray | contains the payload of the request or response (e.g., a Ping). |
public_key | ByteArray[32] | The ed25519 public key of the peer's identity used to verify its signatures. |
signature | ByteArray[32] | The ed25519 signature of the `data` field, signed by using the private key of the peer's identity. |
Ping
Name | Type | Description |
---|---|---|
version | uint32 | The version of the protocol. |
network_id | uint32 | The network identifier. |
timestamp | time | The unix timestamp of the Ping. |
src_addr | string | The IP address, in string form, of the sender (e.g., "192.0.2.1", "[2001:db8::1]"). |
src_port | uint32 | The listening port of the sender. |
dst_addr | string | The string form of the receiver's IP address. This provides a way to discover the external address (after NAT). |
Pong
Name | Type | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req_hash | ByteArray[32] | The blake2b digest of the corresponding received Ping. | ||||||||||||
services | Services supported by the Pong sender.
| |||||||||||||
dst_addr | string | the string form of the receiver's IP address. This MUST mirror the src_addr of the Ping's IP packet. It provides a way to discover the external address (after NAT). |
DiscoveryRequest
Name | Type | Description |
---|---|---|
timestamp | time | The unix timestamp of the DiscoveryRequest. |
DiscoveryResponse
Name | Type | Description | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
req_hash | ByteArray[32] | The blake2b digest of the corresponding received DiscoveryRequest. | ||||||||||||||||||||||||
peers | The list of some randomly chosen peers known by the sender of the DiscoveryResponse |
Name | Type | Description | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
public_key | ByteArray[32] | The ed25519 public key of the peer's identity used to verify its signature. | ||||||||||||
ip | string | The string form of the peer's IP address. | ||||||||||||
services | Services supported by the peer.
|
3.3.2.6 Denial of Service
All the requests and responses exchanged during the Peer Discovery protocol are sent via UDP. As such, any UDP based Denial of Service attack could harm the normal functionality of the protocol. To limit this, hardware based protection such as Firewall or alternatively, rate limiting the incoming requests and responses via leaky/token buckets based techniques could be used.