Skip to content

Latest commit

 

History

History
291 lines (217 loc) · 8.34 KB

v0.44-v0.45.md

File metadata and controls

291 lines (217 loc) · 8.34 KB

Migrating to libp2p@45

A migration guide for refactoring your application code from libp2p v0.44.x to v0.45.0.

Table of Contents

Services

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('...'))

Events

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.

Emitters

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', () => {})

Event changes

Some types have changed.

Please see the API docs for an exhaustive list of events emitted by Libp2p.

peer:connect

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.

peer:disconnect

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.

peer:update

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.

self:peer:update

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.

Atomic peer store methods

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
  }
})

Do not dial private addresses by default in browsers

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
})