@libp2p/circuit-relay-v2 1.0.24 → 1.0.25-44791342

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 (47) hide show
  1. package/dist/index.min.js +5 -5
  2. package/dist/src/constants.d.ts +13 -12
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +13 -12
  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 +94 -52
  9. package/dist/src/pb/index.js.map +1 -1
  10. package/dist/src/server/index.d.ts +1 -7
  11. package/dist/src/server/index.d.ts.map +1 -1
  12. package/dist/src/server/index.js +10 -19
  13. package/dist/src/server/index.js.map +1 -1
  14. package/dist/src/server/reservation-store.d.ts +2 -3
  15. package/dist/src/server/reservation-store.d.ts.map +1 -1
  16. package/dist/src/server/reservation-store.js.map +1 -1
  17. package/dist/src/transport/discovery.d.ts +15 -10
  18. package/dist/src/transport/discovery.d.ts.map +1 -1
  19. package/dist/src/transport/discovery.js +103 -51
  20. package/dist/src/transport/discovery.js.map +1 -1
  21. package/dist/src/transport/index.d.ts +27 -18
  22. package/dist/src/transport/index.d.ts.map +1 -1
  23. package/dist/src/transport/index.js +0 -3
  24. package/dist/src/transport/index.js.map +1 -1
  25. package/dist/src/transport/reservation-store.d.ts +7 -3
  26. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  27. package/dist/src/transport/reservation-store.js +37 -13
  28. package/dist/src/transport/reservation-store.js.map +1 -1
  29. package/dist/src/transport/transport.d.ts +0 -1
  30. package/dist/src/transport/transport.d.ts.map +1 -1
  31. package/dist/src/transport/transport.js +20 -19
  32. package/dist/src/transport/transport.js.map +1 -1
  33. package/package.json +12 -13
  34. package/src/constants.ts +16 -15
  35. package/src/pb/index.ts +96 -53
  36. package/src/server/index.ts +11 -29
  37. package/src/server/reservation-store.ts +2 -4
  38. package/src/transport/discovery.ts +121 -56
  39. package/src/transport/index.ts +28 -18
  40. package/src/transport/reservation-store.ts +45 -13
  41. package/src/transport/transport.ts +21 -21
  42. package/dist/src/server/advert-service.d.ts +0 -44
  43. package/dist/src/server/advert-service.d.ts.map +0 -1
  44. package/dist/src/server/advert-service.js +0 -72
  45. package/dist/src/server/advert-service.js.map +0 -1
  46. package/dist/typedoc-urls.json +0 -12
  47. package/src/server/advert-service.ts +0 -107
@@ -1,24 +1,28 @@
1
- import { TypedEventEmitter } from '@libp2p/interface'
1
+ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
+ import { PeerQueue } from '@libp2p/utils/peer-queue'
3
+ import { anySignal } from 'any-signal'
4
+ import { raceSignal } from 'race-signal'
2
5
  import {
3
- RELAY_RENDEZVOUS_NS,
4
6
  RELAY_V2_HOP_CODEC
5
7
  } from '../constants.js'
6
- import { namespaceToCid } from '../utils.js'
7
- import type { ComponentLogger, Logger, ContentRouting, PeerId, PeerStore, Startable } from '@libp2p/interface'
8
- import type { ConnectionManager, Registrar, TransportManager } from '@libp2p/interface-internal'
8
+ import type { ComponentLogger, Logger, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
9
+ import type { ConnectionManager, RandomWalk, Registrar, TransportManager } from '@libp2p/interface-internal'
9
10
 
10
11
  export interface RelayDiscoveryEvents {
11
12
  'relay:discover': CustomEvent<PeerId>
12
13
  }
13
14
 
14
15
  export interface RelayDiscoveryComponents {
15
- peerId: PeerId
16
16
  peerStore: PeerStore
17
17
  connectionManager: ConnectionManager
18
18
  transportManager: TransportManager
19
- contentRouting: ContentRouting
20
19
  registrar: Registrar
21
20
  logger: ComponentLogger
21
+ randomWalk: RandomWalk
22
+ }
23
+
24
+ export interface RelayDiscoveryInit {
25
+ filter?: TopologyFilter
22
26
  }
23
27
 
24
28
  /**
@@ -26,23 +30,30 @@ export interface RelayDiscoveryComponents {
26
30
  * peers that support the circuit v2 HOP protocol.
27
31
  */
28
32
  export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> implements Startable {
29
- private readonly peerId: PeerId
30
33
  private readonly peerStore: PeerStore
31
- private readonly contentRouting: ContentRouting
32
34
  private readonly registrar: Registrar
35
+ private readonly connectionManager: ConnectionManager
36
+ private readonly randomWalk: RandomWalk
33
37
  private started: boolean
38
+ private running: boolean
34
39
  private topologyId?: string
35
40
  private readonly log: Logger
41
+ private discoveryController: AbortController
42
+ private readonly filter?: TopologyFilter
36
43
 
37
- constructor (components: RelayDiscoveryComponents) {
44
+ constructor (components: RelayDiscoveryComponents, init: RelayDiscoveryInit = {}) {
38
45
  super()
39
46
 
40
47
  this.log = components.logger.forComponent('libp2p:circuit-relay:discover-relays')
41
48
  this.started = false
42
- this.peerId = components.peerId
49
+ this.running = false
43
50
  this.peerStore = components.peerStore
44
- this.contentRouting = components.contentRouting
45
51
  this.registrar = components.registrar
52
+ this.connectionManager = components.connectionManager
53
+ this.randomWalk = components.randomWalk
54
+ this.filter = init.filter
55
+ this.discoveryController = new AbortController()
56
+ setMaxListeners(Infinity, this.discoveryController.signal)
46
57
  }
47
58
 
48
59
  isStarted (): boolean {
@@ -53,8 +64,9 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
53
64
  // register a topology listener for when new peers are encountered
54
65
  // that support the hop protocol
55
66
  this.topologyId = await this.registrar.register(RELAY_V2_HOP_CODEC, {
56
- notifyOnTransient: true,
67
+ filter: this.filter,
57
68
  onConnect: (peerId) => {
69
+ this.log('discovered relay %p', peerId)
58
70
  this.safeDispatchEvent('relay:discover', { detail: peerId })
59
71
  }
60
72
  })
@@ -62,18 +74,12 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
62
74
  this.started = true
63
75
  }
64
76
 
65
- afterStart (): void {
66
- void this.discover()
67
- .catch(err => {
68
- this.log.error('error discovering relays', err)
69
- })
70
- }
71
-
72
77
  stop (): void {
73
78
  if (this.topologyId != null) {
74
79
  this.registrar.unregister(this.topologyId)
75
80
  }
76
81
 
82
+ this.discoveryController?.abort()
77
83
  this.started = false
78
84
  }
79
85
 
@@ -81,54 +87,113 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
81
87
  * Try to listen on available hop relay connections.
82
88
  * The following order will happen while we do not have enough relays:
83
89
  *
84
- * 1. Check the metadata store for known relays, try to listen on the ones we are already connected
90
+ * 1. Check the metadata store for known relays, try to listen on the ones we are already connected to
85
91
  * 2. Dial and try to listen on the peers we know that support hop but are not connected
86
92
  * 3. Search the network
87
93
  */
88
- async discover (): Promise<void> {
89
- this.log('searching peer store for relays')
90
- const peers = (await this.peerStore.all({
91
- filters: [
92
- // filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on
93
- (peer) => {
94
- return peer.protocols.includes(RELAY_V2_HOP_CODEC)
95
- }
96
- ],
97
- orders: [
98
- () => Math.random() < 0.5 ? 1 : -1
99
- ]
100
- }))
101
-
102
- for (const peer of peers) {
103
- this.log('found relay peer %p in content peer store', peer.id)
104
- this.safeDispatchEvent('relay:discover', { detail: peer.id })
94
+ startDiscovery (): void {
95
+ if (this.running) {
96
+ return
105
97
  }
106
98
 
107
- this.log('found %d relay peers in peer store', peers.length)
99
+ this.log('start discovery')
100
+ this.running = true
101
+ this.discoveryController = new AbortController()
102
+ setMaxListeners(Infinity, this.discoveryController.signal)
103
+
104
+ Promise.resolve()
105
+ .then(async () => {
106
+ this.log('searching peer store for relays')
107
+
108
+ const peers = (await this.peerStore.all({
109
+ filters: [
110
+ // filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on
111
+ (peer) => {
112
+ return peer.protocols.includes(RELAY_V2_HOP_CODEC)
113
+ }
114
+ ],
115
+ orders: [
116
+ () => Math.random() < 0.5 ? 1 : -1
117
+ ]
118
+ }))
119
+
120
+ for (const peer of peers) {
121
+ this.log.trace('found relay peer %p in peer store', peer.id)
122
+ this.safeDispatchEvent('relay:discover', { detail: peer.id })
123
+ }
124
+
125
+ this.log('found %d relay peers in peer store', peers.length)
126
+
127
+ // perform random walk and dial peers - after identify has run, the network
128
+ // topology will be notified of new relays
129
+ const queue = new PeerQueue({
130
+ concurrency: 5
131
+ })
132
+
133
+ this.log('start random walk')
134
+ for await (const peer of this.randomWalk.walk({ signal: this.discoveryController.signal })) {
135
+ this.log.trace('found random peer %p', peer.id)
136
+
137
+ if (queue.has(peer.id)) {
138
+ this.log.trace('random peer %p was already in queue', peer.id)
139
+
140
+ // skip peers already in the queue
141
+ continue
142
+ }
108
143
 
109
- try {
110
- this.log('searching content routing for relays')
111
- const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
144
+ if (this.connectionManager.getConnections(peer.id)?.length > 0) {
145
+ this.log.trace('random peer %p was already connected', peer.id)
112
146
 
113
- let found = 0
147
+ // skip peers we are already connected to
148
+ continue
149
+ }
114
150
 
115
- for await (const provider of this.contentRouting.findProviders(cid)) {
116
- if (provider.multiaddrs.length > 0 && !provider.id.equals(this.peerId)) {
117
- const peerId = provider.id
151
+ if (!(await this.connectionManager.isDialable(peer.multiaddrs))) {
152
+ this.log.trace('random peer %p was not dialable', peer.id, peer.multiaddrs.map(ma => ma.toString()))
118
153
 
119
- found++
120
- await this.peerStore.merge(peerId, {
121
- multiaddrs: provider.multiaddrs
154
+ // skip peers we can't dial
155
+ continue
156
+ }
157
+
158
+ this.log.trace('wait for space in queue for %p', peer.id)
159
+
160
+ // pause the random walk until there is space in the queue
161
+ await raceSignal(queue.onSizeLessThan(10), this.discoveryController.signal)
162
+
163
+ this.log('adding random peer %p to dial queue (length: %d)', peer.id, queue.size)
164
+
165
+ // dial the peer - this will cause identify to run and our topology to
166
+ // be notified and we'll attempt to create reservations
167
+ queue.add(async () => {
168
+ const signal = anySignal([this.discoveryController.signal, AbortSignal.timeout(5000)])
169
+ setMaxListeners(Infinity, signal)
170
+
171
+ try {
172
+ await this.connectionManager.openConnection(peer.id, { signal })
173
+ } finally {
174
+ signal.clear()
175
+ }
176
+ }, {
177
+ peerId: peer.id,
178
+ signal: this.discoveryController.signal
122
179
  })
180
+ .catch(err => {
181
+ this.log.error('error opening connection to random peer %p', peer.id, err)
182
+ })
183
+ }
123
184
 
124
- this.log('found relay peer %p in content routing', peerId)
125
- this.safeDispatchEvent('relay:discover', { detail: peerId })
185
+ await queue.onIdle()
186
+ })
187
+ .catch(err => {
188
+ if (!this.discoveryController.signal.aborted) {
189
+ this.log.error('failed when finding relays on the network', err)
126
190
  }
127
- }
191
+ })
192
+ }
128
193
 
129
- this.log('found %d relay peers in content routing', found)
130
- } catch (err: any) {
131
- this.log.error('failed when finding relays on the network', err)
132
- }
194
+ stopDiscovery (): void {
195
+ this.log('stop discovery')
196
+ this.running = false
197
+ this.discoveryController?.abort()
133
198
  }
134
199
  }
@@ -1,20 +1,16 @@
1
- import { type Transport, type Upgrader, type Libp2pEvents, type ComponentLogger, type ConnectionGater, type ContentRouting, type TypedEventTarget, type PeerId, type PeerStore } from '@libp2p/interface'
2
- import { type RelayDiscoveryComponents } from './discovery.js'
3
- import { type RelayStoreInit } from './reservation-store.js'
4
1
  import { CircuitRelayTransport } from './transport.js'
5
- import type { AddressManager, ConnectionManager, Registrar } from '@libp2p/interface-internal'
2
+ import type { RelayDiscoveryComponents } from './discovery.js'
3
+ import type { RelayStoreInit } from './reservation-store.js'
4
+ import type { Transport, Upgrader, Libp2pEvents, ConnectionGater, TypedEventTarget, PeerId, TopologyFilter } from '@libp2p/interface'
5
+ import type { AddressManager, Registrar } from '@libp2p/interface-internal'
6
6
 
7
7
  export interface CircuitRelayTransportComponents extends RelayDiscoveryComponents {
8
8
  peerId: PeerId
9
- peerStore: PeerStore
10
9
  registrar: Registrar
11
- connectionManager: ConnectionManager
12
10
  upgrader: Upgrader
13
11
  addressManager: AddressManager
14
- contentRouting: ContentRouting
15
12
  connectionGater: ConnectionGater
16
13
  events: TypedEventTarget<Libp2pEvents>
17
- logger: ComponentLogger
18
14
  }
19
15
 
20
16
  /**
@@ -22,34 +18,48 @@ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponent
22
18
  */
23
19
  export interface CircuitRelayTransportInit extends RelayStoreInit {
24
20
  /**
25
- * The number of peers running diable relays to search for and
26
- * connect to. (default: 0)
21
+ * The number of peers running diable relays to search for and connect to
22
+ *
23
+ * @default 0
27
24
  */
28
25
  discoverRelays?: number
29
26
 
27
+ /**
28
+ * An optional filter used to prevent duplicate attempts to reserve relay
29
+ * slots on the same peer
30
+ */
31
+ discoveryFilter?: TopologyFilter
32
+
30
33
  /**
31
34
  * The maximum number of simultaneous STOP inbound streams that can be open at
32
- * once - each inbound relayed connection uses a STOP stream (default: 300)
35
+ * once - each inbound relayed connection uses a STOP stream
36
+ *
37
+ * @default 300
33
38
  */
34
39
  maxInboundStopStreams?: number
35
40
 
36
41
  /**
37
- * The maximum number of simultaneous STOP outbound streams that can be open at
38
- * once. If this transport is used along with the relay server these settings
39
- * should be set to the same value (default: 300)
42
+ * The maximum number of simultaneous STOP outbound streams that can be open
43
+ * at once. If this transport is used along with the relay server these
44
+ * settings should be set to the same value
45
+ *
46
+ * @default 300
40
47
  */
41
48
  maxOutboundStopStreams?: number
42
49
 
43
50
  /**
44
- * Incoming STOP requests (e.g. when a remote peer wants to dial us via a relay)
45
- * must finish the initial protocol negotiation within this timeout in ms
46
- * (default: 30000)
51
+ * Incoming STOP requests (e.g. when a remote peer wants to dial us via a
52
+ * relay) must finish the initial protocol negotiation within this timeout in
53
+ * ms
54
+ *
55
+ * @default 30000
47
56
  */
48
57
  stopTimeout?: number
49
58
 
50
59
  /**
51
60
  * When creating a reservation it must complete within this number of ms
52
- * (default: 10000)
61
+ *
62
+ * @default 10000
53
63
  */
54
64
  reservationCompletionTimeout?: number
55
65
  }
@@ -1,15 +1,17 @@
1
- import { TypedEventEmitter } from '@libp2p/interface'
1
+ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
2
  import { PeerMap } from '@libp2p/peer-collections'
3
+ import { createBloomFilter } from '@libp2p/utils/filters'
3
4
  import { PeerQueue } from '@libp2p/utils/peer-queue'
4
5
  import { multiaddr } from '@multiformats/multiaddr'
5
6
  import { pbStream } from 'it-protobuf-stream'
6
7
  import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
7
- import { DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
8
+ import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
8
9
  import { HopMessage, Status } from '../pb/index.js'
9
10
  import { getExpirationMilliseconds } from '../utils.js'
10
11
  import type { Reservation } from '../pb/index.js'
11
12
  import type { TypedEventTarget, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, PeerId, PeerStore, Startable, Metrics } from '@libp2p/interface'
12
13
  import type { ConnectionManager, TransportManager } from '@libp2p/interface-internal'
14
+ import type { Filter } from '@libp2p/utils/filters'
13
15
 
14
16
  // allow refreshing a relay reservation if it will expire in the next 10 minutes
15
17
  const REFRESH_WINDOW = (60 * 1000) * 10
@@ -69,6 +71,7 @@ interface RelayEntry {
69
71
  export interface ReservationStoreEvents {
70
72
  'relay:not-enough-relays': CustomEvent
71
73
  'relay:removed': CustomEvent<PeerId>
74
+ 'relay:created-reservation': CustomEvent<PeerId>
72
75
  }
73
76
 
74
77
  export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents> implements Startable {
@@ -84,6 +87,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
84
87
  private readonly reservationCompletionTimeout: number
85
88
  private started: boolean
86
89
  private readonly log: Logger
90
+ private readonly relayFilter: Filter
87
91
 
88
92
  constructor (components: RelayStoreComponents, init?: RelayStoreInit) {
89
93
  super()
@@ -96,9 +100,10 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
96
100
  this.events = components.events
97
101
  this.reservations = new PeerMap()
98
102
  this.maxDiscoveredRelays = init?.discoverRelays ?? 0
99
- this.maxReservationQueueLength = init?.maxReservationQueueLength ?? 100
100
- this.reservationCompletionTimeout = init?.reservationCompletionTimeout ?? 10000
103
+ this.maxReservationQueueLength = init?.maxReservationQueueLength ?? DEFAULT_MAX_RESERVATION_QUEUE_LENGTH
104
+ this.reservationCompletionTimeout = init?.reservationCompletionTimeout ?? DEFAULT_RESERVATION_COMPLETION_TIMEOUT
101
105
  this.started = false
106
+ this.relayFilter = createBloomFilter(100)
102
107
 
103
108
  // ensure we don't listen on multiple relays simultaneously
104
109
  this.reserveQueue = new PeerQueue({
@@ -123,6 +128,13 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
123
128
  this.started = true
124
129
  }
125
130
 
131
+ afterStart (): void {
132
+ if (this.reservations.size < this.maxDiscoveredRelays) {
133
+ this.log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays)
134
+ this.safeDispatchEvent('relay:not-enough-relays', {})
135
+ }
136
+ }
137
+
126
138
  stop (): void {
127
139
  this.reserveQueue.clear()
128
140
  this.reservations.forEach(({ timeout }) => {
@@ -134,9 +146,9 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
134
146
 
135
147
  /**
136
148
  * If the number of current relays is beneath the configured `maxReservations`
137
- * value, and the passed peer id is not our own, and we have a non-relayed connection
138
- * to the remote, and the remote peer speaks the hop protocol, try to reserve a slot
139
- * on the remote peer
149
+ * value, and the passed peer id is not our own, and we have a non-relayed
150
+ * connection to the remote, and the remote peer speaks the hop protocol, try
151
+ * to reserve a slot on the remote peer
140
152
  */
141
153
  async addRelay (peerId: PeerId, type: RelayType): Promise<void> {
142
154
  if (this.peerId.equals(peerId)) {
@@ -145,18 +157,25 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
145
157
  }
146
158
 
147
159
  if (this.reserveQueue.size > this.maxReservationQueueLength) {
148
- this.log('not adding relay as the queue is full')
160
+ this.log('not adding potential relay peer %p as the queue is full', peerId)
149
161
  return
150
162
  }
151
163
 
152
164
  if (this.reserveQueue.has(peerId)) {
153
- this.log('relay peer is already in the reservation queue')
165
+ this.log('potential relay peer %p is already in the reservation queue', peerId)
166
+ return
167
+ }
168
+
169
+ if (this.relayFilter.has(peerId.toBytes())) {
170
+ this.log('potential relay peer %p has failed previously, not trying again', peerId, new Error('where').stack)
154
171
  return
155
172
  }
156
173
 
157
- this.log('add relay %p', peerId)
174
+ this.log('try to reserve relay slot with %p', peerId)
158
175
 
159
176
  await this.reserveQueue.add(async () => {
177
+ const start = Date.now()
178
+
160
179
  try {
161
180
  // allow refresh of an existing reservation if it is about to expire
162
181
  const existingReservation = this.reservations.get(peerId)
@@ -183,6 +202,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
183
202
  }
184
203
 
185
204
  const signal = AbortSignal.timeout(this.reservationCompletionTimeout)
205
+ setMaxListeners(Infinity, signal)
186
206
 
187
207
  const connection = await this.connectionManager.openConnection(peerId, {
188
208
  signal
@@ -230,8 +250,12 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
230
250
 
231
251
  // listen on multiaddr that only the circuit transport is listening for
232
252
  await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit`)])
253
+
254
+ this.safeDispatchEvent('relay:created-reservation', {
255
+ detail: peerId
256
+ })
233
257
  } catch (err) {
234
- this.log.error('could not reserve slot on %p', peerId, err)
258
+ this.log.error('could not reserve slot on %p after %dms', peerId, Date.now() - start, err)
235
259
 
236
260
  // cancel the renewal timeout if it's been set
237
261
  const reservation = this.reservations.get(peerId)
@@ -242,6 +266,9 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
242
266
 
243
267
  // if listening failed, remove the reservation
244
268
  this.reservations.delete(peerId)
269
+
270
+ // don't try this peer again
271
+ this.relayFilter.add(peerId.toBytes())
245
272
  }
246
273
  }, {
247
274
  peerId
@@ -256,6 +283,10 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
256
283
  return this.reservations.get(peerId)?.reservation
257
284
  }
258
285
 
286
+ reservationCount (): number {
287
+ return this.reservations.size
288
+ }
289
+
259
290
  async #createReservation (connection: Connection, options: AbortOptions): Promise<Reservation> {
260
291
  options.signal?.throwIfAborted()
261
292
 
@@ -270,11 +301,12 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
270
301
  try {
271
302
  response = await hopstr.read(options)
272
303
  } catch (err: any) {
273
- this.log.error('error parsing reserve message response from %p because', connection.remotePeer, err)
274
304
  stream.abort(err)
275
305
  throw err
276
306
  } finally {
277
- await stream.close()
307
+ if (stream.status !== 'closed') {
308
+ await stream.close(options)
309
+ }
278
310
  }
279
311
 
280
312
  if (response.status === Status.OK && (response.reservation != null)) {
@@ -1,11 +1,12 @@
1
- import { CodeError } from '@libp2p/interface'
1
+ import { CodeError, start, stop } from '@libp2p/interface'
2
2
  import { transportSymbol, type Transport, type CreateListenerOptions, type Listener, type Upgrader, type AbortOptions, type ComponentLogger, type Logger, type Connection, type Stream, type ConnectionGater, type PeerId, type PeerStore } from '@libp2p/interface'
3
+ import { peerFilter } from '@libp2p/peer-collections'
3
4
  import { peerIdFromBytes, peerIdFromString } from '@libp2p/peer-id'
4
5
  import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
5
6
  import * as mafmt from '@multiformats/mafmt'
6
7
  import { multiaddr } from '@multiformats/multiaddr'
7
8
  import { pbStream } from 'it-protobuf-stream'
8
- import { CIRCUIT_PROTO_CODE, ERR_HOP_REQUEST_FAILED, ERR_RELAYED_DIAL, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
9
+ import { CIRCUIT_PROTO_CODE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE, DEFAULT_DISCOVERY_FILTER_SIZE, ERR_HOP_REQUEST_FAILED, ERR_RELAYED_DIAL, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
9
10
  import { StopMessage, HopMessage, Status } from '../pb/index.js'
10
11
  import { RelayDiscovery } from './discovery.js'
11
12
  import { createListener } from './listener.js'
@@ -77,8 +78,12 @@ export class CircuitRelayTransport implements Transport {
77
78
  this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
78
79
  this.stopTimeout = init.stopTimeout ?? defaults.stopTimeout
79
80
 
80
- if (init.discoverRelays != null && init.discoverRelays > 0) {
81
- this.discovery = new RelayDiscovery(components)
81
+ const discoverRelays = init.discoverRelays ?? 0
82
+
83
+ if (discoverRelays > 0) {
84
+ this.discovery = new RelayDiscovery(components, {
85
+ filter: init.discoveryFilter ?? peerFilter(DEFAULT_DISCOVERY_FILTER_SIZE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE)
86
+ })
82
87
  this.discovery.addEventListener('relay:discover', (evt) => {
83
88
  this.reservationStore.addRelay(evt.detail, 'discovered')
84
89
  .catch(err => {
@@ -89,10 +94,12 @@ export class CircuitRelayTransport implements Transport {
89
94
 
90
95
  this.reservationStore = new ReservationStore(components, init)
91
96
  this.reservationStore.addEventListener('relay:not-enough-relays', () => {
92
- this.discovery?.discover()
93
- .catch(err => {
94
- this.log.error('could not discover relays', err)
95
- })
97
+ this.discovery?.startDiscovery()
98
+ })
99
+ this.reservationStore.addEventListener('relay:created-reservation', () => {
100
+ if (this.reservationStore.reservationCount() >= discoverRelays) {
101
+ this.discovery?.stopDiscovery()
102
+ }
96
103
  })
97
104
 
98
105
  this.started = false
@@ -103,8 +110,6 @@ export class CircuitRelayTransport implements Transport {
103
110
  }
104
111
 
105
112
  async start (): Promise<void> {
106
- this.reservationStore.start()
107
-
108
113
  await this.registrar.handle(RELAY_V2_STOP_CODEC, (data) => {
109
114
  void this.onStop(data).catch(err => {
110
115
  this.log.error('error while handling STOP protocol', err)
@@ -116,18 +121,13 @@ export class CircuitRelayTransport implements Transport {
116
121
  runOnTransientConnection: true
117
122
  })
118
123
 
119
- await this.discovery?.start()
124
+ await start(this.discovery, this.reservationStore)
120
125
 
121
126
  this.started = true
122
127
  }
123
128
 
124
- afterStart (): void {
125
- this.discovery?.afterStart()
126
- }
127
-
128
129
  async stop (): Promise<void> {
129
- this.discovery?.stop()
130
- this.reservationStore.stop()
130
+ await stop(this.discovery, this.reservationStore)
131
131
  await this.registrar.unhandle(RELAY_V2_STOP_CODEC)
132
132
 
133
133
  this.started = false
@@ -231,9 +231,9 @@ export class CircuitRelayTransport implements Transport {
231
231
  logger: this.logger
232
232
  })
233
233
 
234
- this.log('new outbound transient connection %a', maConn.remoteAddr)
234
+ this.log('new outbound relayed connection %a', maConn.remoteAddr)
235
235
  return await this.upgrader.upgradeOutbound(maConn, {
236
- transient: true
236
+ transient: status.limit != null
237
237
  })
238
238
  } catch (err: any) {
239
239
  this.log.error(`Circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err)
@@ -346,9 +346,9 @@ export class CircuitRelayTransport implements Transport {
346
346
  logger: this.logger
347
347
  })
348
348
 
349
- this.log('new inbound transient connection %a', maConn.remoteAddr)
349
+ this.log('new inbound relayed connection %a', maConn.remoteAddr)
350
350
  await this.upgrader.upgradeInbound(maConn, {
351
- transient: true
351
+ transient: request.limit != null
352
352
  })
353
353
  this.log('%s connection %a upgraded', 'inbound', maConn.remoteAddr)
354
354
  }
@@ -1,44 +0,0 @@
1
- import { TypedEventEmitter } from '@libp2p/interface';
2
- import type { ComponentLogger, ContentRouting, Startable } from '@libp2p/interface';
3
- export interface AdvertServiceInit {
4
- /**
5
- * How long to wait after startup to begin advertising the service
6
- * - if some configured content routers take a while to warm up (for
7
- * example, the DHT needs some peers to be able to publish) this
8
- * value should be high enough that they will have warmed up
9
- */
10
- bootDelay?: number;
11
- }
12
- export interface AdvertServiceComponents {
13
- contentRouting: ContentRouting;
14
- logger: ComponentLogger;
15
- }
16
- export interface AdvertServiceEvents {
17
- 'advert:success': CustomEvent<unknown>;
18
- 'advert:error': CustomEvent<Error>;
19
- }
20
- export declare class AdvertService extends TypedEventEmitter<AdvertServiceEvents> implements Startable {
21
- private readonly contentRouting;
22
- private timeout?;
23
- private started;
24
- private readonly bootDelay;
25
- private readonly log;
26
- /**
27
- * Creates an instance of Relay
28
- */
29
- constructor(components: AdvertServiceComponents, init?: AdvertServiceInit);
30
- isStarted(): boolean;
31
- /**
32
- * Start Relay service
33
- */
34
- start(): void;
35
- /**
36
- * Stop Relay service
37
- */
38
- stop(): void;
39
- /**
40
- * Advertise hop relay service in the network.
41
- */
42
- _advertiseService(): Promise<void>;
43
- }
44
- //# sourceMappingURL=advert-service.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"advert-service.d.ts","sourceRoot":"","sources":["../../../src/server/advert-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAQrD,OAAO,KAAK,EAAE,eAAe,EAAU,cAAc,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE3F,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,cAAc,CAAA;IAC9B,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IACtC,cAAc,EAAE,WAAW,CAAC,KAAK,CAAC,CAAA;CACnC;AAED,qBAAa,aAAc,SAAQ,iBAAiB,CAAC,mBAAmB,CAAE,YAAW,SAAS;IAC5F,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAK;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAE5B;;OAEG;gBACU,UAAU,EAAE,uBAAuB,EAAE,IAAI,CAAC,EAAE,iBAAiB;IAS1E,SAAS,IAAK,OAAO;IAIrB;;OAEG;IACH,KAAK,IAAK,IAAI;IAed;;OAEG;IACH,IAAI,IAAK,IAAI;IAQb;;OAEG;IACG,iBAAiB,IAAK,OAAO,CAAC,IAAI,CAAC;CAqB1C"}