@libp2p/circuit-relay-v2 2.1.4 → 2.1.5-75301ac7d

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. package/dist/index.min.js +2 -2
  2. package/dist/src/constants.d.ts +0 -4
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +0 -4
  5. package/dist/src/constants.js.map +1 -1
  6. package/dist/src/index.d.ts +16 -1
  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 +19 -10
  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/reservation-store.d.ts.map +1 -1
  22. package/dist/src/transport/reservation-store.js +36 -11
  23. package/dist/src/transport/reservation-store.js.map +1 -1
  24. package/dist/src/transport/transport.d.ts.map +1 -1
  25. package/dist/src/transport/transport.js +11 -8
  26. package/dist/src/transport/transport.js.map +1 -1
  27. package/dist/src/utils.d.ts +7 -1
  28. package/dist/src/utils.d.ts.map +1 -1
  29. package/dist/src/utils.js +19 -8
  30. package/dist/src/utils.js.map +1 -1
  31. package/package.json +12 -11
  32. package/src/constants.ts +0 -5
  33. package/src/index.ts +19 -1
  34. package/src/pb/index.proto +20 -1
  35. package/src/pb/index.ts +99 -3
  36. package/src/server/index.ts +21 -11
  37. package/src/server/reservation-store.ts +38 -42
  38. package/src/server/reservation-voucher.ts +2 -2
  39. package/src/transport/reservation-store.ts +47 -15
  40. package/src/transport/transport.ts +12 -8
  41. package/src/utils.ts +21 -8
  42. package/dist/typedoc-urls.json +0 -23
package/src/pb/index.ts CHANGED
@@ -318,7 +318,7 @@ export namespace Peer {
318
318
  export interface Reservation {
319
319
  expire: bigint
320
320
  addrs: Uint8Array[]
321
- voucher?: Uint8Array
321
+ voucher?: Envelope
322
322
  }
323
323
 
324
324
  export namespace Reservation {
@@ -345,7 +345,7 @@ export namespace Reservation {
345
345
 
346
346
  if (obj.voucher != null) {
347
347
  w.uint32(26)
348
- w.bytes(obj.voucher)
348
+ Envelope.codec().encode(obj.voucher, w)
349
349
  }
350
350
 
351
351
  if (opts.lengthDelimited !== false) {
@@ -376,7 +376,9 @@ export namespace Reservation {
376
376
  break
377
377
  }
378
378
  case 3: {
379
- obj.voucher = reader.bytes()
379
+ obj.voucher = Envelope.codec().decode(reader, reader.uint32(), {
380
+ limits: opts.limits?.voucher
381
+ })
380
382
  break
381
383
  }
382
384
  default: {
@@ -580,3 +582,97 @@ export namespace ReservationVoucher {
580
582
  return decodeMessage(buf, ReservationVoucher.codec(), opts)
581
583
  }
582
584
  }
585
+
586
+ export interface Envelope {
587
+ publicKey: Uint8Array
588
+ payloadType: Uint8Array
589
+ payload?: ReservationVoucher
590
+ signature: Uint8Array
591
+ }
592
+
593
+ export namespace Envelope {
594
+ let _codec: Codec<Envelope>
595
+
596
+ export const codec = (): Codec<Envelope> => {
597
+ if (_codec == null) {
598
+ _codec = message<Envelope>((obj, w, opts = {}) => {
599
+ if (opts.lengthDelimited !== false) {
600
+ w.fork()
601
+ }
602
+
603
+ if ((obj.publicKey != null && obj.publicKey.byteLength > 0)) {
604
+ w.uint32(10)
605
+ w.bytes(obj.publicKey)
606
+ }
607
+
608
+ if ((obj.payloadType != null && obj.payloadType.byteLength > 0)) {
609
+ w.uint32(18)
610
+ w.bytes(obj.payloadType)
611
+ }
612
+
613
+ if (obj.payload != null) {
614
+ w.uint32(26)
615
+ ReservationVoucher.codec().encode(obj.payload, w)
616
+ }
617
+
618
+ if ((obj.signature != null && obj.signature.byteLength > 0)) {
619
+ w.uint32(42)
620
+ w.bytes(obj.signature)
621
+ }
622
+
623
+ if (opts.lengthDelimited !== false) {
624
+ w.ldelim()
625
+ }
626
+ }, (reader, length, opts = {}) => {
627
+ const obj: any = {
628
+ publicKey: uint8ArrayAlloc(0),
629
+ payloadType: uint8ArrayAlloc(0),
630
+ signature: uint8ArrayAlloc(0)
631
+ }
632
+
633
+ const end = length == null ? reader.len : reader.pos + length
634
+
635
+ while (reader.pos < end) {
636
+ const tag = reader.uint32()
637
+
638
+ switch (tag >>> 3) {
639
+ case 1: {
640
+ obj.publicKey = reader.bytes()
641
+ break
642
+ }
643
+ case 2: {
644
+ obj.payloadType = reader.bytes()
645
+ break
646
+ }
647
+ case 3: {
648
+ obj.payload = ReservationVoucher.codec().decode(reader, reader.uint32(), {
649
+ limits: opts.limits?.payload
650
+ })
651
+ break
652
+ }
653
+ case 5: {
654
+ obj.signature = reader.bytes()
655
+ break
656
+ }
657
+ default: {
658
+ reader.skipType(tag & 7)
659
+ break
660
+ }
661
+ }
662
+ }
663
+
664
+ return obj
665
+ })
666
+ }
667
+
668
+ return _codec
669
+ }
670
+
671
+ export const encode = (obj: Partial<Envelope>): Uint8Array => {
672
+ return encodeMessage(obj, Envelope.codec())
673
+ }
674
+
675
+ export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Envelope>): Envelope => {
676
+ return decodeMessage(buf, Envelope.codec(), opts)
677
+ }
678
+ }
@@ -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
@@ -227,12 +227,23 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
227
227
  const existingReservation = this.reservations.get(peerId)
228
228
 
229
229
  if (existingReservation != null) {
230
- if (this.connectionManager.getConnections(peerId).map(conn => conn.id).includes(existingReservation.connection) && getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) {
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) {
231
243
  this.log('already have relay reservation with %p but we are still connected and it does not expire soon', peerId)
232
244
  return
233
245
  }
234
246
 
235
- this.log('already have relay reservation with %p but the original connection is no longer open', peerId)
236
247
  await this.#removeReservation(peerId, existingReservation)
237
248
  }
238
249
 
@@ -263,18 +274,33 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
263
274
  signal
264
275
  })
265
276
 
266
- this.log('created reservation on relay peer %p', peerId)
267
-
268
277
  const expiration = getExpirationMilliseconds(reservation.expire)
269
278
 
279
+ this.log('created reservation on relay peer %p, expiry date is %s', peerId, new Date(Date.now() + expiration).toString())
280
+
270
281
  // sets a lower bound on the timeout, and also don't let it go over
271
282
  // 2^31 - 1 (setTimeout will only accept signed 32 bit integers)
272
283
  const timeoutDuration = Math.min(Math.max(expiration - REFRESH_TIMEOUT, REFRESH_TIMEOUT_MIN), Math.pow(2, 31) - 1)
273
284
 
274
285
  const timeout = setTimeout(() => {
275
- this.addRelay(peerId, type).catch(err => {
276
- this.log.error('could not refresh reservation to relay %p', peerId, err)
277
- })
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
+ })
278
304
  }, timeoutDuration)
279
305
 
280
306
  // we've managed to create a reservation successfully
@@ -300,7 +326,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
300
326
  })
301
327
 
302
328
  // listen on multiaddr that only the circuit transport is listening for
303
- 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()}`)])
304
330
 
305
331
  this.safeDispatchEvent('relay:created-reservation', {
306
332
  detail: peerId
@@ -308,18 +334,19 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
308
334
  } catch (err) {
309
335
  this.log.error('could not reserve slot on %p after %dms', peerId, Date.now() - start, err)
310
336
 
337
+ // don't try this peer again
338
+ this.relayFilter.add(peerId.toMultihash().bytes)
339
+
311
340
  // cancel the renewal timeout if it's been set
312
341
  const reservation = this.reservations.get(peerId)
313
342
 
343
+ // if listening failed, remove the reservation
314
344
  if (reservation != null) {
315
- 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
+ })
316
349
  }
317
-
318
- // if listening failed, remove the reservation
319
- this.reservations.delete(peerId)
320
-
321
- // don't try this peer again
322
- this.relayFilter.add(peerId.toMultihash().bytes)
323
350
  }
324
351
  }, {
325
352
  peerId
@@ -406,6 +433,11 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
406
433
  * Remove listen relay
407
434
  */
408
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)
438
+ return
439
+ }
440
+
409
441
  this.log('removing relay reservation with %p from local store', peerId)
410
442
  clearTimeout(reservation.timeout)
411
443
  this.reservations.delete(peerId)
@@ -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
- "ServerReservationStoreInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.ServerReservationStoreInit.html",
18
- "TransportReservationStoreInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.TransportReservationStoreInit.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
- }