@libp2p/circuit-relay-v2 1.0.24-169c9d85e → 1.0.24-440c9b360

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. package/dist/index.min.js +5 -5
  2. package/dist/src/constants.d.ts +13 -12
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +13 -12
  5. package/dist/src/constants.js.map +1 -1
  6. package/dist/src/pb/index.d.ts +7 -7
  7. package/dist/src/pb/index.d.ts.map +1 -1
  8. package/dist/src/pb/index.js +94 -52
  9. package/dist/src/pb/index.js.map +1 -1
  10. package/dist/src/server/index.d.ts +1 -7
  11. package/dist/src/server/index.d.ts.map +1 -1
  12. package/dist/src/server/index.js +2 -16
  13. package/dist/src/server/index.js.map +1 -1
  14. package/dist/src/transport/discovery.d.ts +15 -10
  15. package/dist/src/transport/discovery.d.ts.map +1 -1
  16. package/dist/src/transport/discovery.js +103 -51
  17. package/dist/src/transport/discovery.js.map +1 -1
  18. package/dist/src/transport/index.d.ts +27 -18
  19. package/dist/src/transport/index.d.ts.map +1 -1
  20. package/dist/src/transport/index.js +0 -3
  21. package/dist/src/transport/index.js.map +1 -1
  22. package/dist/src/transport/reservation-store.d.ts +7 -3
  23. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  24. package/dist/src/transport/reservation-store.js +37 -13
  25. package/dist/src/transport/reservation-store.js.map +1 -1
  26. package/dist/src/transport/transport.d.ts +0 -1
  27. package/dist/src/transport/transport.d.ts.map +1 -1
  28. package/dist/src/transport/transport.js +16 -15
  29. package/dist/src/transport/transport.js.map +1 -1
  30. package/package.json +12 -13
  31. package/src/constants.ts +16 -15
  32. package/src/pb/index.ts +96 -53
  33. package/src/server/index.ts +3 -26
  34. package/src/transport/discovery.ts +121 -56
  35. package/src/transport/index.ts +28 -18
  36. package/src/transport/reservation-store.ts +45 -13
  37. package/src/transport/transport.ts +17 -17
  38. package/dist/src/server/advert-service.d.ts +0 -44
  39. package/dist/src/server/advert-service.d.ts.map +0 -1
  40. package/dist/src/server/advert-service.js +0 -72
  41. package/dist/src/server/advert-service.js.map +0 -1
  42. package/src/server/advert-service.ts +0 -107
package/src/pb/index.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
5
5
  /* eslint-disable @typescript-eslint/no-empty-interface */
6
6
 
7
- import { enumeration, encodeMessage, decodeMessage, message } from 'protons-runtime'
8
- import type { Codec } from 'protons-runtime'
7
+ import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
8
+ import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
9
9
  import type { Uint8ArrayList } from 'uint8arraylist'
10
10
 
11
11
  export interface HopMessage {
@@ -72,7 +72,7 @@ export namespace HopMessage {
72
72
  if (opts.lengthDelimited !== false) {
73
73
  w.ldelim()
74
74
  }
75
- }, (reader, length) => {
75
+ }, (reader, length, opts = {}) => {
76
76
  const obj: any = {}
77
77
 
78
78
  const end = length == null ? reader.len : reader.pos + length
@@ -81,24 +81,36 @@ export namespace HopMessage {
81
81
  const tag = reader.uint32()
82
82
 
83
83
  switch (tag >>> 3) {
84
- case 1:
84
+ case 1: {
85
85
  obj.type = HopMessage.Type.codec().decode(reader)
86
86
  break
87
- case 2:
88
- obj.peer = Peer.codec().decode(reader, reader.uint32())
87
+ }
88
+ case 2: {
89
+ obj.peer = Peer.codec().decode(reader, reader.uint32(), {
90
+ limits: opts.limits?.peer
91
+ })
89
92
  break
90
- case 3:
91
- obj.reservation = Reservation.codec().decode(reader, reader.uint32())
93
+ }
94
+ case 3: {
95
+ obj.reservation = Reservation.codec().decode(reader, reader.uint32(), {
96
+ limits: opts.limits?.reservation
97
+ })
92
98
  break
93
- case 4:
94
- obj.limit = Limit.codec().decode(reader, reader.uint32())
99
+ }
100
+ case 4: {
101
+ obj.limit = Limit.codec().decode(reader, reader.uint32(), {
102
+ limits: opts.limits?.limit
103
+ })
95
104
  break
96
- case 5:
105
+ }
106
+ case 5: {
97
107
  obj.status = Status.codec().decode(reader)
98
108
  break
99
- default:
109
+ }
110
+ default: {
100
111
  reader.skipType(tag & 7)
101
112
  break
113
+ }
102
114
  }
103
115
  }
104
116
 
@@ -113,8 +125,8 @@ export namespace HopMessage {
113
125
  return encodeMessage(obj, HopMessage.codec())
114
126
  }
115
127
 
116
- export const decode = (buf: Uint8Array | Uint8ArrayList): HopMessage => {
117
- return decodeMessage(buf, HopMessage.codec())
128
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<HopMessage>): HopMessage => {
129
+ return decodeMessage(buf, HopMessage.codec(), opts)
118
130
  }
119
131
  }
120
132
 
@@ -174,7 +186,7 @@ export namespace StopMessage {
174
186
  if (opts.lengthDelimited !== false) {
175
187
  w.ldelim()
176
188
  }
177
- }, (reader, length) => {
189
+ }, (reader, length, opts = {}) => {
178
190
  const obj: any = {}
179
191
 
180
192
  const end = length == null ? reader.len : reader.pos + length
@@ -183,21 +195,30 @@ export namespace StopMessage {
183
195
  const tag = reader.uint32()
184
196
 
185
197
  switch (tag >>> 3) {
186
- case 1:
198
+ case 1: {
187
199
  obj.type = StopMessage.Type.codec().decode(reader)
188
200
  break
189
- case 2:
190
- obj.peer = Peer.codec().decode(reader, reader.uint32())
201
+ }
202
+ case 2: {
203
+ obj.peer = Peer.codec().decode(reader, reader.uint32(), {
204
+ limits: opts.limits?.peer
205
+ })
191
206
  break
192
- case 3:
193
- obj.limit = Limit.codec().decode(reader, reader.uint32())
207
+ }
208
+ case 3: {
209
+ obj.limit = Limit.codec().decode(reader, reader.uint32(), {
210
+ limits: opts.limits?.limit
211
+ })
194
212
  break
195
- case 4:
213
+ }
214
+ case 4: {
196
215
  obj.status = Status.codec().decode(reader)
197
216
  break
198
- default:
217
+ }
218
+ default: {
199
219
  reader.skipType(tag & 7)
200
220
  break
221
+ }
201
222
  }
202
223
  }
203
224
 
@@ -212,8 +233,8 @@ export namespace StopMessage {
212
233
  return encodeMessage(obj, StopMessage.codec())
213
234
  }
214
235
 
215
- export const decode = (buf: Uint8Array | Uint8ArrayList): StopMessage => {
216
- return decodeMessage(buf, StopMessage.codec())
236
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<StopMessage>): StopMessage => {
237
+ return decodeMessage(buf, StopMessage.codec(), opts)
217
238
  }
218
239
  }
219
240
 
@@ -247,9 +268,9 @@ export namespace Peer {
247
268
  if (opts.lengthDelimited !== false) {
248
269
  w.ldelim()
249
270
  }
250
- }, (reader, length) => {
271
+ }, (reader, length, opts = {}) => {
251
272
  const obj: any = {
252
- id: new Uint8Array(0),
273
+ id: uint8ArrayAlloc(0),
253
274
  addrs: []
254
275
  }
255
276
 
@@ -259,15 +280,22 @@ export namespace Peer {
259
280
  const tag = reader.uint32()
260
281
 
261
282
  switch (tag >>> 3) {
262
- case 1:
283
+ case 1: {
263
284
  obj.id = reader.bytes()
264
285
  break
265
- case 2:
286
+ }
287
+ case 2: {
288
+ if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
289
+ throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
290
+ }
291
+
266
292
  obj.addrs.push(reader.bytes())
267
293
  break
268
- default:
294
+ }
295
+ default: {
269
296
  reader.skipType(tag & 7)
270
297
  break
298
+ }
271
299
  }
272
300
  }
273
301
 
@@ -282,8 +310,8 @@ export namespace Peer {
282
310
  return encodeMessage(obj, Peer.codec())
283
311
  }
284
312
 
285
- export const decode = (buf: Uint8Array | Uint8ArrayList): Peer => {
286
- return decodeMessage(buf, Peer.codec())
313
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Peer>): Peer => {
314
+ return decodeMessage(buf, Peer.codec(), opts)
287
315
  }
288
316
  }
289
317
 
@@ -323,7 +351,7 @@ export namespace Reservation {
323
351
  if (opts.lengthDelimited !== false) {
324
352
  w.ldelim()
325
353
  }
326
- }, (reader, length) => {
354
+ }, (reader, length, opts = {}) => {
327
355
  const obj: any = {
328
356
  expire: 0n,
329
357
  addrs: []
@@ -335,18 +363,26 @@ export namespace Reservation {
335
363
  const tag = reader.uint32()
336
364
 
337
365
  switch (tag >>> 3) {
338
- case 1:
366
+ case 1: {
339
367
  obj.expire = reader.uint64()
340
368
  break
341
- case 2:
369
+ }
370
+ case 2: {
371
+ if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
372
+ throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
373
+ }
374
+
342
375
  obj.addrs.push(reader.bytes())
343
376
  break
344
- case 3:
377
+ }
378
+ case 3: {
345
379
  obj.voucher = reader.bytes()
346
380
  break
347
- default:
381
+ }
382
+ default: {
348
383
  reader.skipType(tag & 7)
349
384
  break
385
+ }
350
386
  }
351
387
  }
352
388
 
@@ -361,8 +397,8 @@ export namespace Reservation {
361
397
  return encodeMessage(obj, Reservation.codec())
362
398
  }
363
399
 
364
- export const decode = (buf: Uint8Array | Uint8ArrayList): Reservation => {
365
- return decodeMessage(buf, Reservation.codec())
400
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Reservation>): Reservation => {
401
+ return decodeMessage(buf, Reservation.codec(), opts)
366
402
  }
367
403
  }
368
404
 
@@ -394,7 +430,7 @@ export namespace Limit {
394
430
  if (opts.lengthDelimited !== false) {
395
431
  w.ldelim()
396
432
  }
397
- }, (reader, length) => {
433
+ }, (reader, length, opts = {}) => {
398
434
  const obj: any = {}
399
435
 
400
436
  const end = length == null ? reader.len : reader.pos + length
@@ -403,15 +439,18 @@ export namespace Limit {
403
439
  const tag = reader.uint32()
404
440
 
405
441
  switch (tag >>> 3) {
406
- case 1:
442
+ case 1: {
407
443
  obj.duration = reader.uint32()
408
444
  break
409
- case 2:
445
+ }
446
+ case 2: {
410
447
  obj.data = reader.uint64()
411
448
  break
412
- default:
449
+ }
450
+ default: {
413
451
  reader.skipType(tag & 7)
414
452
  break
453
+ }
415
454
  }
416
455
  }
417
456
 
@@ -426,8 +465,8 @@ export namespace Limit {
426
465
  return encodeMessage(obj, Limit.codec())
427
466
  }
428
467
 
429
- export const decode = (buf: Uint8Array | Uint8ArrayList): Limit => {
430
- return decodeMessage(buf, Limit.codec())
468
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Limit>): Limit => {
469
+ return decodeMessage(buf, Limit.codec(), opts)
431
470
  }
432
471
  }
433
472
 
@@ -494,10 +533,10 @@ export namespace ReservationVoucher {
494
533
  if (opts.lengthDelimited !== false) {
495
534
  w.ldelim()
496
535
  }
497
- }, (reader, length) => {
536
+ }, (reader, length, opts = {}) => {
498
537
  const obj: any = {
499
- relay: new Uint8Array(0),
500
- peer: new Uint8Array(0),
538
+ relay: uint8ArrayAlloc(0),
539
+ peer: uint8ArrayAlloc(0),
501
540
  expiration: 0n
502
541
  }
503
542
 
@@ -507,18 +546,22 @@ export namespace ReservationVoucher {
507
546
  const tag = reader.uint32()
508
547
 
509
548
  switch (tag >>> 3) {
510
- case 1:
549
+ case 1: {
511
550
  obj.relay = reader.bytes()
512
551
  break
513
- case 2:
552
+ }
553
+ case 2: {
514
554
  obj.peer = reader.bytes()
515
555
  break
516
- case 3:
556
+ }
557
+ case 3: {
517
558
  obj.expiration = reader.uint64()
518
559
  break
519
- default:
560
+ }
561
+ default: {
520
562
  reader.skipType(tag & 7)
521
563
  break
564
+ }
522
565
  }
523
566
  }
524
567
 
@@ -533,7 +576,7 @@ export namespace ReservationVoucher {
533
576
  return encodeMessage(obj, ReservationVoucher.codec())
534
577
  }
535
578
 
536
- export const decode = (buf: Uint8Array | Uint8ArrayList): ReservationVoucher => {
537
- return decodeMessage(buf, ReservationVoucher.codec())
579
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<ReservationVoucher>): ReservationVoucher => {
580
+ return decodeMessage(buf, ReservationVoucher.codec(), opts)
538
581
  }
539
582
  }
@@ -14,7 +14,6 @@ import {
14
14
  } from '../constants.js'
15
15
  import { HopMessage, type Reservation, Status, StopMessage } from '../pb/index.js'
16
16
  import { createLimitedRelay } from '../utils.js'
17
- import { AdvertService, type AdvertServiceComponents, type AdvertServiceInit } from './advert-service.js'
18
17
  import { ReservationStore, type ReservationStoreInit } from './reservation-store.js'
19
18
  import { ReservationVoucherRecord } from './reservation-voucher.js'
20
19
  import type { CircuitRelayService, RelayReservation } from '../index.js'
@@ -31,12 +30,6 @@ export interface CircuitRelayServerInit {
31
30
  */
32
31
  hopTimeout?: number
33
32
 
34
- /**
35
- * If true, advertise this service via libp2p content routing to allow
36
- * peers to locate us on the network (default: false)
37
- */
38
- advertise?: boolean | AdvertServiceInit
39
-
40
33
  /**
41
34
  * Configuration of reservations
42
35
  */
@@ -70,7 +63,7 @@ export interface StopOptions {
70
63
  request: StopMessage
71
64
  }
72
65
 
73
- export interface CircuitRelayServerComponents extends AdvertServiceComponents {
66
+ export interface CircuitRelayServerComponents {
74
67
  registrar: Registrar
75
68
  peerStore: PeerStore
76
69
  addressManager: AddressManager
@@ -98,7 +91,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
98
91
  private readonly connectionManager: ConnectionManager
99
92
  private readonly connectionGater: ConnectionGater
100
93
  private readonly reservationStore: ReservationStore
101
- private readonly advertService: AdvertService | undefined
102
94
  private started: boolean
103
95
  private readonly hopTimeout: number
104
96
  private readonly shutdownController: AbortController
@@ -122,24 +114,13 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
122
114
  this.connectionGater = components.connectionGater
123
115
  this.started = false
124
116
  this.hopTimeout = init?.hopTimeout ?? DEFAULT_HOP_TIMEOUT
125
- this.shutdownController = new AbortController()
126
117
  this.maxInboundHopStreams = init.maxInboundHopStreams
127
118
  this.maxOutboundHopStreams = init.maxOutboundHopStreams
128
119
  this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
120
+ this.reservationStore = new ReservationStore(init.reservations)
129
121
 
122
+ this.shutdownController = new AbortController()
130
123
  setMaxListeners(Infinity, this.shutdownController.signal)
131
-
132
- if (init.advertise != null && init.advertise !== false) {
133
- this.advertService = new AdvertService(components, init.advertise === true ? undefined : init.advertise)
134
- this.advertService.addEventListener('advert:success', () => {
135
- this.safeDispatchEvent('relay:advert:success', {})
136
- })
137
- this.advertService.addEventListener('advert:error', (evt) => {
138
- this.safeDispatchEvent('relay:advert:error', { detail: evt.detail })
139
- })
140
- }
141
-
142
- this.reservationStore = new ReservationStore(init.reservations)
143
124
  }
144
125
 
145
126
  isStarted (): boolean {
@@ -154,9 +135,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
154
135
  return
155
136
  }
156
137
 
157
- // Advertise service if HOP enabled and advertising enabled
158
- this.advertService?.start()
159
-
160
138
  await this.registrar.handle(RELAY_V2_HOP_CODEC, (data) => {
161
139
  void this.onHop(data).catch(err => {
162
140
  this.log.error(err)
@@ -176,7 +154,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
176
154
  * Stop Relay service
177
155
  */
178
156
  async stop (): Promise<void> {
179
- this.advertService?.stop()
180
157
  this.reservationStore.stop()
181
158
  this.shutdownController.abort()
182
159
  await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
@@ -1,24 +1,28 @@
1
- import { TypedEventEmitter } from '@libp2p/interface'
1
+ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
+ import { PeerQueue } from '@libp2p/utils/peer-queue'
3
+ import { anySignal } from 'any-signal'
4
+ import { raceSignal } from 'race-signal'
2
5
  import {
3
- RELAY_RENDEZVOUS_NS,
4
6
  RELAY_V2_HOP_CODEC
5
7
  } from '../constants.js'
6
- import { namespaceToCid } from '../utils.js'
7
- import type { ComponentLogger, Logger, ContentRouting, PeerId, PeerStore, Startable } from '@libp2p/interface'
8
- import type { ConnectionManager, Registrar, TransportManager } from '@libp2p/interface-internal'
8
+ import type { ComponentLogger, Logger, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
9
+ import type { ConnectionManager, RandomWalk, Registrar, TransportManager } from '@libp2p/interface-internal'
9
10
 
10
11
  export interface RelayDiscoveryEvents {
11
12
  'relay:discover': CustomEvent<PeerId>
12
13
  }
13
14
 
14
15
  export interface RelayDiscoveryComponents {
15
- peerId: PeerId
16
16
  peerStore: PeerStore
17
17
  connectionManager: ConnectionManager
18
18
  transportManager: TransportManager
19
- contentRouting: ContentRouting
20
19
  registrar: Registrar
21
20
  logger: ComponentLogger
21
+ randomWalk: RandomWalk
22
+ }
23
+
24
+ export interface RelayDiscoveryInit {
25
+ filter?: TopologyFilter
22
26
  }
23
27
 
24
28
  /**
@@ -26,23 +30,30 @@ export interface RelayDiscoveryComponents {
26
30
  * peers that support the circuit v2 HOP protocol.
27
31
  */
28
32
  export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> implements Startable {
29
- private readonly peerId: PeerId
30
33
  private readonly peerStore: PeerStore
31
- private readonly contentRouting: ContentRouting
32
34
  private readonly registrar: Registrar
35
+ private readonly connectionManager: ConnectionManager
36
+ private readonly randomWalk: RandomWalk
33
37
  private started: boolean
38
+ private running: boolean
34
39
  private topologyId?: string
35
40
  private readonly log: Logger
41
+ private discoveryController: AbortController
42
+ private readonly filter?: TopologyFilter
36
43
 
37
- constructor (components: RelayDiscoveryComponents) {
44
+ constructor (components: RelayDiscoveryComponents, init: RelayDiscoveryInit = {}) {
38
45
  super()
39
46
 
40
47
  this.log = components.logger.forComponent('libp2p:circuit-relay:discover-relays')
41
48
  this.started = false
42
- this.peerId = components.peerId
49
+ this.running = false
43
50
  this.peerStore = components.peerStore
44
- this.contentRouting = components.contentRouting
45
51
  this.registrar = components.registrar
52
+ this.connectionManager = components.connectionManager
53
+ this.randomWalk = components.randomWalk
54
+ this.filter = init.filter
55
+ this.discoveryController = new AbortController()
56
+ setMaxListeners(Infinity, this.discoveryController.signal)
46
57
  }
47
58
 
48
59
  isStarted (): boolean {
@@ -53,8 +64,9 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
53
64
  // register a topology listener for when new peers are encountered
54
65
  // that support the hop protocol
55
66
  this.topologyId = await this.registrar.register(RELAY_V2_HOP_CODEC, {
56
- notifyOnTransient: true,
67
+ filter: this.filter,
57
68
  onConnect: (peerId) => {
69
+ this.log('discovered relay %p', peerId)
58
70
  this.safeDispatchEvent('relay:discover', { detail: peerId })
59
71
  }
60
72
  })
@@ -62,18 +74,12 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
62
74
  this.started = true
63
75
  }
64
76
 
65
- afterStart (): void {
66
- void this.discover()
67
- .catch(err => {
68
- this.log.error('error discovering relays', err)
69
- })
70
- }
71
-
72
77
  stop (): void {
73
78
  if (this.topologyId != null) {
74
79
  this.registrar.unregister(this.topologyId)
75
80
  }
76
81
 
82
+ this.discoveryController?.abort()
77
83
  this.started = false
78
84
  }
79
85
 
@@ -81,54 +87,113 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
81
87
  * Try to listen on available hop relay connections.
82
88
  * The following order will happen while we do not have enough relays:
83
89
  *
84
- * 1. Check the metadata store for known relays, try to listen on the ones we are already connected
90
+ * 1. Check the metadata store for known relays, try to listen on the ones we are already connected to
85
91
  * 2. Dial and try to listen on the peers we know that support hop but are not connected
86
92
  * 3. Search the network
87
93
  */
88
- async discover (): Promise<void> {
89
- this.log('searching peer store for relays')
90
- const peers = (await this.peerStore.all({
91
- filters: [
92
- // filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on
93
- (peer) => {
94
- return peer.protocols.includes(RELAY_V2_HOP_CODEC)
95
- }
96
- ],
97
- orders: [
98
- () => Math.random() < 0.5 ? 1 : -1
99
- ]
100
- }))
101
-
102
- for (const peer of peers) {
103
- this.log('found relay peer %p in content peer store', peer.id)
104
- this.safeDispatchEvent('relay:discover', { detail: peer.id })
94
+ startDiscovery (): void {
95
+ if (this.running) {
96
+ return
105
97
  }
106
98
 
107
- this.log('found %d relay peers in peer store', peers.length)
99
+ this.log('start discovery')
100
+ this.running = true
101
+ this.discoveryController = new AbortController()
102
+ setMaxListeners(Infinity, this.discoveryController.signal)
103
+
104
+ Promise.resolve()
105
+ .then(async () => {
106
+ this.log('searching peer store for relays')
107
+
108
+ const peers = (await this.peerStore.all({
109
+ filters: [
110
+ // filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on
111
+ (peer) => {
112
+ return peer.protocols.includes(RELAY_V2_HOP_CODEC)
113
+ }
114
+ ],
115
+ orders: [
116
+ () => Math.random() < 0.5 ? 1 : -1
117
+ ]
118
+ }))
119
+
120
+ for (const peer of peers) {
121
+ this.log.trace('found relay peer %p in peer store', peer.id)
122
+ this.safeDispatchEvent('relay:discover', { detail: peer.id })
123
+ }
124
+
125
+ this.log('found %d relay peers in peer store', peers.length)
126
+
127
+ // perform random walk and dial peers - after identify has run, the network
128
+ // topology will be notified of new relays
129
+ const queue = new PeerQueue({
130
+ concurrency: 5
131
+ })
132
+
133
+ this.log('start random walk')
134
+ for await (const peer of this.randomWalk.walk({ signal: this.discoveryController.signal })) {
135
+ this.log.trace('found random peer %p', peer.id)
136
+
137
+ if (queue.has(peer.id)) {
138
+ this.log.trace('random peer %p was already in queue', peer.id)
139
+
140
+ // skip peers already in the queue
141
+ continue
142
+ }
108
143
 
109
- try {
110
- this.log('searching content routing for relays')
111
- const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
144
+ if (this.connectionManager.getConnections(peer.id)?.length > 0) {
145
+ this.log.trace('random peer %p was already connected', peer.id)
112
146
 
113
- let found = 0
147
+ // skip peers we are already connected to
148
+ continue
149
+ }
114
150
 
115
- for await (const provider of this.contentRouting.findProviders(cid)) {
116
- if (provider.multiaddrs.length > 0 && !provider.id.equals(this.peerId)) {
117
- const peerId = provider.id
151
+ if (!(await this.connectionManager.isDialable(peer.multiaddrs))) {
152
+ this.log.trace('random peer %p was not dialable', peer.id, peer.multiaddrs.map(ma => ma.toString()))
118
153
 
119
- found++
120
- await this.peerStore.merge(peerId, {
121
- multiaddrs: provider.multiaddrs
154
+ // skip peers we can't dial
155
+ continue
156
+ }
157
+
158
+ this.log.trace('wait for space in queue for %p', peer.id)
159
+
160
+ // pause the random walk until there is space in the queue
161
+ await raceSignal(queue.onSizeLessThan(10), this.discoveryController.signal)
162
+
163
+ this.log('adding random peer %p to dial queue (length: %d)', peer.id, queue.size)
164
+
165
+ // dial the peer - this will cause identify to run and our topology to
166
+ // be notified and we'll attempt to create reservations
167
+ queue.add(async () => {
168
+ const signal = anySignal([this.discoveryController.signal, AbortSignal.timeout(5000)])
169
+ setMaxListeners(Infinity, signal)
170
+
171
+ try {
172
+ await this.connectionManager.openConnection(peer.id, { signal })
173
+ } finally {
174
+ signal.clear()
175
+ }
176
+ }, {
177
+ peerId: peer.id,
178
+ signal: this.discoveryController.signal
122
179
  })
180
+ .catch(err => {
181
+ this.log.error('error opening connection to random peer %p', peer.id, err)
182
+ })
183
+ }
123
184
 
124
- this.log('found relay peer %p in content routing', peerId)
125
- this.safeDispatchEvent('relay:discover', { detail: peerId })
185
+ await queue.onIdle()
186
+ })
187
+ .catch(err => {
188
+ if (!this.discoveryController.signal.aborted) {
189
+ this.log.error('failed when finding relays on the network', err)
126
190
  }
127
- }
191
+ })
192
+ }
128
193
 
129
- this.log('found %d relay peers in content routing', found)
130
- } catch (err: any) {
131
- this.log.error('failed when finding relays on the network', err)
132
- }
194
+ stopDiscovery (): void {
195
+ this.log('stop discovery')
196
+ this.running = false
197
+ this.discoveryController?.abort()
133
198
  }
134
199
  }