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

Sign up to get free protection for your applications and to get access to all the features.
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)