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

Sign up to get free protection for your applications and to get access to all the features.
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"}