@libp2p/circuit-relay-v2 2.1.4 → 2.1.5-3bc9769b8

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 (63) hide show
  1. package/dist/index.min.js +4 -4
  2. package/dist/src/constants.d.ts +0 -5
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +0 -5
  5. package/dist/src/constants.js.map +1 -1
  6. package/dist/src/errors.d.ts +22 -0
  7. package/dist/src/errors.d.ts.map +1 -1
  8. package/dist/src/errors.js +22 -0
  9. package/dist/src/errors.js.map +1 -1
  10. package/dist/src/index.d.ts +16 -1
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/pb/index.d.ts +12 -1
  14. package/dist/src/pb/index.d.ts.map +1 -1
  15. package/dist/src/pb/index.js +78 -2
  16. package/dist/src/pb/index.js.map +1 -1
  17. package/dist/src/server/index.d.ts.map +1 -1
  18. package/dist/src/server/index.js +19 -10
  19. package/dist/src/server/index.js.map +1 -1
  20. package/dist/src/server/reservation-store.d.ts +5 -9
  21. package/dist/src/server/reservation-store.d.ts.map +1 -1
  22. package/dist/src/server/reservation-store.js +32 -33
  23. package/dist/src/server/reservation-store.js.map +1 -1
  24. package/dist/src/server/reservation-voucher.d.ts +1 -1
  25. package/dist/src/transport/discovery.d.ts +1 -0
  26. package/dist/src/transport/discovery.d.ts.map +1 -1
  27. package/dist/src/transport/discovery.js +38 -8
  28. package/dist/src/transport/discovery.js.map +1 -1
  29. package/dist/src/transport/index.d.ts +0 -6
  30. package/dist/src/transport/index.d.ts.map +1 -1
  31. package/dist/src/transport/index.js.map +1 -1
  32. package/dist/src/transport/listener.d.ts +3 -0
  33. package/dist/src/transport/listener.d.ts.map +1 -1
  34. package/dist/src/transport/listener.js +57 -27
  35. package/dist/src/transport/listener.js.map +1 -1
  36. package/dist/src/transport/reservation-store.d.ts +37 -14
  37. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  38. package/dist/src/transport/reservation-store.js +144 -74
  39. package/dist/src/transport/reservation-store.js.map +1 -1
  40. package/dist/src/transport/transport.d.ts +2 -13
  41. package/dist/src/transport/transport.d.ts.map +1 -1
  42. package/dist/src/transport/transport.js +30 -54
  43. package/dist/src/transport/transport.js.map +1 -1
  44. package/dist/src/utils.d.ts +10 -1
  45. package/dist/src/utils.d.ts.map +1 -1
  46. package/dist/src/utils.js +22 -8
  47. package/dist/src/utils.js.map +1 -1
  48. package/package.json +14 -12
  49. package/src/constants.ts +0 -7
  50. package/src/errors.ts +25 -0
  51. package/src/index.ts +19 -1
  52. package/src/pb/index.proto +20 -1
  53. package/src/pb/index.ts +99 -3
  54. package/src/server/index.ts +21 -11
  55. package/src/server/reservation-store.ts +38 -42
  56. package/src/server/reservation-voucher.ts +2 -2
  57. package/src/transport/discovery.ts +47 -9
  58. package/src/transport/index.ts +0 -7
  59. package/src/transport/listener.ts +75 -35
  60. package/src/transport/reservation-store.ts +209 -95
  61. package/src/transport/transport.ts +34 -76
  62. package/src/utils.ts +29 -8
  63. package/dist/typedoc-urls.json +0 -23
@@ -1,3 +1,4 @@
1
+ import { publicKeyToProtobuf } from '@libp2p/crypto/keys'
1
2
  import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
3
  import { peerIdFromMultihash } from '@libp2p/peer-id'
3
4
  import { RecordEnvelope } from '@libp2p/peer-record'
@@ -156,8 +157,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
156
157
  runOnLimitedConnection: true
157
158
  })
158
159
 
159
- this.reservationStore.start()
160
-
161
160
  this.started = true
162
161
  }
163
162
 
@@ -165,7 +164,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
165
164
  * Stop Relay service
166
165
  */
167
166
  async stop (): Promise<void> {
168
- this.reservationStore.stop()
167
+ this.reservationStore.clear()
169
168
  this.shutdownController.abort()
170
169
  await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
171
170
 
@@ -290,16 +289,25 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
290
289
  addrs.push(relayAddr.bytes)
291
290
  }
292
291
 
293
- const voucher = await RecordEnvelope.seal(new ReservationVoucherRecord({
292
+ const envelope = await RecordEnvelope.seal(new ReservationVoucherRecord({
294
293
  peer: remotePeer,
295
294
  relay: this.peerId,
296
- expiration: Number(expire)
295
+ expiration: expire
297
296
  }), this.privateKey)
298
297
 
299
298
  return {
300
299
  addrs,
301
300
  expire,
302
- voucher: voucher.marshal()
301
+ voucher: {
302
+ publicKey: publicKeyToProtobuf(envelope.publicKey),
303
+ payloadType: envelope.payloadType,
304
+ payload: {
305
+ peer: remotePeer.toMultihash().bytes,
306
+ relay: this.peerId.toMultihash().bytes,
307
+ expiration: expire
308
+ },
309
+ signature: envelope.signature
310
+ }
303
311
  }
304
312
  }
305
313
 
@@ -330,7 +338,9 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
330
338
  return
331
339
  }
332
340
 
333
- if (!this.reservationStore.hasReservation(dstPeer)) {
341
+ const reservation = this.reservationStore.get(dstPeer)
342
+
343
+ if (reservation == null) {
334
344
  this.log.error('hop connect denied for destination peer %p not having a reservation for %p with status %s', dstPeer, connection.remotePeer, Status.NO_RESERVATION)
335
345
  await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION }, options)
336
346
  return
@@ -350,7 +360,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
350
360
  return
351
361
  }
352
362
 
353
- const limit = this.reservationStore.get(dstPeer)?.limit
354
363
  const destinationConnection = connections[0]
355
364
 
356
365
  const destinationStream = await this.stopHop({
@@ -361,7 +370,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
361
370
  id: connection.remotePeer.toMultihash().bytes,
362
371
  addrs: []
363
372
  },
364
- limit
373
+ limit: reservation?.limit
365
374
  }
366
375
  }, options)
367
376
 
@@ -374,13 +383,14 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
374
383
  await hopstr.write({
375
384
  type: HopMessage.Type.STATUS,
376
385
  status: Status.OK,
377
- limit
386
+ limit: reservation?.limit
378
387
  }, options)
379
388
  const sourceStream = stream.unwrap()
380
389
 
381
390
  this.log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer)
391
+
382
392
  // Short circuit the two streams to create the relayed connection
383
- createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit, {
393
+ createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, reservation, {
384
394
  log: this.log
385
395
  })
386
396
  }
@@ -1,14 +1,16 @@
1
1
  import { trackedPeerMap } from '@libp2p/peer-collections'
2
- import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
2
+ import { retimeableSignal } from 'retimeable-signal'
3
+ import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
3
4
  import { type Limit, Status } from '../pb/index.js'
4
5
  import type { RelayReservation } from '../index.js'
5
- import type { Metrics, PeerId, Startable } from '@libp2p/interface'
6
+ import type { ComponentLogger, Logger, Metrics, PeerId } from '@libp2p/interface'
6
7
  import type { PeerMap } from '@libp2p/peer-collections'
7
8
  import type { Multiaddr } from '@multiformats/multiaddr'
8
9
 
9
10
  export type ReservationStatus = Status.OK | Status.PERMISSION_DENIED | Status.RESERVATION_REFUSED
10
11
 
11
12
  export interface ReservationStoreComponents {
13
+ logger: ComponentLogger
12
14
  metrics?: Metrics
13
15
  }
14
16
 
@@ -52,20 +54,18 @@ export interface ReservationStoreInit {
52
54
  defaultDataLimit?: bigint
53
55
  }
54
56
 
55
- export class ReservationStore implements Startable {
57
+ export class ReservationStore {
56
58
  public readonly reservations: PeerMap<RelayReservation>
57
- private _started = false
58
- private interval: any
59
59
  private readonly maxReservations: number
60
- private readonly reservationClearInterval: number
61
60
  private readonly applyDefaultLimit: boolean
62
61
  private readonly reservationTtl: number
63
62
  private readonly defaultDurationLimit: number
64
63
  private readonly defaultDataLimit: bigint
64
+ private readonly log: Logger
65
65
 
66
66
  constructor (components: ReservationStoreComponents, init: ReservationStoreInit = {}) {
67
+ this.log = components.logger.forComponent('libp2p:circuit-relay:server:reservation-store')
67
68
  this.maxReservations = init.maxReservations ?? DEFAULT_MAX_RESERVATION_STORE_SIZE
68
- this.reservationClearInterval = init.reservationClearInterval ?? DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL
69
69
  this.applyDefaultLimit = init.applyDefaultLimit !== false
70
70
  this.reservationTtl = init.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL
71
71
  this.defaultDurationLimit = init.defaultDurationLimit ?? DEFAULT_DURATION_LIMIT
@@ -77,59 +77,55 @@ export class ReservationStore implements Startable {
77
77
  })
78
78
  }
79
79
 
80
- isStarted (): boolean {
81
- return this._started
82
- }
83
-
84
- start (): void {
85
- if (this._started) {
86
- return
87
- }
88
- this._started = true
89
- this.interval = setInterval(
90
- () => {
91
- const now = (new Date()).getTime()
92
- this.reservations.forEach((r, k) => {
93
- if (r.expire.getTime() < now) {
94
- this.reservations.delete(k)
95
- }
96
- })
97
- },
98
- this.reservationClearInterval
99
- )
100
- }
101
-
102
- stop (): void {
103
- clearInterval(this.interval)
104
- }
105
-
106
80
  reserve (peer: PeerId, addr: Multiaddr, limit?: Limit): { status: ReservationStatus, expire?: number } {
107
- if (this.reservations.size >= this.maxReservations && !this.reservations.has(peer)) {
81
+ let reservation = this.reservations.get(peer)
82
+
83
+ if (this.reservations.size >= this.maxReservations && reservation == null) {
108
84
  return { status: Status.RESERVATION_REFUSED }
109
85
  }
110
86
 
111
- const expire = new Date(Date.now() + this.reservationTtl)
87
+ const expiry = new Date(Date.now() + this.reservationTtl)
112
88
  let checkedLimit: Limit | undefined
113
89
 
114
90
  if (this.applyDefaultLimit) {
115
- checkedLimit = limit ?? { data: this.defaultDataLimit, duration: this.defaultDurationLimit }
91
+ checkedLimit = limit ?? {
92
+ data: this.defaultDataLimit,
93
+ duration: this.defaultDurationLimit
94
+ }
116
95
  }
117
96
 
118
- this.reservations.set(peer, { addr, expire, limit: checkedLimit })
97
+ if (reservation != null) {
98
+ this.log('refreshing reservation for client %p', peer)
99
+ reservation.signal.reset(this.reservationTtl)
100
+ } else {
101
+ this.log('creating new reservation for client %p', peer)
102
+ reservation = {
103
+ addr,
104
+ expiry,
105
+ limit: checkedLimit,
106
+ signal: retimeableSignal(this.reservationTtl)
107
+ }
108
+ }
109
+
110
+ this.reservations.set(peer, reservation)
111
+
112
+ reservation.signal.addEventListener('abort', () => {
113
+ this.reservations.delete(peer)
114
+ })
119
115
 
120
116
  // return expiry time in seconds
121
- return { status: Status.OK, expire: Math.round(expire.getTime() / 1000) }
117
+ return { status: Status.OK, expire: Math.round(expiry.getTime() / 1000) }
122
118
  }
123
119
 
124
120
  removeReservation (peer: PeerId): void {
125
121
  this.reservations.delete(peer)
126
122
  }
127
123
 
128
- hasReservation (dst: PeerId): boolean {
129
- return this.reservations.has(dst)
130
- }
131
-
132
124
  get (peer: PeerId): RelayReservation | undefined {
133
125
  return this.reservations.get(peer)
134
126
  }
127
+
128
+ clear (): void {
129
+ this.reservations.clear()
130
+ }
135
131
  }
@@ -4,7 +4,7 @@ import type { PeerId, Record } from '@libp2p/interface'
4
4
  export interface ReservationVoucherOptions {
5
5
  relay: PeerId
6
6
  peer: PeerId
7
- expiration: number
7
+ expiration: bigint
8
8
  }
9
9
 
10
10
  export class ReservationVoucherRecord implements Record {
@@ -13,7 +13,7 @@ export class ReservationVoucherRecord implements Record {
13
13
 
14
14
  private readonly relay: PeerId
15
15
  private readonly peer: PeerId
16
- private readonly expiration: number
16
+ private readonly expiration: bigint
17
17
 
18
18
  constructor ({ relay, peer, expiration }: ReservationVoucherOptions) {
19
19
  this.relay = relay
@@ -1,11 +1,11 @@
1
1
  import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
2
  import { PeerQueue } from '@libp2p/utils/peer-queue'
3
3
  import { anySignal } from 'any-signal'
4
- import { raceSignal } from 'race-signal'
4
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
5
5
  import {
6
6
  RELAY_V2_HOP_CODEC
7
7
  } from '../constants.js'
8
- import type { ComponentLogger, Logger, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
8
+ import type { ComponentLogger, Logger, Peer, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
9
9
  import type { ConnectionManager, RandomWalk, Registrar, TransportManager } from '@libp2p/interface-internal'
10
10
 
11
11
  export interface RelayDiscoveryEvents {
@@ -40,6 +40,7 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
40
40
  private readonly log: Logger
41
41
  private discoveryController: AbortController
42
42
  private readonly filter?: TopologyFilter
43
+ private queue?: PeerQueue
43
44
 
44
45
  constructor (components: RelayDiscoveryComponents, init: RelayDiscoveryInit = {}) {
45
46
  super()
@@ -66,7 +67,7 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
66
67
  this.topologyId = await this.registrar.register(RELAY_V2_HOP_CODEC, {
67
68
  filter: this.filter,
68
69
  onConnect: (peerId) => {
69
- this.log.trace('discovered relay %p', peerId)
70
+ this.log.trace('discovered relay %p queue (length: %d, active %d)', peerId, this.queue?.size, this.queue?.running)
70
71
  this.safeDispatchEvent('relay:discover', { detail: peerId })
71
72
  }
72
73
  })
@@ -113,7 +114,23 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
113
114
  }
114
115
  ],
115
116
  orders: [
116
- () => Math.random() < 0.5 ? 1 : -1
117
+ // randomise
118
+ () => Math.random() < 0.5 ? 1 : -1,
119
+ // prefer peers we've connected to in the past
120
+ (a, b) => {
121
+ const lastDialA = getLastDial(a)
122
+ const lastDialB = getLastDial(b)
123
+
124
+ if (lastDialA > lastDialB) {
125
+ return -1
126
+ }
127
+
128
+ if (lastDialB > lastDialA) {
129
+ return 1
130
+ }
131
+
132
+ return 0
133
+ }
117
134
  ]
118
135
  }))
119
136
 
@@ -126,11 +143,12 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
126
143
 
127
144
  // perform random walk and dial peers - after identify has run, the network
128
145
  // topology will be notified of new relays
129
- const queue = new PeerQueue({
146
+ const queue = this.queue = new PeerQueue({
130
147
  concurrency: 5
131
148
  })
132
149
 
133
150
  this.log('start random walk')
151
+
134
152
  for await (const peer of this.randomWalk.walk({ signal: this.discoveryController.signal })) {
135
153
  this.log.trace('found random peer %p', peer.id)
136
154
 
@@ -155,12 +173,16 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
155
173
  continue
156
174
  }
157
175
 
158
- this.log.trace('wait for space in queue for %p', peer.id)
176
+ if (queue.queued > 10) {
177
+ this.log.trace('wait for space in queue for %p', peer.id)
159
178
 
160
- // pause the random walk until there is space in the queue
161
- await raceSignal(queue.onSizeLessThan(10), this.discoveryController.signal)
179
+ // pause the random walk until there is space in the queue
180
+ await queue.onSizeLessThan(10, {
181
+ signal: this.discoveryController.signal
182
+ })
183
+ }
162
184
 
163
- this.log('adding random peer %p to dial queue (length: %d)', peer.id, queue.size)
185
+ this.log('adding random peer %p to dial queue (length: %d, active %d)', peer.id, queue.size, queue.running)
164
186
 
165
187
  // dial the peer - this will cause identify to run and our topology to
166
188
  // be notified and we'll attempt to create reservations
@@ -182,6 +204,8 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
182
204
  })
183
205
  }
184
206
 
207
+ this.log('stop random walk')
208
+
185
209
  await queue.onIdle()
186
210
  })
187
211
  .catch(err => {
@@ -197,3 +221,17 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
197
221
  this.discoveryController?.abort()
198
222
  }
199
223
  }
224
+
225
+ /**
226
+ * Returns the timestamp of the last time we connected to this peer, if we've
227
+ * not connected to them before return 0
228
+ */
229
+ function getLastDial (peer: Peer): number {
230
+ const lastDial = peer.metadata.get('last-dial-success')
231
+
232
+ if (lastDial == null) {
233
+ return 0
234
+ }
235
+
236
+ return new Date(uint8ArrayToString(lastDial)).getTime()
237
+ }
@@ -17,13 +17,6 @@ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponent
17
17
  * RelayConfig configures the circuit v2 relay transport.
18
18
  */
19
19
  export interface CircuitRelayTransportInit extends ReservationStoreInit {
20
- /**
21
- * The number of peers running diable relays to search for and connect to
22
- *
23
- * @default 0
24
- */
25
- discoverRelays?: number
26
-
27
20
  /**
28
21
  * An optional filter used to prevent duplicate attempts to reserve relay
29
22
  * slots on the same peer
@@ -1,8 +1,10 @@
1
- import { ListenError, TypedEventEmitter } from '@libp2p/interface'
2
- import { PeerMap } from '@libp2p/peer-collections'
1
+ import { ListenError, TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
3
2
  import { multiaddr } from '@multiformats/multiaddr'
4
- import type { ReservationStore } from './reservation-store.js'
5
- import type { ComponentLogger, Logger, PeerId, Listener, ListenerEvents } from '@libp2p/interface'
3
+ import { DEFAULT_RESERVATION_COMPLETION_TIMEOUT } from '../constants.js'
4
+ import { CircuitListen, CircuitSearch } from '../utils.js'
5
+ import type { RelayDiscovery } from './discovery.js'
6
+ import type { RelayReservation, ReservationStore } from './reservation-store.js'
7
+ import type { ComponentLogger, Logger, Listener, ListenerEvents, PeerId } from '@libp2p/interface'
6
8
  import type { ConnectionManager } from '@libp2p/interface-internal'
7
9
  import type { Multiaddr } from '@multiformats/multiaddr'
8
10
 
@@ -12,79 +14,117 @@ export interface CircuitRelayTransportListenerComponents {
12
14
  logger: ComponentLogger
13
15
  }
14
16
 
17
+ export interface CircuitRelayTransportListenerInit {
18
+ listenTimeout?: number
19
+ }
20
+
15
21
  class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> implements Listener {
16
22
  private readonly connectionManager: ConnectionManager
17
23
  private readonly reservationStore: ReservationStore
18
- private readonly listeningAddrs: PeerMap<Multiaddr[]>
24
+ private readonly discovery?: RelayDiscovery
25
+ private listeningAddrs: Multiaddr[]
19
26
  private readonly log: Logger
27
+ private readonly listenTimeout: number
28
+ private reservationId?: string
29
+ private relay?: PeerId
20
30
 
21
- constructor (components: CircuitRelayTransportListenerComponents) {
31
+ constructor (components: CircuitRelayTransportListenerComponents, init: CircuitRelayTransportListenerInit = {}) {
22
32
  super()
23
33
 
24
34
  this.log = components.logger.forComponent('libp2p:circuit-relay:transport:listener')
25
35
  this.connectionManager = components.connectionManager
26
36
  this.reservationStore = components.relayStore
27
- this.listeningAddrs = new PeerMap()
37
+ this.listeningAddrs = []
38
+ this.listenTimeout = init.listenTimeout ?? DEFAULT_RESERVATION_COMPLETION_TIMEOUT
28
39
 
29
40
  // remove listening addrs when a relay is removed
30
41
  this.reservationStore.addEventListener('relay:removed', this._onRemoveRelayPeer)
42
+ this.reservationStore.addEventListener('relay:created-reservation', this._onAddRelayPeer)
31
43
  }
32
44
 
33
- _onRemoveRelayPeer = (evt: CustomEvent<PeerId>): void => {
34
- const had = this.listeningAddrs.has(evt.detail)
35
-
36
- this.log('relay peer removed %p - had reservation', evt.detail, had)
45
+ _onRemoveRelayPeer = (evt: CustomEvent<RelayReservation>): void => {
46
+ this.log('relay removed %p our relay %p', evt.detail.relay, this.relay, this.relay?.equals(evt.detail.relay))
37
47
 
38
- if (!had) {
48
+ if (this.relay?.equals(evt.detail.relay) !== true) {
39
49
  return
40
50
  }
41
51
 
42
- this.listeningAddrs.delete(evt.detail)
52
+ this.log('relay peer removed %p', evt.detail.relay)
53
+
54
+ this.listeningAddrs = []
43
55
 
44
56
  // announce listen addresses change
45
57
  this.safeDispatchEvent('listening')
46
58
  }
47
59
 
48
- async listen (addr: Multiaddr): Promise<void> {
49
- this.log('listen on %a', addr)
50
-
51
- // remove the circuit part to get the peer id of the relay
52
- const relayAddr = addr.decapsulate('/p2p-circuit')
53
- const relayConn = await this.connectionManager.openConnection(relayAddr)
60
+ _onAddRelayPeer = (evt: CustomEvent<RelayReservation>): void => {
61
+ const {
62
+ relay, details
63
+ } = evt.detail
54
64
 
55
- if (!this.reservationStore.hasReservation(relayConn.remotePeer)) {
56
- this.log('making reservation on peer %p', relayConn.remotePeer)
57
- // addRelay calls transportManager.listen which calls this listen method
58
- await this.reservationStore.addRelay(relayConn.remotePeer, 'configured')
65
+ if (details.type === 'configured') {
59
66
  return
60
67
  }
61
68
 
62
- const reservation = this.reservationStore.getReservation(relayConn.remotePeer)
63
-
64
- if (reservation == null) {
65
- throw new ListenError('Did not have reservation after making reservation')
66
- }
67
-
68
- if (this.listeningAddrs.has(relayConn.remotePeer)) {
69
- this.log('already listening on relay %p', relayConn.remotePeer)
69
+ if (details.id !== this.reservationId) {
70
70
  return
71
71
  }
72
72
 
73
+ this.log('relay peer added %p', relay)
74
+
75
+ this.relay = relay
76
+
73
77
  // add all addresses from the relay reservation
74
- this.listeningAddrs.set(relayConn.remotePeer, reservation.addrs
78
+ this.listeningAddrs = details.reservation.addrs
75
79
  .map(buf => multiaddr(buf).encapsulate('/p2p-circuit'))
76
- )
77
80
 
81
+ // announce listen addresses change
78
82
  this.safeDispatchEvent('listening')
79
83
  }
80
84
 
85
+ async listen (addr: Multiaddr): Promise<void> {
86
+ this.log('listen on %a', addr)
87
+
88
+ if (CircuitSearch.exactMatch(addr)) {
89
+ // start relay discovery
90
+ this.reservationId = this.reservationStore.reserveRelay()
91
+ } else if (CircuitListen.exactMatch(addr)) {
92
+ const signal = AbortSignal.timeout(this.listenTimeout)
93
+ setMaxListeners(Infinity, signal)
94
+
95
+ // try to make a reservation on one particular relay
96
+ // remove the circuit part to get the peer id of the relay
97
+ const relayAddr = addr.decapsulate('/p2p-circuit')
98
+ const relayConn = await this.connectionManager.openConnection(relayAddr, {
99
+ signal
100
+ })
101
+
102
+ if (!this.reservationStore.hasReservation(relayConn.remotePeer)) {
103
+ this.log('making reservation on peer %p', relayConn.remotePeer)
104
+ const reservation = await this.reservationStore.addRelay(relayConn.remotePeer, 'configured')
105
+ this.log('made reservation on peer %p', relayConn.remotePeer)
106
+
107
+ this.relay = reservation.relay
108
+
109
+ // add all addresses from the relay reservation
110
+ this.listeningAddrs = reservation.details.reservation.addrs
111
+ .map(buf => multiaddr(buf).encapsulate('/p2p-circuit'))
112
+
113
+ // if that succeeded announce listen addresses change
114
+ this.safeDispatchEvent('listening')
115
+ }
116
+ } else {
117
+ throw new ListenError(`Could not listen on p2p-circuit address "${addr}"`)
118
+ }
119
+ }
120
+
81
121
  getAddrs (): Multiaddr[] {
82
122
  return [...this.listeningAddrs.values()].flat()
83
123
  }
84
124
 
85
125
  async close (): Promise<void> {
86
- await this.reservationStore.cancelReservations()
87
- this.listeningAddrs.clear()
126
+ this.reservationStore.cancelReservations()
127
+ this.listeningAddrs = []
88
128
 
89
129
  // remove listener
90
130
  this.reservationStore.removeEventListener('relay:removed', this._onRemoveRelayPeer)