@libp2p/circuit-relay-v2 1.0.24-62e32252a → 1.0.24-757fb2674

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 (42) hide show
  1. package/dist/index.min.js +3 -3
  2. package/dist/src/constants.d.ts +12 -13
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +12 -13
  5. package/dist/src/constants.js.map +1 -1
  6. package/dist/src/pb/index.d.ts +7 -7
  7. package/dist/src/pb/index.d.ts.map +1 -1
  8. package/dist/src/pb/index.js +52 -94
  9. package/dist/src/pb/index.js.map +1 -1
  10. package/dist/src/server/advert-service.d.ts +44 -0
  11. package/dist/src/server/advert-service.d.ts.map +1 -0
  12. package/dist/src/server/advert-service.js +72 -0
  13. package/dist/src/server/advert-service.js.map +1 -0
  14. package/dist/src/server/index.d.ts +7 -1
  15. package/dist/src/server/index.d.ts.map +1 -1
  16. package/dist/src/server/index.js +19 -10
  17. package/dist/src/server/index.js.map +1 -1
  18. package/dist/src/transport/discovery.d.ts +10 -15
  19. package/dist/src/transport/discovery.d.ts.map +1 -1
  20. package/dist/src/transport/discovery.js +51 -103
  21. package/dist/src/transport/discovery.js.map +1 -1
  22. package/dist/src/transport/index.d.ts +18 -27
  23. package/dist/src/transport/index.d.ts.map +1 -1
  24. package/dist/src/transport/index.js +3 -0
  25. package/dist/src/transport/index.js.map +1 -1
  26. package/dist/src/transport/reservation-store.d.ts +3 -7
  27. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  28. package/dist/src/transport/reservation-store.js +13 -37
  29. package/dist/src/transport/reservation-store.js.map +1 -1
  30. package/dist/src/transport/transport.d.ts +1 -0
  31. package/dist/src/transport/transport.d.ts.map +1 -1
  32. package/dist/src/transport/transport.js +19 -20
  33. package/dist/src/transport/transport.js.map +1 -1
  34. package/package.json +12 -11
  35. package/src/constants.ts +15 -16
  36. package/src/pb/index.ts +53 -96
  37. package/src/server/advert-service.ts +107 -0
  38. package/src/server/index.ts +29 -11
  39. package/src/transport/discovery.ts +56 -121
  40. package/src/transport/index.ts +18 -28
  41. package/src/transport/reservation-store.ts +13 -45
  42. package/src/transport/transport.ts +21 -21
package/src/pb/index.ts CHANGED
@@ -4,8 +4,8 @@
4
4
  /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
5
5
  /* eslint-disable @typescript-eslint/no-empty-interface */
6
6
 
7
- import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
8
- import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
7
+ import { enumeration, encodeMessage, decodeMessage, message } from 'protons-runtime'
8
+ import type { Codec } from 'protons-runtime'
9
9
  import type { Uint8ArrayList } from 'uint8arraylist'
10
10
 
11
11
  export interface HopMessage {
@@ -72,7 +72,7 @@ export namespace HopMessage {
72
72
  if (opts.lengthDelimited !== false) {
73
73
  w.ldelim()
74
74
  }
75
- }, (reader, length, opts = {}) => {
75
+ }, (reader, length) => {
76
76
  const obj: any = {}
77
77
 
78
78
  const end = length == null ? reader.len : reader.pos + length
@@ -81,36 +81,24 @@ export namespace HopMessage {
81
81
  const tag = reader.uint32()
82
82
 
83
83
  switch (tag >>> 3) {
84
- case 1: {
84
+ case 1:
85
85
  obj.type = HopMessage.Type.codec().decode(reader)
86
86
  break
87
- }
88
- case 2: {
89
- obj.peer = Peer.codec().decode(reader, reader.uint32(), {
90
- limits: opts.limits?.peer
91
- })
87
+ case 2:
88
+ obj.peer = Peer.codec().decode(reader, reader.uint32())
92
89
  break
93
- }
94
- case 3: {
95
- obj.reservation = Reservation.codec().decode(reader, reader.uint32(), {
96
- limits: opts.limits?.reservation
97
- })
90
+ case 3:
91
+ obj.reservation = Reservation.codec().decode(reader, reader.uint32())
98
92
  break
99
- }
100
- case 4: {
101
- obj.limit = Limit.codec().decode(reader, reader.uint32(), {
102
- limits: opts.limits?.limit
103
- })
93
+ case 4:
94
+ obj.limit = Limit.codec().decode(reader, reader.uint32())
104
95
  break
105
- }
106
- case 5: {
96
+ case 5:
107
97
  obj.status = Status.codec().decode(reader)
108
98
  break
109
- }
110
- default: {
99
+ default:
111
100
  reader.skipType(tag & 7)
112
101
  break
113
- }
114
102
  }
115
103
  }
116
104
 
@@ -125,8 +113,8 @@ export namespace HopMessage {
125
113
  return encodeMessage(obj, HopMessage.codec())
126
114
  }
127
115
 
128
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<HopMessage>): HopMessage => {
129
- return decodeMessage(buf, HopMessage.codec(), opts)
116
+ export const decode = (buf: Uint8Array | Uint8ArrayList): HopMessage => {
117
+ return decodeMessage(buf, HopMessage.codec())
130
118
  }
131
119
  }
132
120
 
@@ -186,7 +174,7 @@ export namespace StopMessage {
186
174
  if (opts.lengthDelimited !== false) {
187
175
  w.ldelim()
188
176
  }
189
- }, (reader, length, opts = {}) => {
177
+ }, (reader, length) => {
190
178
  const obj: any = {}
191
179
 
192
180
  const end = length == null ? reader.len : reader.pos + length
@@ -195,30 +183,21 @@ export namespace StopMessage {
195
183
  const tag = reader.uint32()
196
184
 
197
185
  switch (tag >>> 3) {
198
- case 1: {
186
+ case 1:
199
187
  obj.type = StopMessage.Type.codec().decode(reader)
200
188
  break
201
- }
202
- case 2: {
203
- obj.peer = Peer.codec().decode(reader, reader.uint32(), {
204
- limits: opts.limits?.peer
205
- })
189
+ case 2:
190
+ obj.peer = Peer.codec().decode(reader, reader.uint32())
206
191
  break
207
- }
208
- case 3: {
209
- obj.limit = Limit.codec().decode(reader, reader.uint32(), {
210
- limits: opts.limits?.limit
211
- })
192
+ case 3:
193
+ obj.limit = Limit.codec().decode(reader, reader.uint32())
212
194
  break
213
- }
214
- case 4: {
195
+ case 4:
215
196
  obj.status = Status.codec().decode(reader)
216
197
  break
217
- }
218
- default: {
198
+ default:
219
199
  reader.skipType(tag & 7)
220
200
  break
221
- }
222
201
  }
223
202
  }
224
203
 
@@ -233,8 +212,8 @@ export namespace StopMessage {
233
212
  return encodeMessage(obj, StopMessage.codec())
234
213
  }
235
214
 
236
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<StopMessage>): StopMessage => {
237
- return decodeMessage(buf, StopMessage.codec(), opts)
215
+ export const decode = (buf: Uint8Array | Uint8ArrayList): StopMessage => {
216
+ return decodeMessage(buf, StopMessage.codec())
238
217
  }
239
218
  }
240
219
 
@@ -268,9 +247,9 @@ export namespace Peer {
268
247
  if (opts.lengthDelimited !== false) {
269
248
  w.ldelim()
270
249
  }
271
- }, (reader, length, opts = {}) => {
250
+ }, (reader, length) => {
272
251
  const obj: any = {
273
- id: uint8ArrayAlloc(0),
252
+ id: new Uint8Array(0),
274
253
  addrs: []
275
254
  }
276
255
 
@@ -280,22 +259,15 @@ export namespace Peer {
280
259
  const tag = reader.uint32()
281
260
 
282
261
  switch (tag >>> 3) {
283
- case 1: {
262
+ case 1:
284
263
  obj.id = reader.bytes()
285
264
  break
286
- }
287
- case 2: {
288
- if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
289
- throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
290
- }
291
-
265
+ case 2:
292
266
  obj.addrs.push(reader.bytes())
293
267
  break
294
- }
295
- default: {
268
+ default:
296
269
  reader.skipType(tag & 7)
297
270
  break
298
- }
299
271
  }
300
272
  }
301
273
 
@@ -310,8 +282,8 @@ export namespace Peer {
310
282
  return encodeMessage(obj, Peer.codec())
311
283
  }
312
284
 
313
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Peer>): Peer => {
314
- return decodeMessage(buf, Peer.codec(), opts)
285
+ export const decode = (buf: Uint8Array | Uint8ArrayList): Peer => {
286
+ return decodeMessage(buf, Peer.codec())
315
287
  }
316
288
  }
317
289
 
@@ -351,7 +323,7 @@ export namespace Reservation {
351
323
  if (opts.lengthDelimited !== false) {
352
324
  w.ldelim()
353
325
  }
354
- }, (reader, length, opts = {}) => {
326
+ }, (reader, length) => {
355
327
  const obj: any = {
356
328
  expire: 0n,
357
329
  addrs: []
@@ -363,26 +335,18 @@ export namespace Reservation {
363
335
  const tag = reader.uint32()
364
336
 
365
337
  switch (tag >>> 3) {
366
- case 1: {
338
+ case 1:
367
339
  obj.expire = reader.uint64()
368
340
  break
369
- }
370
- case 2: {
371
- if (opts.limits?.addrs != null && obj.addrs.length === opts.limits.addrs) {
372
- throw new CodeError('decode error - map field "addrs" had too many elements', 'ERR_MAX_LENGTH')
373
- }
374
-
341
+ case 2:
375
342
  obj.addrs.push(reader.bytes())
376
343
  break
377
- }
378
- case 3: {
344
+ case 3:
379
345
  obj.voucher = reader.bytes()
380
346
  break
381
- }
382
- default: {
347
+ default:
383
348
  reader.skipType(tag & 7)
384
349
  break
385
- }
386
350
  }
387
351
  }
388
352
 
@@ -397,8 +361,8 @@ export namespace Reservation {
397
361
  return encodeMessage(obj, Reservation.codec())
398
362
  }
399
363
 
400
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Reservation>): Reservation => {
401
- return decodeMessage(buf, Reservation.codec(), opts)
364
+ export const decode = (buf: Uint8Array | Uint8ArrayList): Reservation => {
365
+ return decodeMessage(buf, Reservation.codec())
402
366
  }
403
367
  }
404
368
 
@@ -430,7 +394,7 @@ export namespace Limit {
430
394
  if (opts.lengthDelimited !== false) {
431
395
  w.ldelim()
432
396
  }
433
- }, (reader, length, opts = {}) => {
397
+ }, (reader, length) => {
434
398
  const obj: any = {}
435
399
 
436
400
  const end = length == null ? reader.len : reader.pos + length
@@ -439,18 +403,15 @@ export namespace Limit {
439
403
  const tag = reader.uint32()
440
404
 
441
405
  switch (tag >>> 3) {
442
- case 1: {
406
+ case 1:
443
407
  obj.duration = reader.uint32()
444
408
  break
445
- }
446
- case 2: {
409
+ case 2:
447
410
  obj.data = reader.uint64()
448
411
  break
449
- }
450
- default: {
412
+ default:
451
413
  reader.skipType(tag & 7)
452
414
  break
453
- }
454
415
  }
455
416
  }
456
417
 
@@ -465,8 +426,8 @@ export namespace Limit {
465
426
  return encodeMessage(obj, Limit.codec())
466
427
  }
467
428
 
468
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Limit>): Limit => {
469
- return decodeMessage(buf, Limit.codec(), opts)
429
+ export const decode = (buf: Uint8Array | Uint8ArrayList): Limit => {
430
+ return decodeMessage(buf, Limit.codec())
470
431
  }
471
432
  }
472
433
 
@@ -533,10 +494,10 @@ export namespace ReservationVoucher {
533
494
  if (opts.lengthDelimited !== false) {
534
495
  w.ldelim()
535
496
  }
536
- }, (reader, length, opts = {}) => {
497
+ }, (reader, length) => {
537
498
  const obj: any = {
538
- relay: uint8ArrayAlloc(0),
539
- peer: uint8ArrayAlloc(0),
499
+ relay: new Uint8Array(0),
500
+ peer: new Uint8Array(0),
540
501
  expiration: 0n
541
502
  }
542
503
 
@@ -546,22 +507,18 @@ export namespace ReservationVoucher {
546
507
  const tag = reader.uint32()
547
508
 
548
509
  switch (tag >>> 3) {
549
- case 1: {
510
+ case 1:
550
511
  obj.relay = reader.bytes()
551
512
  break
552
- }
553
- case 2: {
513
+ case 2:
554
514
  obj.peer = reader.bytes()
555
515
  break
556
- }
557
- case 3: {
516
+ case 3:
558
517
  obj.expiration = reader.uint64()
559
518
  break
560
- }
561
- default: {
519
+ default:
562
520
  reader.skipType(tag & 7)
563
521
  break
564
- }
565
522
  }
566
523
  }
567
524
 
@@ -576,7 +533,7 @@ export namespace ReservationVoucher {
576
533
  return encodeMessage(obj, ReservationVoucher.codec())
577
534
  }
578
535
 
579
- export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<ReservationVoucher>): ReservationVoucher => {
580
- return decodeMessage(buf, ReservationVoucher.codec(), opts)
536
+ export const decode = (buf: Uint8Array | Uint8ArrayList): ReservationVoucher => {
537
+ return decodeMessage(buf, ReservationVoucher.codec())
581
538
  }
582
539
  }
@@ -0,0 +1,107 @@
1
+ import { TypedEventEmitter } from '@libp2p/interface'
2
+ import pRetry from 'p-retry'
3
+ import {
4
+ DEFAULT_ADVERT_BOOT_DELAY,
5
+ ERR_NO_ROUTERS_AVAILABLE,
6
+ RELAY_RENDEZVOUS_NS
7
+ } from '../constants.js'
8
+ import { namespaceToCid } from '../utils.js'
9
+ import type { ComponentLogger, Logger, ContentRouting, Startable } from '@libp2p/interface'
10
+
11
+ export interface AdvertServiceInit {
12
+ /**
13
+ * How long to wait after startup to begin advertising the service
14
+ * - if some configured content routers take a while to warm up (for
15
+ * example, the DHT needs some peers to be able to publish) this
16
+ * value should be high enough that they will have warmed up
17
+ */
18
+ bootDelay?: number
19
+ }
20
+
21
+ export interface AdvertServiceComponents {
22
+ contentRouting: ContentRouting
23
+ logger: ComponentLogger
24
+ }
25
+
26
+ export interface AdvertServiceEvents {
27
+ 'advert:success': CustomEvent<unknown>
28
+ 'advert:error': CustomEvent<Error>
29
+ }
30
+
31
+ export class AdvertService extends TypedEventEmitter<AdvertServiceEvents> implements Startable {
32
+ private readonly contentRouting: ContentRouting
33
+ private timeout?: any
34
+ private started: boolean
35
+ private readonly bootDelay: number
36
+ private readonly log: Logger
37
+
38
+ /**
39
+ * Creates an instance of Relay
40
+ */
41
+ constructor (components: AdvertServiceComponents, init?: AdvertServiceInit) {
42
+ super()
43
+
44
+ this.log = components.logger.forComponent('libp2p:circuit-relay:advert-service')
45
+ this.contentRouting = components.contentRouting
46
+ this.bootDelay = init?.bootDelay ?? DEFAULT_ADVERT_BOOT_DELAY
47
+ this.started = false
48
+ }
49
+
50
+ isStarted (): boolean {
51
+ return this.started
52
+ }
53
+
54
+ /**
55
+ * Start Relay service
56
+ */
57
+ start (): void {
58
+ if (this.started) {
59
+ return
60
+ }
61
+
62
+ // Advertise service if HOP enabled and advertising enabled
63
+ this.timeout = setTimeout(() => {
64
+ this._advertiseService().catch(err => {
65
+ this.log.error('could not advertise service', err)
66
+ })
67
+ }, this.bootDelay)
68
+
69
+ this.started = true
70
+ }
71
+
72
+ /**
73
+ * Stop Relay service
74
+ */
75
+ stop (): void {
76
+ try {
77
+ clearTimeout(this.timeout)
78
+ } catch (err) { }
79
+
80
+ this.started = false
81
+ }
82
+
83
+ /**
84
+ * Advertise hop relay service in the network.
85
+ */
86
+ async _advertiseService (): Promise<void> {
87
+ await pRetry(async () => {
88
+ try {
89
+ const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
90
+ await this.contentRouting.provide(cid)
91
+
92
+ this.safeDispatchEvent('advert:success', { detail: undefined })
93
+ } catch (err: any) {
94
+ this.safeDispatchEvent('advert:error', { detail: err })
95
+
96
+ if (err.code === ERR_NO_ROUTERS_AVAILABLE) {
97
+ this.log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err)
98
+ this.stop()
99
+ return
100
+ }
101
+
102
+ this.log.error('could not advertise service', err)
103
+ throw err
104
+ }
105
+ })
106
+ }
107
+ }
@@ -14,6 +14,7 @@ import {
14
14
  } from '../constants.js'
15
15
  import { HopMessage, type Reservation, Status, StopMessage } from '../pb/index.js'
16
16
  import { createLimitedRelay } from '../utils.js'
17
+ import { AdvertService, type AdvertServiceComponents, type AdvertServiceInit } from './advert-service.js'
17
18
  import { ReservationStore, type ReservationStoreInit } from './reservation-store.js'
18
19
  import { ReservationVoucherRecord } from './reservation-voucher.js'
19
20
  import type { CircuitRelayService, RelayReservation } from '../index.js'
@@ -30,6 +31,12 @@ export interface CircuitRelayServerInit {
30
31
  */
31
32
  hopTimeout?: number
32
33
 
34
+ /**
35
+ * If true, advertise this service via libp2p content routing to allow
36
+ * peers to locate us on the network (default: false)
37
+ */
38
+ advertise?: boolean | AdvertServiceInit
39
+
33
40
  /**
34
41
  * Configuration of reservations
35
42
  */
@@ -63,7 +70,7 @@ export interface StopOptions {
63
70
  request: StopMessage
64
71
  }
65
72
 
66
- export interface CircuitRelayServerComponents {
73
+ export interface CircuitRelayServerComponents extends AdvertServiceComponents {
67
74
  registrar: Registrar
68
75
  peerStore: PeerStore
69
76
  addressManager: AddressManager
@@ -91,6 +98,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
91
98
  private readonly connectionManager: ConnectionManager
92
99
  private readonly connectionGater: ConnectionGater
93
100
  private readonly reservationStore: ReservationStore
101
+ private readonly advertService: AdvertService | undefined
94
102
  private started: boolean
95
103
  private readonly hopTimeout: number
96
104
  private readonly shutdownController: AbortController
@@ -114,13 +122,24 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
114
122
  this.connectionGater = components.connectionGater
115
123
  this.started = false
116
124
  this.hopTimeout = init?.hopTimeout ?? DEFAULT_HOP_TIMEOUT
125
+ this.shutdownController = new AbortController()
117
126
  this.maxInboundHopStreams = init.maxInboundHopStreams
118
127
  this.maxOutboundHopStreams = init.maxOutboundHopStreams
119
128
  this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
120
- this.reservationStore = new ReservationStore(init.reservations)
121
129
 
122
- this.shutdownController = new AbortController()
123
130
  setMaxListeners(Infinity, this.shutdownController.signal)
131
+
132
+ if (init.advertise != null && init.advertise !== false) {
133
+ this.advertService = new AdvertService(components, init.advertise === true ? undefined : init.advertise)
134
+ this.advertService.addEventListener('advert:success', () => {
135
+ this.safeDispatchEvent('relay:advert:success', {})
136
+ })
137
+ this.advertService.addEventListener('advert:error', (evt) => {
138
+ this.safeDispatchEvent('relay:advert:error', { detail: evt.detail })
139
+ })
140
+ }
141
+
142
+ this.reservationStore = new ReservationStore(init.reservations)
124
143
  }
125
144
 
126
145
  isStarted (): boolean {
@@ -135,6 +154,9 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
135
154
  return
136
155
  }
137
156
 
157
+ // Advertise service if HOP enabled and advertising enabled
158
+ this.advertService?.start()
159
+
138
160
  await this.registrar.handle(RELAY_V2_HOP_CODEC, (data) => {
139
161
  void this.onHop(data).catch(err => {
140
162
  this.log.error(err)
@@ -154,6 +176,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
154
176
  * Stop Relay service
155
177
  */
156
178
  async stop (): Promise<void> {
179
+ this.advertService?.stop()
157
180
  this.reservationStore.stop()
158
181
  this.shutdownController.abort()
159
182
  await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
@@ -336,7 +359,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
336
359
  return
337
360
  }
338
361
 
339
- const limit = this.reservationStore.get(dstPeer)?.limit
340
362
  const destinationConnection = connections[0]
341
363
 
342
364
  const destinationStream = await this.stopHop({
@@ -346,8 +368,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
346
368
  peer: {
347
369
  id: connection.remotePeer.toBytes(),
348
370
  addrs: []
349
- },
350
- limit
371
+ }
351
372
  }
352
373
  })
353
374
 
@@ -357,14 +378,11 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
357
378
  return
358
379
  }
359
380
 
360
- await hopstr.write({
361
- type: HopMessage.Type.STATUS,
362
- status: Status.OK,
363
- limit
364
- })
381
+ await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.OK })
365
382
  const sourceStream = stream.unwrap()
366
383
 
367
384
  this.log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer)
385
+ const limit = this.reservationStore.get(dstPeer)?.limit
368
386
  // Short circuit the two streams to create the relayed connection
369
387
  createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit, {
370
388
  log: this.log