@libp2p/circuit-relay-v2 3.2.23-cf9aab5c8 → 3.2.24-a02cb0461

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 (46) hide show
  1. package/dist/index.min.js +1 -1
  2. package/dist/index.min.js.map +4 -4
  3. package/dist/src/index.d.ts +170 -7
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +12 -2
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/server/index.d.ts +41 -45
  8. package/dist/src/server/index.d.ts.map +1 -1
  9. package/dist/src/server/index.js +26 -43
  10. package/dist/src/server/index.js.map +1 -1
  11. package/dist/src/server/reservation-store.d.ts +2 -36
  12. package/dist/src/server/reservation-store.d.ts.map +1 -1
  13. package/dist/src/server/reservation-store.js.map +1 -1
  14. package/dist/src/transport/discovery.d.ts +2 -17
  15. package/dist/src/transport/discovery.d.ts.map +1 -1
  16. package/dist/src/transport/discovery.js +2 -2
  17. package/dist/src/transport/discovery.js.map +1 -1
  18. package/dist/src/transport/index.d.ts +34 -42
  19. package/dist/src/transport/index.d.ts.map +1 -1
  20. package/dist/src/transport/index.js +296 -5
  21. package/dist/src/transport/index.js.map +1 -1
  22. package/dist/src/transport/reservation-store.d.ts +3 -37
  23. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  24. package/dist/src/transport/reservation-store.js +2 -4
  25. package/dist/src/transport/reservation-store.js.map +1 -1
  26. package/dist/src/transport/stream-to-conn.d.ts +19 -0
  27. package/dist/src/transport/stream-to-conn.d.ts.map +1 -0
  28. package/dist/src/transport/stream-to-conn.js +60 -0
  29. package/dist/src/transport/stream-to-conn.js.map +1 -0
  30. package/dist/src/utils.d.ts.map +1 -1
  31. package/dist/src/utils.js +36 -59
  32. package/dist/src/utils.js.map +1 -1
  33. package/package.json +19 -24
  34. package/src/index.ts +207 -8
  35. package/src/server/index.ts +32 -94
  36. package/src/server/reservation-store.ts +2 -42
  37. package/src/transport/discovery.ts +4 -22
  38. package/src/transport/index.ts +344 -46
  39. package/src/transport/reservation-store.ts +6 -44
  40. package/src/transport/stream-to-conn.ts +91 -0
  41. package/src/utils.ts +37 -66
  42. package/dist/src/transport/transport.d.ts +0 -54
  43. package/dist/src/transport/transport.d.ts.map +0 -1
  44. package/dist/src/transport/transport.js +0 -314
  45. package/dist/src/transport/transport.js.map +0 -1
  46. package/src/transport/transport.ts +0 -380
@@ -1,65 +1,363 @@
1
- import { CircuitRelayTransport } from './transport.js'
2
- import type { RelayDiscoveryComponents } from './discovery.js'
3
- import type { ReservationStoreInit } from './reservation-store.js'
4
- import type { Transport, Upgrader, Libp2pEvents, ConnectionGater, PeerId, TopologyFilter } from '@libp2p/interface'
5
- import type { AddressManager, Registrar } from '@libp2p/interface-internal'
6
- import type { TypedEventTarget } from 'main-event'
7
-
8
- export interface CircuitRelayTransportComponents extends RelayDiscoveryComponents {
9
- peerId: PeerId
10
- registrar: Registrar
11
- upgrader: Upgrader
12
- addressManager: AddressManager
13
- connectionGater: ConnectionGater
14
- events: TypedEventTarget<Libp2pEvents>
1
+ import { DialError, InvalidMessageError, serviceCapabilities, serviceDependencies, start, stop, transportSymbol } from '@libp2p/interface'
2
+ import { peerFilter } from '@libp2p/peer-collections'
3
+ import { peerIdFromMultihash, peerIdFromString } from '@libp2p/peer-id'
4
+ import { pbStream } from '@libp2p/utils'
5
+ import { multiaddr } from '@multiformats/multiaddr'
6
+ import { Circuit } from '@multiformats/multiaddr-matcher'
7
+ import { setMaxListeners } from 'main-event'
8
+ import * as Digest from 'multiformats/hashes/digest'
9
+ import { CustomProgressEvent } from 'progress-events'
10
+ import { CIRCUIT_PROTO_CODE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE, DEFAULT_DISCOVERY_FILTER_SIZE, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
11
+ import { StopMessage, HopMessage, Status } from '../pb/index.js'
12
+ import { CircuitListen, CircuitSearch, LimitTracker } from '../utils.js'
13
+ import { RelayDiscovery } from './discovery.js'
14
+ import { createListener } from './listener.js'
15
+ import { ReservationStore } from './reservation-store.js'
16
+ import { streamToMaConnection } from './stream-to-conn.js'
17
+ import type { CircuitRelayTransportComponents, CircuitRelayTransportInit } from '../index.ts'
18
+ import type { Transport, CreateListenerOptions, Listener, Logger, Connection, Stream, OutboundConnectionUpgradeEvents, DialTransportOptions, OpenConnectionProgressEvents } from '@libp2p/interface'
19
+ import type { Multiaddr } from '@multiformats/multiaddr'
20
+ import type { ProgressEvent } from 'progress-events'
21
+
22
+ const isValidStop = (request: StopMessage): request is Required<StopMessage> => {
23
+ if (request.peer == null) {
24
+ return false
25
+ }
26
+
27
+ try {
28
+ request.peer.addrs.forEach(multiaddr)
29
+ } catch {
30
+ return false
31
+ }
32
+
33
+ return true
15
34
  }
16
35
 
17
- /**
18
- * RelayConfig configures the circuit v2 relay transport.
19
- */
20
- export interface CircuitRelayTransportInit extends ReservationStoreInit {
36
+ const defaults = {
37
+ maxInboundStopStreams: MAX_CONNECTIONS,
38
+ maxOutboundStopStreams: MAX_CONNECTIONS,
39
+ stopTimeout: 30000
40
+ }
41
+
42
+ export type CircuitRelayDialEvents =
43
+ OutboundConnectionUpgradeEvents |
44
+ OpenConnectionProgressEvents |
45
+ ProgressEvent<'circuit-relay:open-connection'> |
46
+ ProgressEvent<'circuit-relay:reuse-connection'> |
47
+ ProgressEvent<'circuit-relay:open-hop-stream'> |
48
+ ProgressEvent<'circuit-relay:write-connect-message'> |
49
+ ProgressEvent<'circuit-relay:read-connect-response'>
50
+
51
+ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents> {
52
+ private readonly components: CircuitRelayTransportComponents
53
+ private readonly discovery?: RelayDiscovery
54
+ public readonly reservationStore: ReservationStore
55
+ private readonly maxInboundStopStreams: number
56
+ private readonly maxOutboundStopStreams?: number
57
+ private started: boolean
58
+ private readonly log: Logger
59
+ private shutdownController: AbortController
60
+
61
+ constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit = {}) {
62
+ this.components = components
63
+ this.log = components.logger.forComponent('libp2p:circuit-relay:transport')
64
+ this.maxInboundStopStreams = init.maxInboundStopStreams ?? defaults.maxInboundStopStreams
65
+ this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
66
+ this.shutdownController = new AbortController()
67
+
68
+ this.discovery = new RelayDiscovery(components, {
69
+ filter: init.discoveryFilter ?? peerFilter(DEFAULT_DISCOVERY_FILTER_SIZE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE)
70
+ })
71
+ this.discovery.addEventListener('relay:discover', (evt) => {
72
+ this.reservationStore.addRelay(evt.detail, 'discovered')
73
+ .catch(err => {
74
+ if (err.name !== 'HadEnoughRelaysError' && err.name !== 'RelayQueueFullError') {
75
+ this.log.error('could not add discovered relay %p', evt.detail, err)
76
+ }
77
+ })
78
+ })
79
+ this.reservationStore = new ReservationStore(components, init)
80
+ this.reservationStore.addEventListener('relay:not-enough-relays', () => {
81
+ this.discovery?.startDiscovery()
82
+ })
83
+ this.reservationStore.addEventListener('relay:found-enough-relays', () => {
84
+ this.discovery?.stopDiscovery()
85
+ })
86
+
87
+ this.started = false
88
+
89
+ this.onStop = this.onStop.bind(this)
90
+ }
91
+
92
+ readonly [Symbol.toStringTag] = '@libp2p/circuit-relay-v2-transport'
93
+
94
+ readonly [serviceCapabilities]: string[] = [
95
+ '@libp2p/transport',
96
+ '@libp2p/circuit-relay-v2-transport'
97
+ ]
98
+
99
+ get [serviceDependencies] (): string[] {
100
+ // we only need identify if discovery is enabled
101
+ if (this.discovery != null) {
102
+ return [
103
+ '@libp2p/identify'
104
+ ]
105
+ }
106
+
107
+ return []
108
+ }
109
+
110
+ readonly [transportSymbol] = true
111
+
112
+ isStarted (): boolean {
113
+ return this.started
114
+ }
115
+
116
+ async start (): Promise<void> {
117
+ this.shutdownController = new AbortController()
118
+ setMaxListeners(Infinity, this.shutdownController.signal)
119
+
120
+ await this.components.registrar.handle(RELAY_V2_STOP_CODEC, this.onStop, {
121
+ maxInboundStreams: this.maxInboundStopStreams,
122
+ maxOutboundStreams: this.maxOutboundStopStreams,
123
+ runOnLimitedConnection: true
124
+ })
125
+
126
+ await start(this.discovery, this.reservationStore)
127
+
128
+ this.started = true
129
+ }
130
+
131
+ async stop (): Promise<void> {
132
+ this.shutdownController.abort()
133
+ await stop(this.discovery, this.reservationStore)
134
+ await this.components.registrar.unhandle(RELAY_V2_STOP_CODEC)
135
+
136
+ this.started = false
137
+ }
138
+
21
139
  /**
22
- * An optional filter used to prevent duplicate attempts to reserve relay
23
- * slots on the same peer
140
+ * Dial a peer over a relay
24
141
  */
25
- discoveryFilter?: TopologyFilter
142
+ async dial (ma: Multiaddr, options: DialTransportOptions<CircuitRelayDialEvents>): Promise<Connection> {
143
+ if (ma.protoCodes().filter(code => code === CIRCUIT_PROTO_CODE).length !== 1) {
144
+ const errMsg = 'Invalid circuit relay address'
145
+ this.log.error(errMsg, ma)
146
+ throw new DialError(errMsg)
147
+ }
148
+
149
+ // Check the multiaddr to see if it contains a relay and a destination peer
150
+ const addrs = ma.toString().split('/p2p-circuit')
151
+ const relayAddr = multiaddr(addrs[0])
152
+ const destinationAddr = multiaddr(addrs[addrs.length - 1])
153
+ const relayId = relayAddr.getPeerId()
154
+ const destinationId = destinationAddr.getPeerId()
155
+
156
+ if (relayId == null || destinationId == null) {
157
+ const errMsg = `ircuit relay dial to ${ma.toString()} failed as address did not have both relay and destination PeerIDs`
158
+ this.log.error(`c${errMsg}`)
159
+ throw new DialError(`C${errMsg}`)
160
+ }
161
+
162
+ const relayPeer = peerIdFromString(relayId)
163
+ const destinationPeer = peerIdFromString(destinationId)
164
+
165
+ const relayConnections = this.components.connectionManager.getConnections(relayPeer)
166
+ let relayConnection = relayConnections[0]
167
+
168
+ if (relayConnection == null) {
169
+ await this.components.peerStore.merge(relayPeer, {
170
+ multiaddrs: [relayAddr]
171
+ })
172
+
173
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:open-connection'))
174
+ relayConnection = await this.components.connectionManager.openConnection(relayPeer, options)
175
+ } else {
176
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:reuse-connection'))
177
+ }
178
+
179
+ let stream: Stream | undefined
180
+
181
+ try {
182
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:open-hop-stream'))
183
+ stream = await relayConnection.newStream(RELAY_V2_HOP_CODEC, options)
184
+
185
+ const hopstr = pbStream(stream).pb(HopMessage)
186
+
187
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:write-connect-message'))
188
+ await hopstr.write({
189
+ type: HopMessage.Type.CONNECT,
190
+ peer: {
191
+ id: destinationPeer.toMultihash().bytes,
192
+ addrs: [multiaddr(destinationAddr).bytes]
193
+ }
194
+ }, options)
195
+
196
+ options.onProgress?.(new CustomProgressEvent('circuit-relay:read-connect-response'))
197
+ const status = await hopstr.read(options)
198
+
199
+ if (status.status !== Status.OK) {
200
+ throw new InvalidMessageError(`failed to connect via relay with status ${status?.status?.toString() ?? 'undefined'}`)
201
+ }
202
+
203
+ const limits = new LimitTracker(status.limit)
204
+
205
+ const maConn = streamToMaConnection({
206
+ stream: hopstr.unwrap().unwrap(),
207
+ remoteAddr: ma,
208
+ localAddr: relayAddr.encapsulate(`/p2p-circuit/p2p/${this.components.peerId.toString()}`),
209
+ onDataRead: limits.onData,
210
+ onDataWrite: limits.onData,
211
+ log: stream.log.newScope('circuit-relay:connection')
212
+ })
213
+
214
+ const conn = await this.components.upgrader.upgradeOutbound(maConn, {
215
+ ...options,
216
+ limits: limits.getLimits()
217
+ })
218
+
219
+ conn.log('outbound relayed connection established to %p with limits %o, over connection %s', conn.remotePeer, status.limit ?? 'none', relayConnection.id)
220
+
221
+ return conn
222
+ } catch (err: any) {
223
+ this.log.error('circuit relay dial to destination %p via relay %p failed', destinationPeer, relayPeer, err)
224
+ stream?.abort(err)
225
+
226
+ throw err
227
+ }
228
+ }
26
229
 
27
230
  /**
28
- * The maximum number of simultaneous STOP inbound streams that can be open at
29
- * once - each inbound relayed connection uses a STOP stream
30
- *
31
- * @default 300
231
+ * Create a listener
32
232
  */
33
- maxInboundStopStreams?: number
233
+ createListener (options: CreateListenerOptions): Listener {
234
+ return createListener({
235
+ peerId: this.components.peerId,
236
+ connectionManager: this.components.connectionManager,
237
+ addressManager: this.components.addressManager,
238
+ reservationStore: this.reservationStore,
239
+ logger: this.components.logger
240
+ })
241
+ }
34
242
 
35
243
  /**
36
- * The maximum number of simultaneous STOP outbound streams that can be open
37
- * at once. If this transport is used along with the relay server these
38
- * settings should be set to the same value
39
- *
40
- * @default 300
244
+ * Filter check for all Multiaddrs that this transport can listen on
41
245
  */
42
- maxOutboundStopStreams?: number
246
+ listenFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
247
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
248
+
249
+ return multiaddrs.filter((ma) => {
250
+ return CircuitListen.exactMatch(ma) || CircuitSearch.exactMatch(ma)
251
+ })
252
+ }
43
253
 
44
254
  /**
45
- * Incoming STOP requests (e.g. when a remote peer wants to dial us via a
46
- * relay) must finish the initial protocol negotiation within this timeout in
47
- * ms
48
- *
49
- * @deprecated Configure `connectionManager.inboundUpgradeTimeout` instead
255
+ * Filter check for all Multiaddrs that this transport can dial
50
256
  */
51
- stopTimeout?: number
257
+ dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
258
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
259
+
260
+ return multiaddrs.filter((ma) => {
261
+ return Circuit.exactMatch(ma)
262
+ })
263
+ }
52
264
 
53
265
  /**
54
- * When creating a reservation it must complete within this number of ms
55
- *
56
- * @default 10_000
266
+ * An incoming STOP request means a remote peer wants to dial us via a relay
57
267
  */
58
- reservationCompletionTimeout?: number
59
- }
268
+ async onStop (stream: Stream, connection: Connection): Promise<void> {
269
+ const signal = this.components.upgrader.createInboundAbortSignal(this.shutdownController.signal)
270
+
271
+ try {
272
+ if (!this.reservationStore.hasReservation(connection.remotePeer)) {
273
+ try {
274
+ this.log('dialed via relay we did not have a reservation on, start listening on that relay address')
275
+ await this.components.transportManager.listen([connection.remoteAddr.encapsulate('/p2p-circuit')])
276
+ } catch (err: any) {
277
+ // failed to refresh our hitherto unknown relay reservation but allow the connection attempt anyway
278
+ this.log.error('failed to listen on a relay peer we were dialed via but did not have a reservation on', err)
279
+ }
280
+ }
281
+
282
+ const stopStream = pbStream(stream).pb(StopMessage)
283
+ const request = await stopStream.read({
284
+ signal
285
+ })
286
+
287
+ this.log('new circuit relay v2 stop stream from %p with type %s', connection.remotePeer, request.type)
288
+
289
+ if (request?.type === undefined) {
290
+ this.log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer)
291
+ await stopStream.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
292
+ signal
293
+ })
294
+ await stream.close({
295
+ signal
296
+ })
297
+ return
298
+ }
299
+
300
+ // Validate the STOP request has the required input
301
+ if (request.type !== StopMessage.Type.CONNECT) {
302
+ this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
303
+ await stopStream.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, {
304
+ signal
305
+ })
306
+ await stream.close({
307
+ signal
308
+ })
309
+ return
310
+ }
311
+
312
+ if (!isValidStop(request)) {
313
+ this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
314
+ await stopStream.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
315
+ signal
316
+ })
317
+ await stream.close({
318
+ signal
319
+ })
320
+ return
321
+ }
322
+
323
+ const remotePeerId = peerIdFromMultihash(Digest.decode(request.peer.id))
324
+
325
+ if ((await this.components.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) {
326
+ this.log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer)
327
+ await stopStream.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, {
328
+ signal
329
+ })
330
+ await stream.close({
331
+ signal
332
+ })
333
+ return
334
+ }
335
+
336
+ this.log.trace('sending success response to %p', connection.remotePeer)
337
+ await stopStream.write({ type: StopMessage.Type.STATUS, status: Status.OK }, {
338
+ signal
339
+ })
340
+
341
+ const limits = new LimitTracker(request.limit)
342
+ const remoteAddr = connection.remoteAddr.encapsulate(`/p2p-circuit/p2p/${remotePeerId.toString()}`)
343
+ const localAddr = this.components.addressManager.getAddresses()[0]
344
+ const maConn = streamToMaConnection({
345
+ stream: stopStream.unwrap().unwrap(),
346
+ remoteAddr,
347
+ localAddr,
348
+ onDataRead: limits.onData,
349
+ onDataWrite: limits.onData,
350
+ log: stream.log.newScope('circuit-relay:connection')
351
+ })
352
+
353
+ await this.components.upgrader.upgradeInbound(maConn, {
354
+ limits: limits.getLimits(),
355
+ signal
356
+ })
60
357
 
61
- export function circuitRelayTransport (init: CircuitRelayTransportInit = {}): (components: CircuitRelayTransportComponents) => Transport {
62
- return (components) => {
63
- return new CircuitRelayTransport(components, init)
358
+ maConn.log('inbound relayed connection established to %p with limits %o, over connection %s', remotePeerId, request.limit ?? 'none', connection.id)
359
+ } finally {
360
+ signal?.clear()
361
+ }
64
362
  }
65
363
  }
@@ -1,20 +1,19 @@
1
1
  import { ListenError } from '@libp2p/interface'
2
2
  import { PeerMap } from '@libp2p/peer-collections'
3
- import { createScalableCuckooFilter } from '@libp2p/utils/filters'
4
- import { PeerQueue } from '@libp2p/utils/peer-queue'
3
+ import { createScalableCuckooFilter, PeerQueue, pbStream } from '@libp2p/utils'
5
4
  import { multiaddr } from '@multiformats/multiaddr'
6
5
  import { Circuit } from '@multiformats/multiaddr-matcher'
7
- import { pbStream } from 'it-protobuf-stream'
8
6
  import { TypedEventEmitter, setMaxListeners } from 'main-event'
9
7
  import { nanoid } from 'nanoid'
10
8
  import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, KEEP_ALIVE_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
11
9
  import { DoubleRelayError, HadEnoughRelaysError, RelayQueueFullError } from '../errors.js'
12
10
  import { HopMessage, Status } from '../pb/index.js'
13
11
  import { getExpirationMilliseconds } from '../utils.js'
12
+ import type { TransportReservationStoreComponents, TransportReservationStoreInit } from '../index.ts'
14
13
  import type { Reservation } from '../pb/index.js'
15
- import type { AbortOptions, Libp2pEvents, ComponentLogger, Logger, PeerId, PeerStore, Startable, Metrics, Peer, Connection } from '@libp2p/interface'
14
+ import type { AbortOptions, Libp2pEvents, Logger, PeerId, PeerStore, Startable, Peer, Connection } from '@libp2p/interface'
16
15
  import type { ConnectionManager } from '@libp2p/interface-internal'
17
- import type { Filter } from '@libp2p/utils/filters'
16
+ import type { Filter } from '@libp2p/utils'
18
17
  import type { TypedEventTarget } from 'main-event'
19
18
 
20
19
  // allow refreshing a relay reservation if it will expire in the next 10 minutes
@@ -26,43 +25,6 @@ const REFRESH_TIMEOUT = (60 * 1000) * 5
26
25
  // minimum duration before which a reservation must not be refreshed
27
26
  const REFRESH_TIMEOUT_MIN = 30 * 1000
28
27
 
29
- export interface ReservationStoreComponents {
30
- peerId: PeerId
31
- connectionManager: ConnectionManager
32
- peerStore: PeerStore
33
- events: TypedEventTarget<Libp2pEvents>
34
- logger: ComponentLogger
35
- metrics?: Metrics
36
- }
37
-
38
- export interface ReservationStoreInit {
39
- /**
40
- * Multiple relays may be discovered simultaneously - to prevent listening
41
- * on too many relays, this value controls how many to attempt to reserve a
42
- * slot on at once. If set to more than one, we may end up listening on
43
- * more relays than the `maxReservations` value, but on networks with poor
44
- * connectivity the user may wish to attempt to reserve on multiple relays
45
- * simultaneously.
46
- *
47
- * @default 1
48
- */
49
- reservationConcurrency?: number
50
-
51
- /**
52
- * Limit the number of potential relays we will dial
53
- *
54
- * @default 100
55
- */
56
- maxReservationQueueLength?: number
57
-
58
- /**
59
- * When creating a reservation it must complete within this number of ms
60
- *
61
- * @default 5000
62
- */
63
- reservationCompletionTimeout?: number
64
- }
65
-
66
28
  export type RelayType = 'discovered' | 'configured'
67
29
 
68
30
  export interface DiscoveredRelayEntry {
@@ -120,7 +82,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
120
82
  private readonly log: Logger
121
83
  private relayFilter: Filter
122
84
 
123
- constructor (components: ReservationStoreComponents, init?: ReservationStoreInit) {
85
+ constructor (components: TransportReservationStoreComponents, init?: TransportReservationStoreInit) {
124
86
  super()
125
87
 
126
88
  this.log = components.logger.forComponent('libp2p:circuit-relay:transport:reservation-store')
@@ -453,7 +415,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
453
415
  }
454
416
  }
455
417
 
456
- this.log.trace('read response %o', response)
418
+ this.log.trace('read response %s', response.status)
457
419
 
458
420
  if (response.status === Status.OK && response.reservation != null) {
459
421
  // check that the returned relay has the relay address - this can be
@@ -0,0 +1,91 @@
1
+ import { AbstractMultiaddrConnection } from '@libp2p/utils'
2
+ import { Uint8ArrayList } from 'uint8arraylist'
3
+ import type { AbortOptions, MultiaddrConnection, Stream } from '@libp2p/interface'
4
+ import type { AbstractMultiaddrConnectionInit, SendResult } from '@libp2p/utils'
5
+
6
+ export interface StreamMultiaddrConnectionInit extends Omit<AbstractMultiaddrConnectionInit, 'direction'> {
7
+ stream: Stream
8
+
9
+ /**
10
+ * A callback invoked when data is read from the stream
11
+ */
12
+ onDataRead?(buf: Uint8ArrayList | Uint8Array): void
13
+
14
+ /**
15
+ * A callback invoked when data is written to the stream
16
+ */
17
+ onDataWrite?(buf: Uint8ArrayList | Uint8Array): void
18
+ }
19
+
20
+ class StreamMultiaddrConnection extends AbstractMultiaddrConnection {
21
+ private stream: Stream
22
+ private init: StreamMultiaddrConnectionInit
23
+
24
+ constructor (init: StreamMultiaddrConnectionInit) {
25
+ super({
26
+ ...init,
27
+ direction: init.stream.direction
28
+
29
+ })
30
+
31
+ this.init = init
32
+ this.stream = init.stream
33
+
34
+ this.stream.addEventListener('close', (evt) => {
35
+ this.onTransportClosed(evt.error)
36
+ })
37
+
38
+ this.stream.addEventListener('remoteCloseWrite', (evt) => {
39
+ this.onRemoteCloseWrite()
40
+
41
+ // close our end when the remote closes
42
+ this.close()
43
+ .catch(err => {
44
+ this.abort(err)
45
+ })
46
+ })
47
+
48
+ // count incoming bytes
49
+ this.stream.addEventListener('message', (evt) => {
50
+ init.onDataRead?.(evt.data)
51
+ this.onData(evt.data)
52
+ })
53
+
54
+ // forward drain events
55
+ this.stream.addEventListener('drain', () => {
56
+ this.safeDispatchEvent('drain')
57
+ })
58
+ }
59
+
60
+ sendData (data: Uint8ArrayList): SendResult {
61
+ this.init.onDataWrite?.(data)
62
+
63
+ return {
64
+ sentBytes: data.byteLength,
65
+ canSendMore: this.stream.send(data)
66
+ }
67
+ }
68
+
69
+ async sendClose (options?: AbortOptions): Promise<void> {
70
+ await this.stream.close(options)
71
+ }
72
+
73
+ sendReset (): void {
74
+ this.stream.abort(new Error('An error occurred'))
75
+ }
76
+
77
+ sendPause (): void {
78
+ this.stream.pause()
79
+ }
80
+
81
+ sendResume (): void {
82
+ this.stream.resume()
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Convert a Stream into a MultiaddrConnection.
88
+ */
89
+ export function streamToMaConnection (init: StreamMultiaddrConnectionInit): MultiaddrConnection {
90
+ return new StreamMultiaddrConnection(init)
91
+ }