Skip to content

Commit

Permalink
refactor!: remove libp2p.keychain (#2084)
Browse files Browse the repository at this point in the history
* refactor!: remove libp2p.keychain

* chore: remove @libp2p/keychain dep from libp2p

* chore: add migration documentation

---------

Co-authored-by: Alex Potsides <alex@achingbrain.net>
  • Loading branch information
wemeetagain and achingbrain committed Oct 31, 2023
1 parent fc8f89b commit 3a2d6a0
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 181 deletions.
32 changes: 32 additions & 0 deletions doc/migrations/v0.46-v1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,38 @@ __Describe__
-->

### KeyChain

The KeyChain object is no longer included on Libp2p and must be instantiated explicitly if desired.

**Before**

```ts
import type { KeyChain } from '@libp2p/interface/keychain'

const libp2p = await createLibp2p(...)

const keychain: KeyChain = libp2p.keychain
```

***After***

```ts
import { DefaultKeyChain } from '@libp2p/keychain'
import type { KeyChain } from '@libp2p/interface/keychain'

const libp2p = await createLibp2p({
...
services: {
keychain: (components) => new DefaultKeyChain(components, {
...DefaultKeyChain.generateOptions()
})
}
})

const keychain: KeyChain = libp2p.services.keychain
```

## Module Updates

With this release you should update the following libp2p modules if you are relying on them:
Expand Down
15 changes: 0 additions & 15 deletions packages/interface/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import type { Connection, NewStreamOptions, Stream } from './connection/index.js'
import type { ContentRouting } from './content-routing/index.js'
import type { TypedEventTarget } from './events.js'
import type { KeyChain } from './keychain/index.js'
import type { Metrics } from './metrics/index.js'
import type { PeerId } from './peer-id/index.js'
import type { PeerInfo } from './peer-info/index.js'
Expand Down Expand Up @@ -372,20 +371,6 @@ export interface Libp2p<T extends ServiceMap = ServiceMap> extends Startable, Ty
*/
contentRouting: ContentRouting

/**
* The keychain contains the keys used by the current node, and can create new
* keys, export them, import them, etc.
*
* @example
*
* ```js
* const keyInfo = await libp2p.keychain.createKey('new key')
* console.info(keyInfo)
* // { id: '...', name: 'new key' }
* ```
*/
keychain: KeyChain

/**
* The metrics subsystem allows recording values to assess the health/performance
* of the running node.
Expand Down
1 change: 0 additions & 1 deletion packages/libp2p/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@
"@libp2p/crypto": "^2.0.6",
"@libp2p/interface": "^0.1.4",
"@libp2p/interface-internal": "^0.1.7",
"@libp2p/keychain": "^3.0.6",
"@libp2p/logger": "^3.0.4",
"@libp2p/multistream-select": "^4.0.4",
"@libp2p/peer-collections": "^4.0.6",
Expand Down
1 change: 0 additions & 1 deletion packages/libp2p/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export enum codes {
ERR_NO_ROUTERS_AVAILABLE = 'ERR_NO_ROUTERS_AVAILABLE',
ERR_CONNECTION_NOT_MULTIPLEXED = 'ERR_CONNECTION_NOT_MULTIPLEXED',
ERR_NO_DIAL_TOKENS = 'ERR_NO_DIAL_TOKENS',
ERR_KEYCHAIN_REQUIRED = 'ERR_KEYCHAIN_REQUIRED',
ERR_INVALID_CMS = 'ERR_INVALID_CMS',
ERR_MISSING_KEYS = 'ERR_MISSING_KEYS',
ERR_NO_KEY = 'ERR_NO_KEY',
Expand Down
6 changes: 0 additions & 6 deletions packages/libp2p/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type { PeerId } from '@libp2p/interface/peer-id'
import type { PeerRouting } from '@libp2p/interface/peer-routing'
import type { StreamMuxerFactory } from '@libp2p/interface/stream-muxer'
import type { Transport } from '@libp2p/interface/transport'
import type { KeyChainInit } from '@libp2p/keychain'
import type { PersistentPeerStoreInit } from '@libp2p/peer-store'
import type { Datastore } from 'interface-datastore'

Expand Down Expand Up @@ -79,11 +78,6 @@ export interface Libp2pInit<T extends ServiceMap = { x: Record<string, unknown>
*/
peerStore: PersistentPeerStoreInit

/**
* keychain configuration
*/
keychain: KeyChainInit

/**
* An array that must include at least 1 compliant transport
*/
Expand Down
43 changes: 1 addition & 42 deletions packages/libp2p/src/libp2p.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import { CodeError } from '@libp2p/interface/errors'
import { TypedEventEmitter, CustomEvent } from '@libp2p/interface/events'
import { peerDiscovery } from '@libp2p/interface/peer-discovery'
import { type PeerRouting, peerRouting } from '@libp2p/interface/peer-routing'
import { DefaultKeyChain } from '@libp2p/keychain'
import { logger } from '@libp2p/logger'
import { PeerSet } from '@libp2p/peer-collections'
import { peerIdFromString } from '@libp2p/peer-id'
import { createEd25519PeerId } from '@libp2p/peer-id-factory'
import { PersistentPeerStore } from '@libp2p/peer-store'
import { isMultiaddr, type Multiaddr } from '@multiformats/multiaddr'
import { MemoryDatastore } from 'datastore-core/memory'
import mergeOptions from 'merge-options'
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { DefaultAddressManager } from './address-manager/index.js'
Expand All @@ -31,14 +29,12 @@ import type { Components } from './components.js'
import type { Libp2p, Libp2pInit, Libp2pOptions } from './index.js'
import type { Libp2pEvents, PendingDial, ServiceMap, AbortOptions } from '@libp2p/interface'
import type { Connection, NewStreamOptions, Stream } from '@libp2p/interface/connection'
import type { KeyChain } from '@libp2p/interface/keychain'
import type { Metrics } from '@libp2p/interface/metrics'
import type { PeerId } from '@libp2p/interface/peer-id'
import type { PeerInfo } from '@libp2p/interface/peer-info'
import type { PeerStore } from '@libp2p/interface/peer-store'
import type { Topology } from '@libp2p/interface/topology'
import type { StreamHandler, StreamHandlerOptions } from '@libp2p/interface-internal/registrar'
import type { Datastore } from 'interface-datastore'

const log = logger('libp2p')

Expand All @@ -47,7 +43,6 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
public peerStore: PeerStore
public contentRouting: ContentRouting
public peerRouting: PeerRouting
public keychain: KeyChain
public metrics?: Metrics
public services: T

Expand Down Expand Up @@ -133,13 +128,6 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
// Addresses {listen, announce, noAnnounce}
this.configureComponent('addressManager', new DefaultAddressManager(this.components, init.addresses))

// Create keychain
const keychainOpts = DefaultKeyChain.generateOptions()
this.keychain = this.configureComponent('keyChain', new DefaultKeyChain(this.components, {
...keychainOpts,
...init.keychain
}))

// Peer routers
const peerRouters: PeerRouting[] = (init.peerRouters ?? []).map((fn, index) => this.configureComponent(`peer-router-${index}`, fn(this.components)))
this.peerRouting = this.components.peerRouting = this.configureComponent('peerRouting', new DefaultPeerRouting(this.components, {
Expand Down Expand Up @@ -222,13 +210,6 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends

log('libp2p is starting')

const keys = await this.keychain.listKeys()

if (keys.find(key => key.name === 'self') == null) {
log('importing self key into keychain')
await this.keychain.importPeer('self', this.components.peerId)
}

try {
await this.components.beforeStart?.()
await this.components.start()
Expand Down Expand Up @@ -411,29 +392,7 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
* libp2p interface and is useful for testing and debugging.
*/
export async function createLibp2pNode <T extends ServiceMap = Record<string, unknown>> (options: Libp2pOptions<T>): Promise<Libp2pNode<T>> {
if (options.peerId == null) {
const datastore = options.datastore as Datastore | undefined

if (datastore != null) {
try {
// try load the peer id from the keychain
const keyChain = new DefaultKeyChain({
datastore
}, mergeOptions(DefaultKeyChain.generateOptions(), options.keychain))

options.peerId = await keyChain.exportPeerId('self')
} catch (err: any) {
if (err.code !== 'ERR_NOT_FOUND') {
throw err
}
}
}
}

if (options.peerId == null) {
// no peer id in the keychain, create a new peer id
options.peerId = await createEd25519PeerId()
}
options.peerId ??= await createEd25519PeerId()

return new Libp2pNode(validateConfig(options))
}
116 changes: 0 additions & 116 deletions packages/libp2p/test/core/peer-id.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { webSockets } from '@libp2p/websockets'
import { expect } from 'aegir/chai'
import { MemoryDatastore } from 'datastore-core'
import { createLibp2p, type Libp2p } from '../../src/index.js'
import { plaintext } from '../../src/insecure/index.js'

Expand All @@ -27,119 +26,4 @@ describe('peer-id', () => {

expect(libp2p.peerId).to.be.ok()
})

it('should retrieve the PeerId from the datastore', async () => {
const datastore = new MemoryDatastore()

libp2p = await createLibp2p({
datastore,
transports: [
webSockets()
],
connectionEncryption: [
plaintext()
]
})

// this PeerId was created by default
const peerId = libp2p.peerId

await libp2p.stop()

// create a new node from the same datastore
libp2p = await createLibp2p({
datastore,
transports: [
webSockets()
],
connectionEncryption: [
plaintext()
]
})

// the new node should have read the PeerId from the datastore
// instead of creating a new one
expect(libp2p.peerId.toString()).to.equal(peerId.toString())
})

it('should retrieve the PeerId from the datastore with a keychain password', async () => {
const datastore = new MemoryDatastore()
const keychain = {
pass: 'very-long-password-must-be-over-twenty-characters-long',
dek: {
salt: 'CpjNIxMqAZ+aJg+ezLfuzG4a'
}
}

libp2p = await createLibp2p({
datastore,
keychain,
transports: [
webSockets()
],
connectionEncryption: [
plaintext()
]
})

// this PeerId was created by default
const peerId = libp2p.peerId

await libp2p.stop()

// create a new node from the same datastore
libp2p = await createLibp2p({
datastore,
keychain,
transports: [
webSockets()
],
connectionEncryption: [
plaintext()
]
})

// the new node should have read the PeerId from the datastore
// instead of creating a new one
expect(libp2p.peerId.toString()).to.equal(peerId.toString())
})

it('should fail to start if retrieving the PeerId from the datastore fails', async () => {
const datastore = new MemoryDatastore()
const keychain = {
pass: 'very-long-password-must-be-over-twenty-characters-long',
dek: {
salt: 'CpjNIxMqAZ+aJg+ezLfuzG4a'
}
}

libp2p = await createLibp2p({
datastore,
keychain,
transports: [
webSockets()
],
connectionEncryption: [
plaintext()
]
})
await libp2p.stop()

// creating a new node from the same datastore but with the wrong keychain config should fail
await expect(createLibp2p({
datastore,
keychain: {
pass: 'different-very-long-password-must-be-over-twenty-characters-long',
dek: {
salt: 'different-CpjNIxMqAZ+aJg+ezLfuzG4a'
}
},
transports: [
webSockets()
],
connectionEncryption: [
plaintext()
]
})).to.eventually.rejectedWith('Invalid PEM formatted message')
})
})

0 comments on commit 3a2d6a0

Please sign in to comment.