@libp2p/circuit-relay-v2 1.0.24 → 1.0.25-863b3de03

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) 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 +10 -19
  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 +20 -19
  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 +11 -29
  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 +21 -21
  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/dist/typedoc-urls.json +0 -12
  43. 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)
@@ -359,6 +336,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
359
336
  return
360
337
  }
361
338
 
339
+ const limit = this.reservationStore.get(dstPeer)?.limit
362
340
  const destinationConnection = connections[0]
363
341
 
364
342
  const destinationStream = await this.stopHop({
@@ -368,7 +346,8 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
368
346
  peer: {
369
347
  id: connection.remotePeer.toBytes(),
370
348
  addrs: []
371
- }
349
+ },
350
+ limit
372
351
  }
373
352
  })
374
353
 
@@ -378,11 +357,14 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
378
357
  return
379
358
  }
380
359
 
381
- await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.OK })
360
+ await hopstr.write({
361
+ type: HopMessage.Type.STATUS,
362
+ status: Status.OK,
363
+ limit
364
+ })
382
365
  const sourceStream = stream.unwrap()
383
366
 
384
367
  this.log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer)
385
- const limit = this.reservationStore.get(dstPeer)?.limit
386
368
  // Short circuit the two streams to create the relayed connection
387
369
  createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit, {
388
370
  log: this.log
@@ -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
  }