LibP2P: Multiaddr - Enode - ENR ?!

I’ve recently been working on a couple of Eth1.0 and Eth2.0 related projects diving deeper into the world of P2P networking (libp2p). One aspect that I found interesting is that there are multiple ways to convey a node’s peer-to-peer address and identity. multiaddr, enode, and ENR are the ones used in the Ethereum network stack. In this article, we are going to shed some light on them.

As it can be cumbersome to convert between the various address types (from ENR to enode or multiaddr) we’ve thrown together a web-app for you to play around with as you read along.

LibP2P - Address Converter Web-App

LibP2P - Address Converter Web-App

Multiaddr

The multiaddr standard aims to establish unambiguous “self-describing network addresses.” The format can be used universally and is not bound to just peer-to-peer or Ethereum use-cases. Addresses are represented as layered key-value pairs with the human-readable representation looking like this:

/ip4/192.168.86.67/tcp/13000

  • IPv4 network address 192.168.86.67
  • Service listening on TCP port 13000

Another example is the multiaddr /dns4/foo.com/tcp/80/http/bar/baz.jpg. The resource it describes can also be represented by the URL http://foo.com/bar/baz.jpg. The big difference, however, is that the multiaddr is more explicit than the URL notation. For example, the multiaddr requires that foo.com resolves to an IPv4 address while the URL notation leaves it up to the implementation.

Let’s look at another example. The IPv4-Port tuple 127.0.0.1:9090 does not provide any information about the type of transport (TCP, UDP, other) being used. The address type is inferred from the length of the network address instead of being explicitly stated and if the address was a DNS domain name it could resolves to IPv4 or IPv6 or both.

Coming back to the Ethereum p2p network, a peer is identified by their peer- or node-id, which is derived from the peer’s public key (keccak(pubkey_bytes)). This peer identification can be appended to the multiaddress to allow the connecting node to verify that it is actually talking to the node it is expecting to talk to.

Consider this example of an ethereum node’s multiaddress:

/ip4/192.168.86.67/tcp/13000/p2p/7y9Qv7mG2h6fnzcDkeqVsEvW2rU9PdybSZ8y1dCrB9pe

  • IPv4 network address: 192.168.86.67
  • Transport protocol, listening port (TCP): 13000
  • P2P node-id in base58 encoded format: 7y9Qv7mG2h6fnzcDkeqVsEvW2rU9PdybSZ8y1dCrB9pe

The multiaddress format is often used when referring to a specific ethereum peer.

You can find more examples in the multiaddr specification.

Enode

Another way to describe an Ethereum node is by using the “enode” URL scheme. This scheme is specifically used in the Ethereum protocol landscape.

Consider this example of an ethereum node’s enode:// URL:

enode://11501bf6f21a04763aedb7b408c14b514de61c29eb9bd902a0884b2f9a2653d5b49fbf0a5019aa707e0fac1efca56c2a4e14293c579eaa3f353cdbafb22d253b@192.168.86.67:13000?discport=30301

  • URL-scheme: enode
  • IPv4 network address: 192.168.86.67 (DNS Domain Names not allowed)
  • Transport protocol, listening port (TCP): 13000
  • Discovery protocol port (UDP): 30301
  • P2P Node’s public-key in hex-encoded format: 11501bf6f21a04763aedb7b408c14b514de61c29eb9bd902a0884b2f9a2653d5b49fbf0a5019aa707e0fac1efca56c2a4e14293c579eaa3f353cdbafb22d253b

The Enode scheme is typically used in the discovery process and when specifying bootstrap nodes. Similar to the multiaddress p2p extension’s node-id, the P2P Node’s public-key is used to authenticate the peer.

Here’s an example for a random enode tracked on ethernodes.org:

enode://00029fb539bbbebc7bcc986bca2b1d3e262a1133901c3cc699f8dd9cba91df51ede5fed9c2c25b74425d64344a9a9d393904c6f0f8bd95cc0c5e2699b6a19ea1@47.94.142.31:53454

Ethereum Node Records (ENR)

ENR’s are specific to the Ethereum network and used by a node to share information about itself. For example, a node can pack arbitrary key-value pairs along with a sequence number into a record and sign it. When ENR information is relayed in the P2P network, each node can verify the authenticity of the record. The record’s sequence number allows peers to detect that their cached record is outdated. Peers can also passively learn from relayed ENRs about other nodes in the network and decide if they want to peer with them. Ideally, the node record would include more information like the purpose of a node on the network, e.g. what network they’re on, their last head, etc.

Consider the following human-readable representation of an ENR:

enr:-LK4QMer7ejH4SWXlSIdM6gOBUD6WH86M95-6ZQ04KOrsAWaDaswyYp9hFmzRpnGVypSlHL_QB2VzNT8ATRckIfnmosBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD9yjmwAAABIf__________gmlkgnY0gmlwhMCoVkOJc2VjcDI1NmsxoQMRUBv28hoEdjrtt7QIwUtRTeYcKeub2QKgiEsvmiZT1YN0Y3CCMsiDdWRwgi7g

The data structure is prefixed by enr: followed by a base64 encoded RLP list containing:

[signature, seq, k=id, v=scheme, (k, v)* ]

  • A cryptographic signature of the record contents
  • A sequence number versioning the record
  • Arbitrary key-value pairs like:

A record is validated according to the identity scheme (id) specified in the record.

TLDR;

We’ve learned that:

  • multiaddr is the libp2p-native way of addressing nodes in a peer-to-peer network, and a generic way to describe an endpoint or resource in a layered network stack. A node’s multiaddr can be recovered from an enode or ENR.
  • enode:// is an Ethereum specific URL-scheme mainly used in the Ethereum node discovery process. The enode notation is superseded by the more versatile ENR structure.
  • An ENR is a signed, versioned record, adhering to an identity scheme that includes an arbitrary list of key-value pairs. The record may be used by neighbors to learn about new peers. Information present in an ENR may be used to derive a node’s network address according to the identity scheme specified. While ENR’s may have a larger footprint, they are more flexible and future proof (versioning, plug-in identity scheme) than the limited enode notation. ENR’s replaced enodes as the format to specify bootstrap nodes for Eth2.0 nodes.

ENR records are RLP-encoded and therefore not immediately human-readable. E.g. to extract a node’s address from an ENR one has to decode the record. To make your life a little bit easier I’ve created a standalone web application that helps you convert ENRenodemultiaddr 🙌.

enr2maddr - Address Converter (GitHub)


Thinking about smart contract security? We can provide training, ongoing advice, and smart contract auditing. Contact us.

All posts chevronRight icon