@libp2p/circuit-relay-v2 2.1.3 → 2.1.4-5d199f9b6

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 (53) hide show
  1. package/dist/index.min.js +2 -2
  2. package/dist/src/constants.d.ts +4 -6
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +5 -6
  5. package/dist/src/constants.js.map +1 -1
  6. package/dist/src/index.d.ts +18 -3
  7. package/dist/src/index.d.ts.map +1 -1
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/pb/index.d.ts +12 -1
  10. package/dist/src/pb/index.d.ts.map +1 -1
  11. package/dist/src/pb/index.js +78 -2
  12. package/dist/src/pb/index.js.map +1 -1
  13. package/dist/src/server/index.d.ts.map +1 -1
  14. package/dist/src/server/index.js +71 -60
  15. package/dist/src/server/index.js.map +1 -1
  16. package/dist/src/server/reservation-store.d.ts +5 -9
  17. package/dist/src/server/reservation-store.d.ts.map +1 -1
  18. package/dist/src/server/reservation-store.js +32 -33
  19. package/dist/src/server/reservation-store.js.map +1 -1
  20. package/dist/src/server/reservation-voucher.d.ts +1 -1
  21. package/dist/src/transport/discovery.js +1 -1
  22. package/dist/src/transport/discovery.js.map +1 -1
  23. package/dist/src/transport/index.d.ts +2 -2
  24. package/dist/src/transport/index.d.ts.map +1 -1
  25. package/dist/src/transport/listener.d.ts.map +1 -1
  26. package/dist/src/transport/listener.js +23 -22
  27. package/dist/src/transport/listener.js.map +1 -1
  28. package/dist/src/transport/reservation-store.d.ts +4 -3
  29. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  30. package/dist/src/transport/reservation-store.js +111 -47
  31. package/dist/src/transport/reservation-store.js.map +1 -1
  32. package/dist/src/transport/transport.d.ts.map +1 -1
  33. package/dist/src/transport/transport.js +11 -8
  34. package/dist/src/transport/transport.js.map +1 -1
  35. package/dist/src/utils.d.ts +7 -1
  36. package/dist/src/utils.d.ts.map +1 -1
  37. package/dist/src/utils.js +19 -8
  38. package/dist/src/utils.js.map +1 -1
  39. package/package.json +12 -12
  40. package/src/constants.ts +7 -7
  41. package/src/index.ts +21 -3
  42. package/src/pb/index.proto +20 -1
  43. package/src/pb/index.ts +99 -3
  44. package/src/server/index.ts +75 -64
  45. package/src/server/reservation-store.ts +38 -42
  46. package/src/server/reservation-voucher.ts +2 -2
  47. package/src/transport/discovery.ts +1 -1
  48. package/src/transport/index.ts +2 -2
  49. package/src/transport/listener.ts +28 -25
  50. package/src/transport/reservation-store.ts +147 -57
  51. package/src/transport/transport.ts +12 -8
  52. package/src/utils.ts +21 -8
  53. package/dist/typedoc-urls.json +0 -23
@@ -1,15 +1,14 @@
1
- import { KEEP_ALIVE, TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
1
+ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
2
  import { PeerMap } from '@libp2p/peer-collections'
3
3
  import { createBloomFilter } from '@libp2p/utils/filters'
4
4
  import { PeerQueue } from '@libp2p/utils/peer-queue'
5
5
  import { multiaddr } from '@multiformats/multiaddr'
6
6
  import { pbStream } from 'it-protobuf-stream'
7
- import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
8
- import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
7
+ import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, KEEP_ALIVE_TAG, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
9
8
  import { HopMessage, Status } from '../pb/index.js'
10
9
  import { getExpirationMilliseconds } from '../utils.js'
11
10
  import type { Reservation } from '../pb/index.js'
12
- import type { TypedEventTarget, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, PeerId, PeerStore, Startable, Metrics } from '@libp2p/interface'
11
+ import type { TypedEventTarget, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, PeerId, PeerStore, Startable, Metrics, Peer } from '@libp2p/interface'
13
12
  import type { ConnectionManager, TransportManager } from '@libp2p/interface-internal'
14
13
  import type { Filter } from '@libp2p/utils/filters'
15
14
 
@@ -22,7 +21,7 @@ const REFRESH_TIMEOUT = (60 * 1000) * 5
22
21
  // minimum duration before which a reservation must not be refreshed
23
22
  const REFRESH_TIMEOUT_MIN = 30 * 1000
24
23
 
25
- export interface RelayStoreComponents {
24
+ export interface ReservationStoreComponents {
26
25
  peerId: PeerId
27
26
  connectionManager: ConnectionManager
28
27
  transportManager: TransportManager
@@ -32,7 +31,7 @@ export interface RelayStoreComponents {
32
31
  metrics?: Metrics
33
32
  }
34
33
 
35
- export interface RelayStoreInit {
34
+ export interface ReservationStoreInit {
36
35
  /**
37
36
  * Multiple relays may be discovered simultaneously - to prevent listening
38
37
  * on too many relays, this value controls how many to attempt to reserve a
@@ -71,6 +70,11 @@ interface RelayEntry {
71
70
  timeout: ReturnType<typeof setTimeout>
72
71
  type: RelayType
73
72
  reservation: Reservation
73
+
74
+ /**
75
+ * Stores the id of the connection we have to the relay
76
+ */
77
+ connection: string
74
78
  }
75
79
 
76
80
  export interface ReservationStoreEvents {
@@ -94,7 +98,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
94
98
  private readonly log: Logger
95
99
  private readonly relayFilter: Filter
96
100
 
97
- constructor (components: RelayStoreComponents, init?: RelayStoreInit) {
101
+ constructor (components: ReservationStoreComponents, init?: ReservationStoreInit) {
98
102
  super()
99
103
 
100
104
  this.log = components.logger.forComponent('libp2p:circuit-relay:transport:reservation-store')
@@ -117,11 +121,21 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
117
121
  metrics: components.metrics
118
122
  })
119
123
 
120
- // When a peer disconnects, if we had a reservation on that peer
121
- // remove the reservation and multiaddr and maybe trigger search
122
- // for new relays
123
- this.events.addEventListener('peer:disconnect', (evt) => {
124
- this.#removeRelay(evt.detail)
124
+ // reservations are only valid while we are still connected to the relay.
125
+ // if we had a reservation opened via that connection, remove it and maybe
126
+ // trigger a search for new relays
127
+ this.events.addEventListener('connection:close', (evt) => {
128
+ const reservation = [...this.reservations.values()]
129
+ .find(reservation => reservation.connection === evt.detail.id)
130
+
131
+ if (reservation == null) {
132
+ return
133
+ }
134
+
135
+ this.#removeReservation(evt.detail.remotePeer, reservation)
136
+ .catch(err => {
137
+ this.log('could not remove relay %p - %e', evt.detail, err)
138
+ })
125
139
  })
126
140
  }
127
141
 
@@ -134,10 +148,37 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
134
148
  }
135
149
 
136
150
  afterStart (): void {
137
- if (this.reservations.size < this.maxDiscoveredRelays) {
138
- this.log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays)
139
- this.safeDispatchEvent('relay:not-enough-relays', {})
140
- }
151
+ // remove old relay tags
152
+ void Promise.resolve()
153
+ .then(async () => {
154
+ const relayPeers: Peer[] = await this.peerStore.all({
155
+ filters: [(peer) => {
156
+ return peer.tags.has(RELAY_TAG)
157
+ }]
158
+ })
159
+
160
+ this.log('removing tag from %d old relays', relayPeers.length)
161
+
162
+ // remove old relay tag and redial
163
+ await Promise.all(
164
+ relayPeers.map(async peer => {
165
+ await this.peerStore.merge(peer.id, {
166
+ tags: {
167
+ [RELAY_TAG]: undefined,
168
+ [KEEP_ALIVE_TAG]: undefined
169
+ }
170
+ })
171
+ })
172
+ )
173
+
174
+ if (this.reservations.size < this.maxDiscoveredRelays) {
175
+ this.log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays)
176
+ this.safeDispatchEvent('relay:not-enough-relays', {})
177
+ }
178
+ })
179
+ .catch(err => {
180
+ this.log.error(err)
181
+ })
141
182
  }
142
183
 
143
184
  stop (): void {
@@ -157,26 +198,26 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
157
198
  */
158
199
  async addRelay (peerId: PeerId, type: RelayType): Promise<void> {
159
200
  if (this.peerId.equals(peerId)) {
160
- this.log('not trying to use self as relay')
201
+ this.log.trace('not trying to use self as relay')
161
202
  return
162
203
  }
163
204
 
164
205
  if (this.reserveQueue.size > this.maxReservationQueueLength) {
165
- this.log('not adding potential relay peer %p as the queue is full', peerId)
206
+ this.log.trace('not adding potential relay peer %p as the queue is full', peerId)
166
207
  return
167
208
  }
168
209
 
169
210
  if (this.reserveQueue.has(peerId)) {
170
- this.log('potential relay peer %p is already in the reservation queue', peerId)
211
+ this.log.trace('potential relay peer %p is already in the reservation queue', peerId)
171
212
  return
172
213
  }
173
214
 
174
215
  if (this.relayFilter.has(peerId.toMultihash().bytes)) {
175
- this.log('potential relay peer %p has failed previously, not trying again', peerId)
216
+ this.log.trace('potential relay peer %p has failed previously, not trying again', peerId)
176
217
  return
177
218
  }
178
219
 
179
- this.log('try to reserve relay slot with %p', peerId)
220
+ this.log.trace('try to reserve relay slot with %p', peerId)
180
221
 
181
222
  await this.reserveQueue.add(async () => {
182
223
  const start = Date.now()
@@ -186,13 +227,24 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
186
227
  const existingReservation = this.reservations.get(peerId)
187
228
 
188
229
  if (existingReservation != null) {
189
- if (getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) {
190
- this.log('already have reservation on relay peer %p and it expires in more than 10 minutes', peerId)
230
+ const connections = this.connectionManager.getConnections(peerId)
231
+ let connected = false
232
+
233
+ if (connections.length === 0) {
234
+ this.log('already have relay reservation with %p but we are no longer connected', peerId)
235
+ }
236
+
237
+ if (connections.map(conn => conn.id).includes(existingReservation.connection)) {
238
+ this.log('already have relay reservation with %p and the original connection is still open', peerId)
239
+ connected = true
240
+ }
241
+
242
+ if (connected && getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) {
243
+ this.log('already have relay reservation with %p but we are still connected and it does not expire soon', peerId)
191
244
  return
192
245
  }
193
246
 
194
- clearTimeout(existingReservation.timeout)
195
- this.reservations.delete(peerId)
247
+ await this.#removeReservation(peerId, existingReservation)
196
248
  }
197
249
 
198
250
  if (type === 'discovered' && [...this.reservations.values()].reduce((acc, curr) => {
@@ -202,7 +254,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
202
254
 
203
255
  return acc
204
256
  }, 0) >= this.maxDiscoveredRelays) {
205
- this.log('already have enough discovered relays')
257
+ this.log.trace('already have enough discovered relays')
206
258
  return
207
259
  }
208
260
 
@@ -222,25 +274,41 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
222
274
  signal
223
275
  })
224
276
 
225
- this.log('created reservation on relay peer %p', peerId)
226
-
227
277
  const expiration = getExpirationMilliseconds(reservation.expire)
228
278
 
279
+ this.log('created reservation on relay peer %p, expiry date is %s', peerId, new Date(Date.now() + expiration).toString())
280
+
229
281
  // sets a lower bound on the timeout, and also don't let it go over
230
282
  // 2^31 - 1 (setTimeout will only accept signed 32 bit integers)
231
283
  const timeoutDuration = Math.min(Math.max(expiration - REFRESH_TIMEOUT, REFRESH_TIMEOUT_MIN), Math.pow(2, 31) - 1)
232
284
 
233
285
  const timeout = setTimeout(() => {
234
- this.addRelay(peerId, type).catch(err => {
235
- this.log.error('could not refresh reservation to relay %p', peerId, err)
236
- })
286
+ this.log('refresh reservation to relay %p', peerId)
287
+
288
+ this.addRelay(peerId, type)
289
+ .catch(async err => {
290
+ this.log.error('could not refresh reservation to relay %p - %e', peerId, err)
291
+
292
+ const reservation = this.reservations.get(peerId)
293
+
294
+ if (reservation == null) {
295
+ this.log.error('did not have reservation after refreshing reservation failed %p', peerId)
296
+ return
297
+ }
298
+
299
+ await this.#removeReservation(peerId, reservation)
300
+ })
301
+ .catch(err => {
302
+ this.log.error('could not remove expired reservation to relay %p - %e', peerId, err)
303
+ })
237
304
  }, timeoutDuration)
238
305
 
239
306
  // we've managed to create a reservation successfully
240
307
  this.reservations.set(peerId, {
241
308
  timeout,
242
309
  reservation,
243
- type
310
+ type,
311
+ connection: connection.id
244
312
  })
245
313
 
246
314
  // ensure we don't close the connection to the relay
@@ -250,7 +318,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
250
318
  value: 1,
251
319
  ttl: expiration
252
320
  },
253
- [KEEP_ALIVE]: {
321
+ [KEEP_ALIVE_TAG]: {
254
322
  value: 1,
255
323
  ttl: expiration
256
324
  }
@@ -258,7 +326,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
258
326
  })
259
327
 
260
328
  // listen on multiaddr that only the circuit transport is listening for
261
- await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit`)])
329
+ await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit/p2p/${this.peerId.toString()}`)])
262
330
 
263
331
  this.safeDispatchEvent('relay:created-reservation', {
264
332
  detail: peerId
@@ -266,18 +334,19 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
266
334
  } catch (err) {
267
335
  this.log.error('could not reserve slot on %p after %dms', peerId, Date.now() - start, err)
268
336
 
337
+ // don't try this peer again
338
+ this.relayFilter.add(peerId.toMultihash().bytes)
339
+
269
340
  // cancel the renewal timeout if it's been set
270
341
  const reservation = this.reservations.get(peerId)
271
342
 
343
+ // if listening failed, remove the reservation
272
344
  if (reservation != null) {
273
- clearTimeout(reservation.timeout)
345
+ this.#removeReservation(peerId, reservation)
346
+ .catch(err => {
347
+ this.log.error('could not remove reservation on %p after reserving slot failed - %e', peerId, err)
348
+ })
274
349
  }
275
-
276
- // if listening failed, remove the reservation
277
- this.reservations.delete(peerId)
278
-
279
- // don't try this peer again
280
- this.relayFilter.add(peerId.toMultihash().bytes)
281
350
  }
282
351
  }, {
283
352
  peerId
@@ -296,6 +365,14 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
296
365
  return this.reservations.size
297
366
  }
298
367
 
368
+ async cancelReservations (): Promise<void> {
369
+ await Promise.all(
370
+ [...this.reservations.entries()].map(async ([peerId, reservation]) => {
371
+ await this.#removeReservation(peerId, reservation)
372
+ })
373
+ )
374
+ }
375
+
299
376
  async #createReservation (connection: Connection, options: AbortOptions): Promise<Reservation> {
300
377
  options.signal?.throwIfAborted()
301
378
 
@@ -318,24 +395,31 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
318
395
  }
319
396
  }
320
397
 
321
- if (response.status === Status.OK && (response.reservation != null)) {
398
+ if (response.status === Status.OK && response.reservation != null) {
322
399
  // check that the returned relay has the relay address - this can be
323
400
  // omitted when requesting a reservation from a go-libp2p relay we
324
401
  // already have a reservation on
325
- let hasRelayAddress = false
326
- const relayAddressBytes = connection.remoteAddr.bytes
402
+ const addresses = new Set<string>()
403
+ addresses.add(connection.remoteAddr.toString())
327
404
 
328
405
  for (const buf of response.reservation.addrs) {
329
- if (uint8ArrayEquals(relayAddressBytes, buf)) {
330
- hasRelayAddress = true
331
- break
406
+ let ma = multiaddr(buf)
407
+
408
+ if (ma.getPeerId() == null) {
409
+ ma = ma.encapsulate(`/p2p/${connection.remotePeer}`)
332
410
  }
333
- }
334
411
 
335
- if (!hasRelayAddress) {
336
- response.reservation.addrs.push(relayAddressBytes)
412
+ // TODO: workaround for https://github.com/libp2p/go-libp2p/issues/3003
413
+ ma = multiaddr(ma.toString().replace(
414
+ `/p2p/${connection.remotePeer}/p2p/${connection.remotePeer}`,
415
+ `/p2p/${connection.remotePeer}`
416
+ ))
417
+
418
+ addresses.add(ma.toString())
337
419
  }
338
420
 
421
+ response.reservation.addrs = [...addresses].map(str => multiaddr(str).bytes)
422
+
339
423
  return response.reservation
340
424
  }
341
425
 
@@ -348,18 +432,24 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
348
432
  /**
349
433
  * Remove listen relay
350
434
  */
351
- #removeRelay (peerId: PeerId): void {
352
- const existingReservation = this.reservations.get(peerId)
353
-
354
- if (existingReservation == null) {
435
+ async #removeReservation (peerId: PeerId, reservation: RelayEntry): Promise<void> {
436
+ if (!this.reservations.has(peerId)) {
437
+ this.log('not removing relay reservation with %p from local store as we do not have a reservation with this peer', peerId)
355
438
  return
356
439
  }
357
440
 
358
- this.log('connection to relay %p closed, removing reservation from local store', peerId)
359
-
360
- clearTimeout(existingReservation.timeout)
441
+ this.log('removing relay reservation with %p from local store', peerId)
442
+ clearTimeout(reservation.timeout)
361
443
  this.reservations.delete(peerId)
362
444
 
445
+ // untag the relay
446
+ await this.peerStore.merge(peerId, {
447
+ tags: {
448
+ [RELAY_TAG]: undefined,
449
+ [KEEP_ALIVE_TAG]: undefined
450
+ }
451
+ })
452
+
363
453
  this.safeDispatchEvent('relay:removed', { detail: peerId })
364
454
 
365
455
  if (this.reservations.size < this.maxDiscoveredRelays) {
@@ -2,14 +2,14 @@ import { DialError, InvalidMessageError, serviceCapabilities, serviceDependencie
2
2
  import { peerFilter } from '@libp2p/peer-collections'
3
3
  import { peerIdFromMultihash, peerIdFromString } from '@libp2p/peer-id'
4
4
  import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
5
- import * as mafmt from '@multiformats/mafmt'
6
5
  import { multiaddr } from '@multiformats/multiaddr'
6
+ import { Circuit } from '@multiformats/multiaddr-matcher'
7
7
  import { pbStream } from 'it-protobuf-stream'
8
8
  import * as Digest from 'multiformats/hashes/digest'
9
9
  import { CustomProgressEvent } from 'progress-events'
10
10
  import { CIRCUIT_PROTO_CODE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE, DEFAULT_DISCOVERY_FILTER_SIZE, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
11
11
  import { StopMessage, HopMessage, Status } from '../pb/index.js'
12
- import { LimitTracker } from '../utils.js'
12
+ import { CircuitListen, LimitTracker } from '../utils.js'
13
13
  import { RelayDiscovery } from './discovery.js'
14
14
  import { createListener } from './listener.js'
15
15
  import { ReservationStore } from './reservation-store.js'
@@ -184,9 +184,9 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
184
184
  const destinationId = destinationAddr.getPeerId()
185
185
 
186
186
  if (relayId == null || destinationId == null) {
187
- const errMsg = `Circuit relay dial to ${ma.toString()} failed as address did not have peer ids`
188
- this.log.error(errMsg)
189
- throw new DialError(errMsg)
187
+ const errMsg = `ircuit relay dial to ${ma.toString()} failed as address did not have both relay and destination PeerIDs`
188
+ this.log.error(`c${errMsg}`)
189
+ throw new DialError(`C${errMsg}`)
190
190
  }
191
191
 
192
192
  const relayPeer = peerIdFromString(relayId)
@@ -281,7 +281,7 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
281
281
  onProgress
282
282
  })
283
283
  } catch (err: any) {
284
- this.log.error(`Circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err)
284
+ this.log.error(`circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err)
285
285
  disconnectOnFailure && await connection.close()
286
286
  throw err
287
287
  }
@@ -305,7 +305,7 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
305
305
  multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
306
306
 
307
307
  return multiaddrs.filter((ma) => {
308
- return mafmt.Circuit.matches(ma)
308
+ return CircuitListen.exactMatch(ma)
309
309
  })
310
310
  }
311
311
 
@@ -313,7 +313,11 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
313
313
  * Filter check for all Multiaddrs that this transport can dial
314
314
  */
315
315
  dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
316
- return this.listenFilter(multiaddrs)
316
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
317
+
318
+ return multiaddrs.filter((ma) => {
319
+ return Circuit.exactMatch(ma)
320
+ })
317
321
  }
318
322
 
319
323
  /**
package/src/utils.ts CHANGED
@@ -1,7 +1,10 @@
1
+ import { P2P } from '@multiformats/multiaddr-matcher'
2
+ import { fmt, peerId, literal, optional, and } from '@multiformats/multiaddr-matcher/utils'
1
3
  import { anySignal } from 'any-signal'
2
4
  import { CID } from 'multiformats/cid'
3
5
  import { sha256 } from 'multiformats/hashes/sha2'
4
6
  import { DurationLimitError, TransferLimitError } from './errors.js'
7
+ import type { RelayReservation } from './index.js'
5
8
  import type { Limit } from './pb/index.js'
6
9
  import type { ConnectionLimits, LoggerOptions, Stream } from '@libp2p/interface'
7
10
  import type { Source } from 'it-stream-types'
@@ -34,16 +37,18 @@ async function * countStreamBytes (source: Source<Uint8Array | Uint8ArrayList>,
34
37
  }
35
38
  }
36
39
 
37
- export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal, limit: Limit | undefined, options: LoggerOptions): void {
40
+ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal, reservation: RelayReservation, options: LoggerOptions): void {
38
41
  function abortStreams (err: Error): void {
39
42
  src.abort(err)
40
43
  dst.abort(err)
41
44
  }
42
45
 
43
- const signals = [abortSignal]
46
+ // combine shutdown signal and reservation expiry signal
47
+ const signals = [abortSignal, reservation.signal]
44
48
 
45
- if (limit?.duration != null) {
46
- signals.push(AbortSignal.timeout(limit.duration))
49
+ if (reservation.limit?.duration != null) {
50
+ options.log('limiting relayed connection duration to %dms', reservation.limit.duration)
51
+ signals.push(AbortSignal.timeout(reservation.limit.duration))
47
52
  }
48
53
 
49
54
  const signal = anySignal(signals)
@@ -53,15 +58,16 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort
53
58
 
54
59
  let dataLimit: { remaining: bigint } | undefined
55
60
 
56
- if (limit?.data != null) {
61
+ if (reservation.limit?.data != null) {
57
62
  dataLimit = {
58
- remaining: limit.data
63
+ remaining: reservation.limit.data
59
64
  }
60
65
  }
61
66
 
62
67
  queueMicrotask(() => {
63
68
  const onAbort = (): void => {
64
- dst.abort(new DurationLimitError(`duration limit of ${limit?.duration} ms exceeded`))
69
+ options.log('relayed connection reached time limit')
70
+ dst.abort(new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`))
65
71
  }
66
72
 
67
73
  signal.addEventListener('abort', onAbort, { once: true })
@@ -83,7 +89,8 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort
83
89
 
84
90
  queueMicrotask(() => {
85
91
  const onAbort = (): void => {
86
- src.abort(new DurationLimitError(`duration limit of ${limit?.duration} ms exceeded`))
92
+ options.log('relayed connection reached time limit')
93
+ src.abort(new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`))
87
94
  }
88
95
 
89
96
  signal.addEventListener('abort', onAbort, { once: true })
@@ -185,3 +192,9 @@ export class LimitTracker {
185
192
  return output
186
193
  }
187
194
  }
195
+
196
+ /**
197
+ * A custom matcher that makes the `/p2p/peer-id` part of circuit relay
198
+ * addresses optional
199
+ */
200
+ export const CircuitListen = fmt(and(P2P.matchers[0], literal('p2p-circuit'), optional(peerId())))
@@ -1,23 +0,0 @@
1
- {
2
- "codec": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.Limit.codec.html",
3
- "decode": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.Limit.decode.html",
4
- "encode": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.Limit.encode.html",
5
- "CircuitRelayServerComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServerComponents.html",
6
- "CircuitRelayServerInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServerInit.html",
7
- "CircuitRelayService": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayService.html",
8
- ".:CircuitRelayService": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayService.html",
9
- "CircuitRelayServiceEvents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServiceEvents.html",
10
- ".:CircuitRelayServiceEvents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServiceEvents.html",
11
- "CircuitRelayTransportComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayTransportComponents.html",
12
- "CircuitRelayTransportInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayTransportInit.html",
13
- "Limit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.Limit-1.html",
14
- "RelayDiscoveryComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayDiscoveryComponents.html",
15
- "RelayReservation": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayReservation.html",
16
- ".:RelayReservation": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayReservation.html",
17
- "RelayStoreInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayStoreInit.html",
18
- "ReservationStoreInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.ReservationStoreInit.html",
19
- "RELAY_V2_HOP_CODEC": "https://libp2p.github.io/js-libp2p/variables/_libp2p_circuit_relay_v2.RELAY_V2_HOP_CODEC.html",
20
- "RELAY_V2_STOP_CODEC": "https://libp2p.github.io/js-libp2p/variables/_libp2p_circuit_relay_v2.RELAY_V2_STOP_CODEC.html",
21
- "circuitRelayServer": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.circuitRelayServer.html",
22
- "circuitRelayTransport": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.circuitRelayTransport.html"
23
- }