A migration guide for refactoring your application code from libp2p v0.44.x to v0.45.0.
libp2p now accepts arbitrary service modules that can use internal components to fulfil their functions.
This reduces the attack surface area of libp2p nodes as less functionality is enabled by default, and with tree shaking less code will be included in bundles making for faster downloads when used in browsers.
Several optional modules have been removed and must now be configured as services:
Before
import { createLibp2p } from 'libp2p'
import { circuitRelayServer } from 'libp2p/circuit-relay'
import { kadDHT } from '@libp2p/kad-dht'
import { gossipSub } from '@ChainSafe/libp2p-gossipsub'
const node = createLibp2p({
// ... other options here
identify: {
/** identify options **/
},
ping: {
/** ping options **/
},
fetch: {
/** fetch options **/
},
nat: {
/** UPnP NAT options **/
},
autonat: {
/** AutoNAT options **/
},
pubsub: gossipSub(),
dht: kadDHT(),
relay: circuitRelayServer()
})
After
import { createLibp2p } from 'libp2p'
import { autoNATService } from 'libp2p/autonat'
import { circuitRelayServer } from 'libp2p/circuit-relay'
import { identifyService } from 'libp2p/identify'
import { pingService } from 'libp2p/ping'
import { fetchService } from 'libp2p/fetch'
import { uPnPNATService } from 'libp2p/upnp-nat'
import { kadDHT } from '@libp2p/kad-dht'
import { gossipsub } from '@ChainSafe/libp2p-gossipsub'
const node = createLibp2p({
// ... other options here
services: {
identify: identifyService({
/** identify options **/
}),
ping: pingService({
/** ping options **/
}),
fetch: fetchService({
/** fetch options **/
}),
uPnPNAT: uPnPNATService({
/** UPnP NAT options **/
}),
autoNAT: autoNATService({
/** AutoNAT options **/
}),
pubsub: gossipsub(),
dht: kadDHT(),
relay: circuitRelayServer()
}
})
Configured services can be accessed via the .services
key:
const result = await node.services.ping.ping(multiaddr('...'))
The events emitted by libp2p have been refactored to be more consistent and to give more insight into the inner workings of libp2p.
Please see the API docs for an exhaustive list of events emitted by Libp2p.
The primary interaction point for events is now the libp2p node itself, no need to access internal properties to set up listeners.
Before
import { createLibp2p } from 'libp2p'
const node = await createLibp2p({ /* ... */ })
node.connectionManager.addEventListener('peer:connect', () => {})
After
import { createLibp2p } from 'libp2p'
const node = await createLibp2p({ /* ... */ })
node.addEventListener('peer:connect', () => {})
Some types have changed.
Please see the API docs for an exhaustive list of events emitted by Libp2p.
The detail field for this event was a Connection now it is a PeerId
It is emitted when a new peer opens it's first connection.
To receive notifications of the opening of individual connections, listen for the connection:open
event instead.
The detail field for this event was a Connection now it is a PeerId
It is emitted when all connections for the peer have been closed.
To receive notifications of the closing of individual connections, listen for the connection:close
event instead.
This event is emitted when a peer's data has been changed in the peer store. This can be in response to a user manually updating the peer, or after the Identify protocol has completed.
This event occurs when the data of the running node has changed. It may have started listening on a new multiaddr, AutoNAT may have given us new confidence in an external address or a user may have manually updated the information.
The libp2p peer store has been refactored to reduce the number of methods it exposes.
Previously it had separate components for managing addresses, protocols, metadata, etc, all of which exposed async methods which meant updating the data for a peer could involve multiple async calls which required complicated internal locking mechanisms which introduced a lot of latency into libp2p nodes performing many peer operations.
The updated peer store has simple save
, patch
and merge
methods which update all fields in a peer's stored data at once.
Before
import { createLibp2p } from 'libp2p'
const node = await createLibp2p({ /* ... */ })
// add addresses
await node.peerStore.addressBook.add(peerId, [
multiaddr('/ip4/43.14.67.21/tcp/3847')
])
// set protocols
await node.peerStore.protoBook.set(peerId, [
'/a-proto/1.0.0',
'/another-proto/1.0.0'
])
// add metadata
await node.peerStore.metadataBook.set(peerId, 'key', Uint8Array.from([0, 1, 2, 3]))
// add tags
await node.peerStore.tagPeer(peerId, 'tag-name', { value: 10 })
After
import { createLibp2p } from 'libp2p'
const node = await createLibp2p({ /* ... */ })
// `save` replaces all data for the peer. Use with caution - any fields not passed
// will be deleted
await node.peerStore.save(peerId, {
multiaddrs: [
multiaddr('/ip4/43.14.67.21/tcp/3847')
],
protocols: [
'/a-proto/1.0.0',
'/another-proto/1.0.0'
],
metadata: {
key: Uint8Array.from([0, 1, 2, 3])
},
tags: {
'tag-name': { value: 10 }
}
})
Other ways to update peers are available which are more concise and allow you to just update specific fields:
// `patch` replaces only the passed fields and retains all other data
await node.peerStore.patch(peerId, {
multiaddrs: [
multiaddr('/ip4/43.14.67.21/tcp/3847')
]
})
// `merge` behaves like `patch` but deeply merges multiaddrs, protocols, metadata,
// and tags, removing duplicates. any existing metadata/tags with the same
// keys/tag names will be overwritten.
await node.peerStore.merge(peerId, {
multiaddrs: [
multiaddr('/ip4/43.14.67.21/tcp/3847')
]
})
You can also remove fields quickly:
// passing `undefined` to `merge` is a quick way of deleting metadata/tags
await node.peerStore.merge(peerId, {
metadata: {
key: undefined
},
tags: {
'tag-name': undefined
}
})
Browsers are incredibly resource-constrained environments in which to run a network-heavy program like libp2p.
This is compounded by the fact that remote peers often include private network addresses in their peer records, so a libp2p node will often waste resources by trying to dial unroutable addresses.
The default connection gater used by libp2p in browsers will filter out any private addresses and not attempt to dial them.
No change has been made to the connection gater used by Node.js.
This can be re-enabled by configuring a more permissive connection gater:
Before
import { createLibp2p } from 'libp2p'
const node = createLibp2p({
// ... other options here
})
After
import { createLibp2p } from 'libp2p'
const node = createLibp2p({
connectionGater: {
denyDialMultiaddr: () => false
}
// ... other options here
})