Skip to content

Latest commit

 

History

History
262 lines (205 loc) · 7.8 KB

v0.36-v.037.md

File metadata and controls

262 lines (205 loc) · 7.8 KB

Migrating to libp2p@37

A migration guide for refactoring your application code from libp2p v0.36.x to v0.37.0.

Table of Contents

ESM

The biggest change to [email protected] is that the module is now ESM-only.

ESM is the module system for JavaScript, it allows us to structure our code in separate files without polluting a global namespace.

Other systems have tried to fill this gap, notably CommonJS, AMD, RequireJS and others, but ESM is the official standard format to package JavaScript code for reuse.

TypeScript

The core libp2p module and all supporting modules have now been ported to TypeScript in a complete ground-up rewrite. This will not have a huge impact on most application code, but those that are type-aware, either by being written in TypeScript themselves or using JSDoc comments will notice full type completion and better error message when coding against the libp2p API.

To reflect the updated nature of these modules, all ecosystem modules have been moved to the @libp2p org on npm, so libp2p-tcp has become @libp2p/tcp, libp2p-mplex has become @libp2p/mplex and so on. @chainsafe/libp2p-noise and libp2p-gossipsub are unaffected.

Config

Because libp2p is now fully typed it was necessary to refactor the configuration object passed to the libp2p constructor. The reason being, it previously accepted config objects to pass to the constructors of the various modules - to type those we'd need to know the types of all possible modules in advance which isn't possible.

The following changes have been made to the configuration object:

  1. It now takes instances of modules rather than their classes
  2. Keys from the config and modules objects have been migrated to the root of the object
  3. Use of the enabled flag has been removed - if you don't want a particular feature enabled, don't pass a module implementing that feature
  4. Some keys have been renamed = transport -> transports, streamMuxer -> streamMuxers, connEncryption -> connectionEncryption, etc
  5. Keys from config.dialer have been moved to config.connectionManager as the connection manager is now responsible for managing connections
  6. The protocolPrefix configuration option is now passed on a per-protocol basis for identify, fetch and ping

Before

import Libp2p from 'libp2p'
import TCP from 'libp2p-tcp'
import Mplex from 'libp2p-mplex'
import { NOISE } from '@chainsafe/libp2p-noise'
import Gossipsub from 'libp2p-gossipsub'
import KadDHT from 'libp2p-kad-dht'
import Bootstrap from 'libp2p-bootstrap'
import MulticastDNS from 'libp2p-mdns'

const node = await Libp2p.create({
  addresses: {
    listen: ['/ip4/127.0.0.1/tcp/8000']
  },
  modules: {
    transport: [
      TCP
    ],
    streamMuxer: [
      Mplex
    ],
    connEncryption: [
      NOISE
    ],
    dht: KadDHT,
    pubsub: Gossipsub,
    peerDiscovery: [
      Bootstrap,
      MulticastDNS
    ]
  },
  protocolPrefix: 'ipfs',
  config: {
    peerDiscovery: {
      autoDial: true,
      [MulticastDNS.tag]: {
        interval: 1000,
        enabled: true
      },
      [Bootstrap.tag]: {
        list: [
          // .. multiaddrs here
        ],
        interval: 2000,
        enabled: true
      }
    },
    dialer: {
      dialTimeout: 60000
    }
  }
})

After

import { createLibp2p } from 'libp2p'
import { TCP } from '@libp2p/tcp'
import { Mplex } from '@libp2p/mplex'
import { Noise } from '@chainsafe/libp2p-noise'
import Gossipsub from '@chainsafe/libp2p-gossipsub'
import { KadDHT } from '@libp2p/kad-dht'
import { Bootstrap } from '@libp2p/bootstrap'
import { MulticastDNS } from '@libp2p/mdns'

const node = await createLibp2p({
  addresses: {
    listen: ['/ip4/127.0.0.1/tcp/8000']
  },
  addressManager: {
    autoDial: true
  },
  connectionManager: {
    dialTimeout: 60000
  },
  transports: [
    new TCP()
  ],
  streamMuxers: [
    new Mplex()
  ],
  connectionEncryption: [
    new Noise()
  ],
  dht: new KadDHT(),
  pubsub: new Gossipsub(),
  peerDiscovery: [
    new Bootstrap({
      list: [
        // .. multiaddrs here
      ],
      interval: 2000
    }),
    new MulticastDNS({
      interval: 1000
    })
  ],
  identify: {
    protocolPrefix: 'ipfs'
  }
})

Bundled modules

Previously you'd have to use deep import paths to get at bundled modules such as the private network module.

Access to these modules is now controlled by the package.json export map so your import paths will need to be updated:

Before

import plaintext from 'libp2p/src/insecure/plaintext.js'
import Protector from 'libp2p/src/pnet/index.js'
import generateKey from 'libp2p/src/pnet/key-generator.js'
import TransportManager from 'libp2p/src/transport-manager.js'

After

import { Plaintext } from 'libp2p/insecure'
import { PreSharedKeyConnectionProtector, generateKey } from 'libp2p/pnet'
import { TransportManager } from 'libp2p/transport-manager'

Events

To reduce our dependency on Node.js internals, use of EventEmitter has been replaced with the standard EventTarget.

The EventTarget API is very similar to HTML DOM Events used by the browser.

All events are instances of the CustomEvent class. Event-specific information can be accessed via the .detail property of the passed event.

They type of event emitted can be inferred from the types for each event emitter.

Before

const handler = (peerInfo) => {
  //...
}

// listen for event
libp2p.on('peer:discovery', handler)

// stop listening for event
libp2p.removeListener('peer:discovery', handler)
libp2p.off('peer:discovery', handler)

After

const handler = (event) => {
  const peerInfo = event.detail
  //...
}

// listen for event
libp2p.addEventListener('peer:discovery', handler)

// stop listening for event
libp2p.removeEventListener('peer:discovery', handler)

Pubsub

Similar to the events refactor above, pubsub is now driven by the standard EventTarget API.

You can still subscribe to events without a listener with .subscribe but all other uses now use the standard API.

Similar to the other events emitted by libp2p the event type is CustomEvent. This is part of the js language but at the time of writing Node.js does not support CustomEvent, so a polyfill is supplied as part of the @libp2p/interfaces

Before

const handler = (message: Message) => {
  const topic = message.topic

  //...
}

// listen for event
libp2p.pubsub.subscribe('my-topic')
libp2p.pubsub.on('my-topic', handler)

// send event
libp2p.pubsub.emit('my-topic', Uint8Array.from([0, 1, 2, 3]))

// stop listening for event
libp2p.unsubscribe('my-topic', handler)
libp2p.pubsub.off('my-topic', handler)

After

import type { Message } from '@libp2p/interface-pubsub'

const handler = (event: CustomEvent<Message>) => {
  const message = event.detail
  const topic = message.topic

  //...
}

// listen for event
libp2p.pubsub.subscribe('my-topic')
libp2p.pubsub.addEventListener('message', handler)

// send event
libp2p.pubsub.publish('my-topic', Uint8Array.from([0, 1, 2, 3]))

// stop listening for event
libp2p.pubsub.unsubscribe('my-topic')
libp2p.pubsub.removeEventListener('message', handler)