Skip to content

Commit 01e9a5f

Browse files
authored
chore: remove kad-dht, bootstrap and mdns deps from libp2p (#2313)
Refactors the tests for these modules to test use of the interfaces. This will let us, for example, make a breaking change to `@libp2p/kad-dht` without it causing a major for libp2p. A followup that moves the specific tests to the integration suite will come next.
1 parent 7429155 commit 01e9a5f

File tree

23 files changed

+859
-1331
lines changed

23 files changed

+859
-1331
lines changed

packages/interface/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
"it-pushable": "^3.2.1",
6868
"it-stream-types": "^2.0.1",
6969
"multiformats": "^12.1.3",
70+
"progress-events": "^1.0.0",
7071
"uint8arraylist": "^2.4.3"
7172
},
7273
"devDependencies": {

packages/interface/src/content-routing/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { AbortOptions } from '../index.js'
1+
import type { RoutingOptions } from '../index.js'
22
import type { PeerInfo } from '../peer-info/index.js'
33
import type { CID } from 'multiformats/cid'
44

@@ -23,6 +23,14 @@ import type { CID } from 'multiformats/cid'
2323
*/
2424
export const contentRoutingSymbol = Symbol.for('@libp2p/content-routing')
2525

26+
/**
27+
* Implementers of this interface can provide a ContentRouting implementation to
28+
* interested callers.
29+
*/
30+
export interface ContentRoutingProvider {
31+
[contentRoutingSymbol]: ContentRouting
32+
}
33+
2634
export interface ContentRouting {
2735
/**
2836
* The implementation of this method should ensure that network peers know the
@@ -35,7 +43,7 @@ export interface ContentRouting {
3543
* await contentRouting.provide(cid)
3644
* ```
3745
*/
38-
provide(cid: CID, options?: AbortOptions): Promise<void>
46+
provide(cid: CID, options?: RoutingOptions): Promise<void>
3947

4048
/**
4149
* Find the providers of the passed CID.
@@ -49,7 +57,7 @@ export interface ContentRouting {
4957
* }
5058
* ```
5159
*/
52-
findProviders(cid: CID, options?: AbortOptions): AsyncIterable<PeerInfo>
60+
findProviders(cid: CID, options?: RoutingOptions): AsyncIterable<PeerInfo>
5361

5462
/**
5563
* Puts a value corresponding to the passed key in a way that can later be
@@ -65,7 +73,7 @@ export interface ContentRouting {
6573
* await contentRouting.put(key, value)
6674
* ```
6775
*/
68-
put(key: Uint8Array, value: Uint8Array, options?: AbortOptions): Promise<void>
76+
put(key: Uint8Array, value: Uint8Array, options?: RoutingOptions): Promise<void>
6977

7078
/**
7179
* Retrieves a value from the network corresponding to the passed key.
@@ -79,5 +87,5 @@ export interface ContentRouting {
7987
* const value = await contentRouting.get(key)
8088
* ```
8189
*/
82-
get(key: Uint8Array, options?: AbortOptions): Promise<Uint8Array>
90+
get(key: Uint8Array, options?: RoutingOptions): Promise<Uint8Array>
8391
}

packages/interface/src/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type { StreamHandler, StreamHandlerOptions } from './stream-handler/index
2727
import type { Topology } from './topology/index.js'
2828
import type { Listener } from './transport/index.js'
2929
import type { Multiaddr } from '@multiformats/multiaddr'
30+
import type { ProgressOptions } from 'progress-events'
3031

3132
/**
3233
* Used by the connection manager to sort addresses into order before dialling
@@ -671,6 +672,28 @@ export type RecursivePartial<T> = {
671672
[P in keyof T]?: T[P] extends Array<infer I> ? Array<RecursivePartial<I>> : T[P] extends (...args: any[]) => any ? T[P] : RecursivePartial<T[P]>
672673
}
673674

675+
/**
676+
* When a routing operation involves reading values, these options allow
677+
* controlling where the values are read from. By default libp2p will check
678+
* local caches but may not use the network if a valid local value is found,
679+
* these options allow tuning that behaviour.
680+
*/
681+
export interface RoutingOptions extends AbortOptions, ProgressOptions {
682+
/**
683+
* Pass `false` to not use the network
684+
*
685+
* @default true
686+
*/
687+
useNetwork?: boolean
688+
689+
/**
690+
* Pass `false` to not use cached values
691+
*
692+
* @default true
693+
*/
694+
useCache?: boolean
695+
}
696+
674697
export * from './connection/index.js'
675698
export * from './connection-encrypter/index.js'
676699
export * from './connection-gater/index.js'

packages/interface/src/peer-discovery/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ import type { PeerInfo } from '../peer-info/index.js'
2222
*/
2323
export const peerDiscoverySymbol = Symbol.for('@libp2p/peer-discovery')
2424

25+
/**
26+
* Implementers of this interface can provide a PeerDiscovery implementation to
27+
* interested callers.
28+
*/
29+
export interface PeerDiscoveryProvider {
30+
[peerDiscoverySymbol]: PeerDiscovery
31+
}
32+
2533
export interface PeerDiscoveryEvents {
2634
'peer': CustomEvent<PeerInfo>
2735
}

packages/interface/src/peer-routing/index.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { AbortOptions } from '../index.js'
1+
import type { RoutingOptions } from '../index.js'
22
import type { PeerId } from '../peer-id/index.js'
33
import type { PeerInfo } from '../peer-info/index.js'
44

@@ -23,6 +23,14 @@ import type { PeerInfo } from '../peer-info/index.js'
2323
*/
2424
export const peerRoutingSymbol = Symbol.for('@libp2p/peer-routing')
2525

26+
/**
27+
* Implementers of this interface can provide a PeerRouting implementation to
28+
* interested callers.
29+
*/
30+
export interface PeerRoutingProvider {
31+
[peerRoutingSymbol]: PeerRouting
32+
}
33+
2634
export interface PeerRouting {
2735
/**
2836
* Searches the network for peer info corresponding to the passed peer id.
@@ -34,7 +42,7 @@ export interface PeerRouting {
3442
* const peer = await peerRouting.findPeer(peerId, options)
3543
* ```
3644
*/
37-
findPeer(peerId: PeerId, options?: AbortOptions): Promise<PeerInfo>
45+
findPeer(peerId: PeerId, options?: RoutingOptions): Promise<PeerInfo>
3846

3947
/**
4048
* Search the network for peers that are closer to the passed key. Peer
@@ -49,5 +57,5 @@ export interface PeerRouting {
4957
* }
5058
* ```
5159
*/
52-
getClosestPeers(key: Uint8Array, options?: AbortOptions): AsyncIterable<PeerInfo>
60+
getClosestPeers(key: Uint8Array, options?: RoutingOptions): AsyncIterable<PeerInfo>
5361
}

packages/libp2p/.aegir.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default {
88
// use dynamic import because we only want to reference these files during the test run, e.g. after building
99
const { webSockets } = await import('@libp2p/websockets')
1010
const { mplex } = await import('@libp2p/mplex')
11-
const { noise } = await import('@chainsafe/libp2p-noise')
1211
const { createEd25519PeerId } = await import('@libp2p/peer-id-factory')
1312
const { yamux } = await import('@chainsafe/libp2p-yamux')
1413
const { WebSockets } = await import('@multiformats/mafmt')
@@ -39,7 +38,6 @@ export default {
3938
mplex()
4039
],
4140
connectionEncryption: [
42-
noise(),
4341
plaintext()
4442
],
4543
services: {

packages/libp2p/package.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,9 @@
103103
"interface-datastore": "^8.2.0",
104104
"it-all": "^3.0.2",
105105
"it-drain": "^3.0.2",
106-
"it-filter": "^3.0.1",
107-
"it-first": "^3.0.3",
108106
"it-map": "^3.0.4",
109107
"it-merge": "^3.0.0",
108+
"it-parallel": "^3.0.6",
110109
"it-pipe": "^3.0.1",
111110
"it-stream-types": "^2.0.1",
112111
"merge-options": "^3.0.4",
@@ -119,14 +118,10 @@
119118
"uint8arrays": "^5.0.0"
120119
},
121120
"devDependencies": {
122-
"@chainsafe/libp2p-noise": "^14.0.0",
123121
"@chainsafe/libp2p-yamux": "^6.0.1",
124-
"@libp2p/bootstrap": "^10.0.7",
125122
"@libp2p/circuit-relay-v2": "^1.0.7",
126123
"@libp2p/identify": "^1.0.6",
127124
"@libp2p/interface-compliance-tests": "^5.0.7",
128-
"@libp2p/kad-dht": "^11.0.7",
129-
"@libp2p/mdns": "^10.0.7",
130125
"@libp2p/mplex": "^10.0.7",
131126
"@libp2p/plaintext": "^1.0.7",
132127
"@libp2p/tcp": "^9.0.7",

packages/libp2p/src/connection-manager/dial-queue.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,14 @@ export class DialQueue {
294294

295295
private createDialAbortControllers (userSignal?: AbortSignal): ClearableSignal {
296296
// let any signal abort the dial
297-
const signal = anySignal(
298-
[AbortSignal.timeout(this.dialTimeout),
299-
this.shutDownController.signal,
300-
userSignal
301-
]
302-
)
303-
304-
try {
305-
// This emitter gets listened to a lot
306-
setMaxListeners?.(Infinity, signal)
307-
} catch {}
297+
const signal = anySignal([
298+
AbortSignal.timeout(this.dialTimeout),
299+
this.shutDownController.signal,
300+
userSignal
301+
])
302+
303+
// This emitter gets listened to a lot
304+
setMaxListeners(Infinity, signal)
308305

309306
return signal
310307
}

packages/libp2p/src/content-routing/index.ts renamed to packages/libp2p/src/content-routing.ts

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import { CodeError } from '@libp2p/interface'
2+
import { PeerSet } from '@libp2p/peer-collections'
23
import merge from 'it-merge'
3-
import { pipe } from 'it-pipe'
4-
import { codes, messages } from '../errors.js'
5-
import {
6-
storeAddresses,
7-
uniquePeers,
8-
requirePeers
9-
} from './utils.js'
10-
import type { AbortOptions, ContentRouting, PeerInfo, PeerStore, Startable } from '@libp2p/interface'
4+
import parallel from 'it-parallel'
5+
import { codes, messages } from './errors.js'
6+
import type { AbortOptions, ComponentLogger, ContentRouting, Logger, PeerInfo, PeerRouting, PeerStore, RoutingOptions, Startable } from '@libp2p/interface'
117
import type { CID } from 'multiformats/cid'
128

139
export interface CompoundContentRoutingInit {
@@ -16,14 +12,18 @@ export interface CompoundContentRoutingInit {
1612

1713
export interface CompoundContentRoutingComponents {
1814
peerStore: PeerStore
15+
peerRouting: PeerRouting
16+
logger: ComponentLogger
1917
}
2018

2119
export class CompoundContentRouting implements ContentRouting, Startable {
20+
private readonly log: Logger
2221
private readonly routers: ContentRouting[]
2322
private started: boolean
2423
private readonly components: CompoundContentRoutingComponents
2524

2625
constructor (components: CompoundContentRoutingComponents, init: CompoundContentRoutingInit) {
26+
this.log = components.logger.forComponent('libp2p:content-routing')
2727
this.routers = init.routers ?? []
2828
this.started = false
2929
this.components = components
@@ -44,19 +44,65 @@ export class CompoundContentRouting implements ContentRouting, Startable {
4444
/**
4545
* Iterates over all content routers in parallel to find providers of the given key
4646
*/
47-
async * findProviders (key: CID, options: AbortOptions = {}): AsyncIterable<PeerInfo> {
47+
async * findProviders (key: CID, options: RoutingOptions = {}): AsyncIterable<PeerInfo> {
4848
if (this.routers.length === 0) {
4949
throw new CodeError('No content routers available', codes.ERR_NO_ROUTERS_AVAILABLE)
5050
}
5151

52-
yield * pipe(
53-
merge(
54-
...this.routers.map(router => router.findProviders(key, options))
55-
),
56-
(source) => storeAddresses(source, this.components.peerStore),
57-
(source) => uniquePeers(source),
58-
(source) => requirePeers(source)
59-
)
52+
const self = this
53+
const seen = new PeerSet()
54+
55+
for await (const peer of parallel(
56+
async function * () {
57+
const source = merge(
58+
...self.routers.map(router => router.findProviders(key, options))
59+
)
60+
61+
for await (let peer of source) {
62+
yield async () => {
63+
// find multiaddrs if they are missing
64+
if (peer.multiaddrs.length === 0) {
65+
try {
66+
peer = await self.components.peerRouting.findPeer(peer.id, {
67+
...options,
68+
useCache: false
69+
})
70+
} catch (err) {
71+
self.log.error('could not find peer multiaddrs', err)
72+
return
73+
}
74+
}
75+
76+
return peer
77+
}
78+
}
79+
}()
80+
)) {
81+
// the peer was yielded by a content router without multiaddrs and we
82+
// failed to load them
83+
if (peer == null) {
84+
continue
85+
}
86+
87+
// skip peers without addresses
88+
if (peer.multiaddrs.length === 0) {
89+
continue
90+
}
91+
92+
// ensure we have the addresses for a given peer
93+
await this.components.peerStore.merge(peer.id, {
94+
multiaddrs: peer.multiaddrs
95+
})
96+
97+
// deduplicate peers
98+
if (seen.has(peer.id)) {
99+
continue
100+
}
101+
102+
seen.add(peer.id)
103+
104+
yield peer
105+
}
60106
}
61107

62108
/**
@@ -68,7 +114,9 @@ export class CompoundContentRouting implements ContentRouting, Startable {
68114
throw new CodeError('No content routers available', codes.ERR_NO_ROUTERS_AVAILABLE)
69115
}
70116

71-
await Promise.all(this.routers.map(async (router) => { await router.provide(key, options) }))
117+
await Promise.all(this.routers.map(async (router) => {
118+
await router.provide(key, options)
119+
}))
72120
}
73121

74122
/**

packages/libp2p/src/content-routing/utils.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

0 commit comments

Comments
 (0)