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

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 +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