@libp2p/circuit-relay-v2 3.2.23 → 3.2.24-0f07e3df5

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 (52) hide show
  1. package/dist/index.min.js +1 -1
  2. package/dist/index.min.js.map +4 -4
  3. package/dist/src/constants.d.ts +0 -4
  4. package/dist/src/constants.d.ts.map +1 -1
  5. package/dist/src/constants.js +0 -4
  6. package/dist/src/constants.js.map +1 -1
  7. package/dist/src/index.d.ts +170 -7
  8. package/dist/src/index.d.ts.map +1 -1
  9. package/dist/src/index.js +12 -2
  10. package/dist/src/index.js.map +1 -1
  11. package/dist/src/server/index.d.ts +41 -45
  12. package/dist/src/server/index.d.ts.map +1 -1
  13. package/dist/src/server/index.js +30 -47
  14. package/dist/src/server/index.js.map +1 -1
  15. package/dist/src/server/reservation-store.d.ts +2 -36
  16. package/dist/src/server/reservation-store.d.ts.map +1 -1
  17. package/dist/src/server/reservation-store.js.map +1 -1
  18. package/dist/src/transport/discovery.d.ts +2 -17
  19. package/dist/src/transport/discovery.d.ts.map +1 -1
  20. package/dist/src/transport/discovery.js +2 -2
  21. package/dist/src/transport/discovery.js.map +1 -1
  22. package/dist/src/transport/index.d.ts +34 -42
  23. package/dist/src/transport/index.d.ts.map +1 -1
  24. package/dist/src/transport/index.js +291 -5
  25. package/dist/src/transport/index.js.map +1 -1
  26. package/dist/src/transport/reservation-store.d.ts +3 -37
  27. package/dist/src/transport/reservation-store.d.ts.map +1 -1
  28. package/dist/src/transport/reservation-store.js +4 -6
  29. package/dist/src/transport/reservation-store.js.map +1 -1
  30. package/dist/src/transport/stream-to-conn.d.ts +19 -0
  31. package/dist/src/transport/stream-to-conn.d.ts.map +1 -0
  32. package/dist/src/transport/stream-to-conn.js +60 -0
  33. package/dist/src/transport/stream-to-conn.js.map +1 -0
  34. package/dist/src/utils.d.ts.map +1 -1
  35. package/dist/src/utils.js +36 -59
  36. package/dist/src/utils.js.map +1 -1
  37. package/package.json +19 -24
  38. package/src/constants.ts +0 -5
  39. package/src/index.ts +207 -8
  40. package/src/server/index.ts +35 -100
  41. package/src/server/reservation-store.ts +2 -42
  42. package/src/transport/discovery.ts +4 -22
  43. package/src/transport/index.ts +338 -46
  44. package/src/transport/reservation-store.ts +8 -46
  45. package/src/transport/stream-to-conn.ts +91 -0
  46. package/src/utils.ts +37 -66
  47. package/dist/src/transport/transport.d.ts +0 -54
  48. package/dist/src/transport/transport.d.ts.map +0 -1
  49. package/dist/src/transport/transport.js +0 -314
  50. package/dist/src/transport/transport.js.map +0 -1
  51. package/dist/typedoc-urls.json +0 -23
  52. package/src/transport/transport.ts +0 -378
@@ -1,378 +0,0 @@
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 { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
5
- import { multiaddr } from '@multiformats/multiaddr'
6
- import { Circuit } from '@multiformats/multiaddr-matcher'
7
- import { pbStream } from 'it-protobuf-stream'
8
- import { setMaxListeners } from 'main-event'
9
- import * as Digest from 'multiformats/hashes/digest'
10
- import { CustomProgressEvent } from 'progress-events'
11
- 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'
12
- import { StopMessage, HopMessage, Status } from '../pb/index.js'
13
- import { CircuitListen, CircuitSearch, LimitTracker } from '../utils.js'
14
- import { RelayDiscovery } from './discovery.js'
15
- import { createListener } from './listener.js'
16
- import { ReservationStore } from './reservation-store.js'
17
- import type { CircuitRelayTransportComponents, CircuitRelayTransportInit } from './index.js'
18
- import type { Transport, CreateListenerOptions, Listener, Upgrader, ComponentLogger, Logger, Connection, Stream, ConnectionGater, PeerId, PeerStore, OutboundConnectionUpgradeEvents, DialTransportOptions, OpenConnectionProgressEvents, IncomingStreamData } from '@libp2p/interface'
19
- import type { AddressManager, ConnectionManager, Registrar, TransportManager } from '@libp2p/interface-internal'
20
- import type { Multiaddr } from '@multiformats/multiaddr'
21
- import type { ProgressEvent } from 'progress-events'
22
-
23
- const isValidStop = (request: StopMessage): request is Required<StopMessage> => {
24
- if (request.peer == null) {
25
- return false
26
- }
27
-
28
- try {
29
- request.peer.addrs.forEach(multiaddr)
30
- } catch {
31
- return false
32
- }
33
-
34
- return true
35
- }
36
-
37
- const defaults = {
38
- maxInboundStopStreams: MAX_CONNECTIONS,
39
- maxOutboundStopStreams: MAX_CONNECTIONS,
40
- stopTimeout: 30000
41
- }
42
-
43
- export type CircuitRelayDialEvents =
44
- OutboundConnectionUpgradeEvents |
45
- OpenConnectionProgressEvents |
46
- ProgressEvent<'circuit-relay:open-connection'> |
47
- ProgressEvent<'circuit-relay:reuse-connection'> |
48
- ProgressEvent<'circuit-relay:open-hop-stream'> |
49
- ProgressEvent<'circuit-relay:write-connect-message'> |
50
- ProgressEvent<'circuit-relay:read-connect-response'>
51
-
52
- export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents> {
53
- private readonly discovery?: RelayDiscovery
54
- private readonly registrar: Registrar
55
- private readonly peerStore: PeerStore
56
- private readonly connectionManager: ConnectionManager
57
- private readonly transportManager: TransportManager
58
- private readonly peerId: PeerId
59
- private readonly upgrader: Upgrader
60
- private readonly addressManager: AddressManager
61
- private readonly connectionGater: ConnectionGater
62
- public readonly reservationStore: ReservationStore
63
- private readonly logger: ComponentLogger
64
- private readonly maxInboundStopStreams: number
65
- private readonly maxOutboundStopStreams?: number
66
- private started: boolean
67
- private readonly log: Logger
68
- private shutdownController: AbortController
69
-
70
- constructor (components: CircuitRelayTransportComponents, init: CircuitRelayTransportInit = {}) {
71
- this.log = components.logger.forComponent('libp2p:circuit-relay:transport')
72
- this.registrar = components.registrar
73
- this.peerStore = components.peerStore
74
- this.connectionManager = components.connectionManager
75
- this.transportManager = components.transportManager
76
- this.logger = components.logger
77
- this.peerId = components.peerId
78
- this.upgrader = components.upgrader
79
- this.addressManager = components.addressManager
80
- this.connectionGater = components.connectionGater
81
- this.maxInboundStopStreams = init.maxInboundStopStreams ?? defaults.maxInboundStopStreams
82
- this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
83
- this.shutdownController = new AbortController()
84
-
85
- this.discovery = new RelayDiscovery(components, {
86
- filter: init.discoveryFilter ?? peerFilter(DEFAULT_DISCOVERY_FILTER_SIZE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE)
87
- })
88
- this.discovery.addEventListener('relay:discover', (evt) => {
89
- this.reservationStore.addRelay(evt.detail, 'discovered')
90
- .catch(err => {
91
- if (err.name !== 'HadEnoughRelaysError' && err.name !== 'RelayQueueFullError') {
92
- this.log.error('could not add discovered relay %p', evt.detail, err)
93
- }
94
- })
95
- })
96
- this.reservationStore = new ReservationStore(components, init)
97
- this.reservationStore.addEventListener('relay:not-enough-relays', () => {
98
- this.discovery?.startDiscovery()
99
- })
100
- this.reservationStore.addEventListener('relay:found-enough-relays', () => {
101
- this.discovery?.stopDiscovery()
102
- })
103
-
104
- this.started = false
105
- }
106
-
107
- readonly [Symbol.toStringTag] = '@libp2p/circuit-relay-v2-transport'
108
-
109
- readonly [serviceCapabilities]: string[] = [
110
- '@libp2p/transport',
111
- '@libp2p/circuit-relay-v2-transport'
112
- ]
113
-
114
- get [serviceDependencies] (): string[] {
115
- // we only need identify if discovery is enabled
116
- if (this.discovery != null) {
117
- return [
118
- '@libp2p/identify'
119
- ]
120
- }
121
-
122
- return []
123
- }
124
-
125
- readonly [transportSymbol] = true
126
-
127
- isStarted (): boolean {
128
- return this.started
129
- }
130
-
131
- async start (): Promise<void> {
132
- this.shutdownController = new AbortController()
133
- setMaxListeners(Infinity, this.shutdownController.signal)
134
-
135
- await this.registrar.handle(RELAY_V2_STOP_CODEC, (data) => {
136
- const signal = this.upgrader.createInboundAbortSignal(this.shutdownController.signal)
137
-
138
- void this.onStop(data, signal)
139
- .catch(err => {
140
- this.log.error('error while handling STOP protocol', err)
141
- data.stream.abort(err)
142
- })
143
- .finally(() => {
144
- signal.clear()
145
- })
146
- }, {
147
- maxInboundStreams: this.maxInboundStopStreams,
148
- maxOutboundStreams: this.maxOutboundStopStreams,
149
- runOnLimitedConnection: true
150
- })
151
-
152
- await start(this.discovery, this.reservationStore)
153
-
154
- this.started = true
155
- }
156
-
157
- async stop (): Promise<void> {
158
- this.shutdownController.abort()
159
- await stop(this.discovery, this.reservationStore)
160
- await this.registrar.unhandle(RELAY_V2_STOP_CODEC)
161
-
162
- this.started = false
163
- }
164
-
165
- /**
166
- * Dial a peer over a relay
167
- */
168
- async dial (ma: Multiaddr, options: DialTransportOptions<CircuitRelayDialEvents>): Promise<Connection> {
169
- if (ma.protoCodes().filter(code => code === CIRCUIT_PROTO_CODE).length !== 1) {
170
- const errMsg = 'Invalid circuit relay address'
171
- this.log.error(errMsg, ma)
172
- throw new DialError(errMsg)
173
- }
174
-
175
- // Check the multiaddr to see if it contains a relay and a destination peer
176
- const addrs = ma.toString().split('/p2p-circuit')
177
- const relayAddr = multiaddr(addrs[0])
178
- const destinationAddr = multiaddr(addrs[addrs.length - 1])
179
- const relayId = relayAddr.getPeerId()
180
- const destinationId = destinationAddr.getPeerId()
181
-
182
- if (relayId == null || destinationId == null) {
183
- const errMsg = `ircuit relay dial to ${ma.toString()} failed as address did not have both relay and destination PeerIDs`
184
- this.log.error(`c${errMsg}`)
185
- throw new DialError(`C${errMsg}`)
186
- }
187
-
188
- const relayPeer = peerIdFromString(relayId)
189
- const destinationPeer = peerIdFromString(destinationId)
190
-
191
- const relayConnections = this.connectionManager.getConnections(relayPeer)
192
- let relayConnection = relayConnections[0]
193
-
194
- if (relayConnection == null) {
195
- await this.peerStore.merge(relayPeer, {
196
- multiaddrs: [relayAddr]
197
- })
198
-
199
- options.onProgress?.(new CustomProgressEvent('circuit-relay:open-connection'))
200
- relayConnection = await this.connectionManager.openConnection(relayPeer, options)
201
- } else {
202
- options.onProgress?.(new CustomProgressEvent('circuit-relay:reuse-connection'))
203
- }
204
-
205
- let stream: Stream | undefined
206
-
207
- try {
208
- options.onProgress?.(new CustomProgressEvent('circuit-relay:open-hop-stream'))
209
- stream = await relayConnection.newStream(RELAY_V2_HOP_CODEC, options)
210
-
211
- const pbstr = pbStream(stream)
212
- const hopstr = pbstr.pb(HopMessage)
213
-
214
- options.onProgress?.(new CustomProgressEvent('circuit-relay:write-connect-message'))
215
- await hopstr.write({
216
- type: HopMessage.Type.CONNECT,
217
- peer: {
218
- id: destinationPeer.toMultihash().bytes,
219
- addrs: [multiaddr(destinationAddr).bytes]
220
- }
221
- }, options)
222
-
223
- options.onProgress?.(new CustomProgressEvent('circuit-relay:read-connect-response'))
224
- const status = await hopstr.read(options)
225
-
226
- if (status.status !== Status.OK) {
227
- throw new InvalidMessageError(`failed to connect via relay with status ${status?.status?.toString() ?? 'undefined'}`)
228
- }
229
-
230
- const limits = new LimitTracker(status.limit)
231
-
232
- const maConn = streamToMaConnection({
233
- stream: pbstr.unwrap(),
234
- remoteAddr: ma,
235
- localAddr: relayAddr.encapsulate(`/p2p-circuit/p2p/${this.peerId.toString()}`),
236
- logger: this.logger,
237
- onDataRead: limits.onData,
238
- onDataWrite: limits.onData
239
- })
240
-
241
- this.log('new outbound relayed connection %a', maConn.remoteAddr)
242
-
243
- return await this.upgrader.upgradeOutbound(maConn, {
244
- ...options,
245
- limits: limits.getLimits()
246
- })
247
- } catch (err: any) {
248
- this.log.error('circuit relay dial to destination %p via relay %p failed', destinationPeer, relayPeer, err)
249
- stream?.abort(err)
250
-
251
- throw err
252
- }
253
- }
254
-
255
- /**
256
- * Create a listener
257
- */
258
- createListener (options: CreateListenerOptions): Listener {
259
- return createListener({
260
- peerId: this.peerId,
261
- connectionManager: this.connectionManager,
262
- addressManager: this.addressManager,
263
- reservationStore: this.reservationStore,
264
- logger: this.logger
265
- })
266
- }
267
-
268
- /**
269
- * Filter check for all Multiaddrs that this transport can listen on
270
- */
271
- listenFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
272
- multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
273
-
274
- return multiaddrs.filter((ma) => {
275
- return CircuitListen.exactMatch(ma) || CircuitSearch.exactMatch(ma)
276
- })
277
- }
278
-
279
- /**
280
- * Filter check for all Multiaddrs that this transport can dial
281
- */
282
- dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
283
- multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
284
-
285
- return multiaddrs.filter((ma) => {
286
- return Circuit.exactMatch(ma)
287
- })
288
- }
289
-
290
- /**
291
- * An incoming STOP request means a remote peer wants to dial us via a relay
292
- */
293
- async onStop ({ connection, stream }: IncomingStreamData, signal: AbortSignal): Promise<void> {
294
- if (!this.reservationStore.hasReservation(connection.remotePeer)) {
295
- try {
296
- this.log('dialed via relay we did not have a reservation on, start listening on that relay address')
297
- await this.transportManager.listen([connection.remoteAddr.encapsulate('/p2p-circuit')])
298
- } catch (err: any) {
299
- // failed to refresh our hitherto unknown relay reservation but allow the connection attempt anyway
300
- this.log.error('failed to listen on a relay peer we were dialed via but did not have a reservation on', err)
301
- }
302
- }
303
-
304
- const pbstr = pbStream(stream).pb(StopMessage)
305
- const request = await pbstr.read({
306
- signal
307
- })
308
-
309
- this.log('new circuit relay v2 stop stream from %p with type %s', connection.remotePeer, request.type)
310
-
311
- if (request?.type === undefined) {
312
- this.log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer)
313
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
314
- signal
315
- })
316
- await stream.close()
317
- return
318
- }
319
-
320
- // Validate the STOP request has the required input
321
- if (request.type !== StopMessage.Type.CONNECT) {
322
- this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
323
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, {
324
- signal
325
- })
326
- await stream.close()
327
- return
328
- }
329
-
330
- if (!isValidStop(request)) {
331
- this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
332
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
333
- signal
334
- })
335
- await stream.close({
336
- signal
337
- })
338
- return
339
- }
340
-
341
- const remotePeerId = peerIdFromMultihash(Digest.decode(request.peer.id))
342
-
343
- if ((await this.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) {
344
- this.log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer)
345
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, {
346
- signal
347
- })
348
- await stream.close({
349
- signal
350
- })
351
- return
352
- }
353
-
354
- this.log.trace('sending success response to %p', connection.remotePeer)
355
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.OK }, {
356
- signal
357
- })
358
-
359
- const limits = new LimitTracker(request.limit)
360
- const remoteAddr = connection.remoteAddr.encapsulate(`/p2p-circuit/p2p/${remotePeerId.toString()}`)
361
- const localAddr = this.addressManager.getAddresses()[0]
362
- const maConn = streamToMaConnection({
363
- stream: pbstr.unwrap().unwrap(),
364
- remoteAddr,
365
- localAddr,
366
- logger: this.logger,
367
- onDataRead: limits.onData,
368
- onDataWrite: limits.onData
369
- })
370
-
371
- this.log('new inbound relayed connection %a', maConn.remoteAddr)
372
- await this.upgrader.upgradeInbound(maConn, {
373
- limits: limits.getLimits(),
374
- signal
375
- })
376
- this.log('%s connection %a upgraded', 'inbound', maConn.remoteAddr)
377
- }
378
- }