Skip to content

Latest commit

 

History

History
327 lines (246 loc) · 9.65 KB

v0.27-v.28.md

File metadata and controls

327 lines (246 loc) · 9.65 KB

Migrating to the libp2p@0.28 API

A migration guide for refactoring your application code from libp2p v0.27.x to v0.28.0.

Table of Contents

PeerStore API

In libp2p@0.27 we integrated the PeerStore (former peer-book) into the codebase. By that time, it was not documented in the API DOC since it kept the same API as the peer-book and it was expected to be completelly rewritten in libp2p@0.28.

Moving towards a separation of concerns regarding known peers' data, as well as enabling PeerStore persistence, the PeerStore is now divided into four main components: AddressBook, ProtoBook, KeyBook and MetadataBook. This resulted in API changes in the PeerStore, since each type of peer data should now be added in an atomic fashion.

Adding a Peer

Before

const peerId = ...
const peerInfo = new PeerInfo(peerId)

peerInfo.protocols.add('/ping/1.0.0')
peerInfo.protocols.add('/ping/2.0.0')
peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/0')

libp2p.peerStore.put(peerInfo)

After

const peerId = ...
const protocols = ['/ping/1.0.0', 'ping/2.0.0']
const multiaddrs = ['/ip4/127.0.0.1/tcp/0']

libp2p.peerStore.protoBook.add(peerId, protocols)
libp2p.peerStore.addressBook.add(peerId, multiaddrs)

Getting a Peer

Before

const peerId = ...
const peerInfo = libp2p.peerStore.get(peerId)
// { id: PeerId, multiaddrs: MultiaddrSet, protocols: Set<string>}

After

const peerId = ...
const peer = libp2p.peerStore.get(peerId)
// { id: PeerId, addresses: Array<{ multiaddr: Multiaddr }>, protocols: Array<string> }

Checking for a Peer

Before

const peerId = ...
const hasData = libp2p.peerStore.has(peerId)

After

const peerId = ...
const hasData = Boolean(libp2p.peerStore.get(peerId))

Removing a Peer

Before

libp2p.peerStore.remove(peerId)

After

// Atomic
libp2p.peerStore.protoBook.delete(peerId)
libp2p.peerStore.addressBook.delete(peerId)
// Remove the peer and ALL of its associated data
libp2p.peerStore.delete(peerId)

Get all known Peers

Before

const peers = libp2p.peerStore.peers
// Map<string, PeerInfo>

After

const peers = libp2p.peerStore.peers
// Similar to libp2p.peerStore.get()
// Map<string, { id: PeerId, addresses: Array<{ multiaddr: Multiaddr }>, protocols: Array<string> }

Migrating from Peer Info

PeerInfo is a libp2p peer abstraction layer that combines a PeerId with known data of the peer, namely its multiaddrs and protocols. It has been used for a long time by js-libp2p and its modules to carry this data around the libp2p stack, as well as by the libp2p API, both for providing this data to the users or to receive it from them.

Since this PeerInfo instances were navigating through the entire codebases, some data inconsistencies could be observed in libp2p. Different libp2p subsystems were running with different visions of the known peers data. For instance, a libp2p subsystem receives a copy of this instance with the peer multiaddrs and protocols, but if new data of the peer is obtained from other subsystem, it would not be updated on the former. Moreover, considering that several subsystems were modifying the peer data, libp2p had no way to determine the accurate data.

Considering the complete revamp of the libp2p PeerStore towards its second version, the PeerStore now acts as the single source of truth, we do not need to carry PeerInfo instances around. This also solves all the problems stated above, since subsystems will report new observations to the PeerStore.

Create

While it was possible to create a libp2p node without providing a PeerInfo, there were 2 use cases where a PeerInfo was provided when creating a libp2p node.

Using an existing PeerId

libp2p.create receives a peerId property instead of a peerInfo property.

Before

const peerId = ...
const peerInfo = new PeerInfo(peerId)

const libp2p = await Libp2p.create({
  peerInfo
  // ...
})

After

const peerId = ...

const libp2p = await Libp2p.create({
  peerId
  // ...
})

Providing listen addresses

Before

const peerId = ...
const peerInfo = new PeerInfo(peerId)

peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/0')

const libp2p = await Libp2p.create({
  peerInfo
  // ...
})

await libp2p.start()

After

const peerId = ...

const libp2p = await Libp2p.create({
  peerId,
  addresses: {
    listen: ['/ip4/127.0.0.1/tcp/0']
  }
  // ...
})
await libp2p.start()

There is also a bonus regarding the peer addresses. libp2p@0.28 comes with an AddressManager that also allows the configuration of announce and noAnnounce addresses. This was possible to achieve before, but in a hacky way by removing or adding addresses to the peerInfo, after the node starts.

Before

const peerId = ...
const peerInfo = new PeerInfo(peerId)

peerInfo.multiaddrs.add('/ip4/127.0.0.1/tcp/8000')

const libp2p = await Libp2p.create({
  peerInfo
  // ...
})

await libp2p.start()
peerInfo.multiaddrs.add('/dns4/peer.io') // Announce
peerInfo.multiaddrs.delete('/ip4/127.0.0.1/tcp/8000') // Not announce

After

const peerId = ...

const libp2p = await Libp2p.create({
  peerId,
  addresses: {
    listen: ['/ip4/127.0.0.1/tcp/8000'],
    announce: ['/dns4/peer.io'],
    noAnnounce: ['/ip4/127.0.0.1/tcp/8000']
  }
  // ...
})
await libp2p.start()

API Implications

Peer Dialing, Hangup and Ping

libp2p.dial, libp2p.dialProtocol, libp2p.hangup and libp2p.ping supported as the target parameter a PeerInfo, a PeerId, a Multiaddr and a string representation of the multiaddr. Considering that PeerInfo is being removed from libp2p, all these methods will now support the other 3 possibilities.

There is one relevant aspect to consider with this change. When using a PeerId, the PeerStore MUST have known addresses for that peer in its AddressBook, so that it can perform the request. This was also true in the past, but it is important pointing it out because it might not be enough to switch from using PeerInfo to PeerId. When using a PeerInfo, the PeerStore was not required to have the multiaddrs when they existed on the PeerInfo instance.

Before

const peerInfo = ... // PeerInfo containing its multiaddrs

const connection = await libp2p.dial(peerInfo)

After

const peerId = ...

// Known multiaddrs should be added to the PeerStore
libp2p.peerStore.addressBook.add(peerId, multiaddrs)

const connection = await libp2p.dial(peerId)

Content Routing and Peer Routing

Both content-routing and peer-routing interfaces were modified to not return a 'PeerInfo' instance.

Before

for await (const peerInfo of libp2p.contentRouting.findProviders(cid)) {
  // peerInfo is a PeerInfo instance
}

After

for await (const peer of libp2p.contentRouting.findProviders(cid)) {
  // { id: PeerId, multiaddrs: Multiaddr[] }
}

Before

const peerInfo = await libp2p.peerRouting.findPeer(peerId)
// peerInfo is a PeerInfo instance

After

const peer = await libp2p.peerRouting.findPeer(peerId)
// { id: PeerId, multiaddrs: Multiaddr[] }

Connection Manager and Registrar

Registrar was introduced in libp2p@0.27 along with libp2p topologies. Registrar and ConnectionManager were both listening on new connections and keeping their record of the open connections with other peers.

The registrar API was not documented in the API DOC. However, it exposed a useful method for some libp2p users, libp2p.registrar.getConnection(). On the other hand, the connection Manager did not provide any methods to access its stored connections. On libp2p@0.28 we removed this data duplication and the connections are handled solely by the ConnectionManager.

Before

const connection = libp2p.registrar.getConnection(peerId)

After

const connection = libp2p.connectionManager.get(peerId)

Events

Connection Events

Libp2p emits events whenever new connections are established. These emitted events previously providing the PeerInfo of the peer that connected. In libp2p@0.28 these events are now emitted from the Connection Manager and will now emit the connection itself.

Before

libp2p.on('peer:connect', (peerInfo) => {
// PeerInfo instance
})

libp2p.on('peer:disconnect', (peerInfo) => {
// PeerInfo instance
})

After

libp2p.connectionManager.on('peer:connect', (connection) => {
// Connection instance
})

libp2p.connectionManager.on('peer:disconnect', (connection) => {
// Connection instance
})

Peer Discovery

Before

libp2p.on('peer:discovery', (peerInfo) => {
// PeerInfo instance
})

After

libp2p.on('peer:discovery', (peerId) => {
// peerId instance
})