A migration guide for refactoring your application code from libp2p v0.46
to v1.0.0
.
All exports from @libp2p/interface
and @libp2p/interface-internal
are
now exported from the root of the module, no more having to work out which
subpath to import from.
Before
import type { PeerId } from '@libp2p/interface/peer-id'
import type { Connection } from '@libp2p/interface/connection'
import type { Stream } from '@libp2p/interface/stream-muxer'
After
import type { Connection, PeerId, Stream } from '@libp2p/interface'
js-libp2p
has always had a focus on being a modular, composable ecosystem of
modules. v1.x.x
takes this further and extracts all of the optional
functionality out of the core and into modules that you can include in your app
if you need them, or omit them if you don't.
These modules can be configured as services to enable optional functionality.
The AutoNAT service is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { autoNATService } from 'libp2p/autonat'
const node = await createLibp2p({
services: {
autoNAT: autoNATService()
}
})
After
import { createLibp2p } from 'libp2p'
import { autoNAT } from '@libp2p/autonat'
const node = await createLibp2p({
services: {
autoNAT: autoNAT()
}
})
The Ping service is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { pingService } from 'libp2p/ping'
const node = await createLibp2p({
services: {
ping: pingService()
}
})
After
import { createLibp2p } from 'libp2p'
import { ping } from '@libp2p/ping'
const node = await createLibp2p({
services: {
ping: ping()
}
})
The Identify service is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { identifyService } from 'libp2p/identify'
const node = await createLibp2p({
services: {
identify: identifyService()
}
})
After
import { createLibp2p } from 'libp2p'
import { identify } from '@libp2p/identify'
const node = await createLibp2p({
services: {
identify: identify()
}
})
The DCUtR service is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { dcutrService } from 'libp2p/dcutr'
const node = await createLibp2p({
services: {
dcutr: dcutrService()
}
})
After
import { createLibp2p } from 'libp2p'
import { dcutr } from '@libp2p/dcutr'
const node = await createLibp2p({
services: {
dcutr: dcutr()
}
})
The Fetch service is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { fetchService } from 'libp2p/fetch'
const node = await createLibp2p({
services: {
fetch: fetchService()
}
})
After
import { createLibp2p } from 'libp2p'
import { fetch } from '@libp2p/fetch'
const node = await createLibp2p({
services: {
fetch: fetch()
}
})
The UPnPNat service module is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { uPnPNATService } from 'libp2p/upnp-nat'
const node = await createLibp2p({
services: {
uPnPNAT: uPnPNATService()
}
})
After
import { createLibp2p } from 'libp2p'
import { uPnPNAT } from '@libp2p/upnp-nat'
const node = await createLibp2p({
services: {
uPnPNAT: uPnPNAT()
}
})
The Perf service module exports have been renamed in line with the other changes here.
Before
import { createLibp2p } from 'libp2p'
import { perService } from '@libp2p/perf'
const node = await createLibp2p({
services: {
perf: perService()
}
})
After
import { createLibp2p } from 'libp2p'
import { perf } from '@libp2p/perf'
const node = await createLibp2p({
services: {
perf: perf()
}
})
The Plaintext connection encrypter module is now published in its own package.
Note that it is still insecure and should not be used in production.
Before
import { createLibp2p } from 'libp2p'
import { plaintext } from 'libp2p/insecure'
const node = await createLibp2p({
connectionEncryption: [
plaintext: plaintext()
]
})
After
import { createLibp2p } from 'libp2p'
import { plaintext } from '@libp2p/plaintext'
const node = await createLibp2p({
connectionEncryption: [
plaintext: plaintext()
]
})
The KeyChain object is no longer included on Libp2p and must be instantiated explicitly if desired.
Before
import type { KeyChain } from '@libp2p/interface/keychain'
const libp2p = await createLibp2p(...)
const keychain: KeyChain = libp2p.keychain
After
import { keychain, type Keychain } from '@libp2p/keychain'
const libp2p = await createLibp2p({
...
services: {
keychain: keychain()
}
})
const keychain: Keychain = libp2p.services.keychain
The pnet module is now published in its own package.
Before
import { createLibp2p } from 'libp2p'
import { preSharedKey, generateKey } from 'libp2p/pnet'
const node = await createLibp2p({
// ...other options
connectionProtector: preSharedKey({
psk: generateKey(new Uint8Array(95))
})
})
After
import { createLibp2p } from 'libp2p'
import { preSharedKey, generateKey } from '@libp2p/pnet'
const node = await createLibp2p({
// ...other options
connectionProtector: preSharedKey({
psk: generateKey(new Uint8Array(95))
})
})
The libp2p PeerId
is a public/private keypair which is encrypted at rest by libp2p's @libp2p/keychain
module.
As of libp2p@1.0.0
the keychain is no longer part of the default bundle so to replicate this behaviour you need to use the @libp2p/keychain
module to load a peer id from the datastore before starting your node.
import { keychain, type KeychainInit } from '@libp2p/keychain'
import { defaultLogger } from '@libp2p/logger'
import { LevelDatastore } from 'datastore-level'
import { Key } from 'interface-datastore/key'
import { createLibp2p } from 'libp2p'
// the datastore should come from your config
const datastore = new LevelDatastore('/path/to/db')
// the keychain options should come from your config
const keychainInit: KeychainInit = {
pass: 'very long, very secure password'
}
// if peerId is undefined, one will be generated
let peerId: PeerId | undefined
// try to load it from the keychain
const chain = keychain(keychainInit)({
datastore,
logger: defaultLogger()
})
const selfKey = new Key('/pkcs8/self')
if (await datastore.has(selfKey)) {
// load the peer id from the keychain
peerId = await chain.exportPeerId('self')
}
const node = await createLibp2p({
peerId
// ... other options
})
if (peerId != null && !await datastore.has(selfKey)) {
// a new PeerId would have been generated so store it in the keychain for next time
await chain.importPeer('self', libp2p.peerId)
}
The following metrics were renamed:
libp2p_dialler_pending_dials
=> libp2p_dial_queue_pending_dials
libp2p_dialler_in_progress_dials
=> libp2p_dial_queue_in_progress_dials
The observed behavior of dialing peers has been that given a list of supported addresses, if any one routable address would succeed then all would succeed and that if any routable address would fail then all would fail.
Consequently the previous dial behaviour of dialing all available addresses (up to a concurrency limit) and cancelling any in-flight dials when the first succeeds was a very inefficient use of resources.
Since libp2p@0.46.10
we have only dialed one address at a time for each peer by setting the default value of the ConnectionManager
's maxParallelDialsPerPeer
option to 1
. As of libp2p@1.0.0
this option has been removed.