Notes on the Identify family of protocols & IDService

I was going through to the Identify family of protocols to improve my own understanding of how it works.
Here are some notes:


The Identify message

The Identify message that we send to another peer is connection scoped AND contains roughly the following information:

  • The protocols we support/have handlers for.
  • The address of the remote peer that we are observing on the connection i.e. Connection.RemoteMultiaddr().
  • Our Public Key.
  • The addresses we are listening on i.e. host.Addrs:
    * We include loopback addresses here ONLY if either the local address of the connection or the remote address of the connection are loopback addresses. The idea is that our loopback addresses are of interest to the other party ONLY if we are running on the same machine.
  • Some auxiliary information like ProtocolVersion, AgentVersion etc. though I don’t think it’s used anywhere right now.
  • The Delta i.e. what protocols have we newly added & removed support for.

All fields except the last field i.e. Delta are sent as part of the Identify & Identify Push protocols whereas Delta and ONLY Delta is sent as part of the Identify Delta protocol.


The Protocols

The IDService supports three protocols:

Identify

When we form a new outbound connection with a peer, we open an Identify stream with the peer & the peer sends us an Identify message. On receiving the Identify message(let’s call this a “reponse”), we:

  • Remove the existing set of protocols supported by the remote peer in the peerstore and then add the protocols sent by the remote peer in the message.
  • Update the public key for the remote peer in the peerstore.
  • Add our observed address sent by the remote peer in the message to the set of observed addresses that the IDService maintains. I’ve yet to dig into how these are used later on by the peer.
  • Remove the current listen addresses of the remote peer from the peerstore and then add the listen addresses sent by the remote peer to the peerstore. If we are connected to the remote peer when we do this, we add them with a “permanent” TTL otherwise we add them with a TTL of 10 minutes.

When a remote peer forms a new inbound connection to us, it opens an Identify stream with us -> we respond with an Identify message and it’s consumed on the remote peer exactly as described above.

Thus, peers on both side of the connection learn useful information about each other.

  • Once we have successfully consumed an Identify message sent by a remote peer, we emit a EvtPeerIdentificationCompleted event on the event bus.
  • If anything goes wrong in cosuming an Identify message/remote peer fails to send it, we emit a EvtPeerIdentificationFailed event on the event bus.

When we disconnect from a peer, the IDService sets the TTL for the addresses of the peer to 10 minutes so they can evicted later if we don’t reconnect to the peer within that interval.

Identify Push

The set of addresses a peer listens on is not a constant. It’s important for peers we are connected to/peers in general to know our updated listen addresses so they can share it with others & use it for new connections. To accomplish this, we have the Identify Push protocol:

  • We currently poll the host addresses every minute & if we detect a change in the addresses, we craft an Identify message as described before. Among other things, it will include the new set of listen addresses. Note that this Identify message is a snapshot & not a delta.
  • We then concurrently open an Identify Push stream to all peers we are connected to that support the Identify Push protocol and send them the message.
  • The peers that receive the message will consume the message in exactly the same way that they consume a response of the Identify protocol.
  • Work is currently underway for the Host to emit an EvtLocalAddressesUpdated event when it’s listen addresses change & for the Identify Push to get triggered in response to that event. PR is at https://github.com/libp2p/go-libp2p/pull/747/files. The PR also replaces the current set of listen addresses we send with “Signed Peer Records” but that is outside the scope of this writeup.

Identify Delta

  • When a peer update’s the set of protocols it supports i.e. adds support for a protocol or removes support for a protocol, it emits an EvtLocalProtocolsUpdated event on the event bus.
  • In response to this event, we craft an Identify message with ONLY it’s Delta field initialized with the protocols that have been newly added/removed. This is not a snapshot.
  • We then concurrently open an Identify Delta stream to all peers we are connected to that support the Identify Delta protocol and send them the message.
  • On receiving an Identify Delta message, the receiving peer adds/removes the protocols for the peer from the peerstore.
  • It then emits an EvtPeerProtocolsUpdated event to the event bus.

Note: The Identify Delta, Identify Push & Identify protocols can race with each other thus leading to a bad state in the peerstore for the peer. Work is currently underway at https://github.com/libp2p/go-libp2p/issues/823 to incrementally improve this.