@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,380 +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
- log: this.log,
237
- onDataRead: limits.onData,
238
- onDataWrite: limits.onData
239
- })
240
-
241
- const conn = await this.upgrader.upgradeOutbound(maConn, {
242
- ...options,
243
- limits: limits.getLimits()
244
- })
245
-
246
- conn.log('outbound relayed connection established to %p with limits %o, over connection %s', conn.remotePeer, status.limit ?? 'none', relayConnection.id)
247
-
248
- return conn
249
- } catch (err: any) {
250
- this.log.error('circuit relay dial to destination %p via relay %p failed', destinationPeer, relayPeer, err)
251
- stream?.abort(err)
252
-
253
- throw err
254
- }
255
- }
256
-
257
- /**
258
- * Create a listener
259
- */
260
- createListener (options: CreateListenerOptions): Listener {
261
- return createListener({
262
- peerId: this.peerId,
263
- connectionManager: this.connectionManager,
264
- addressManager: this.addressManager,
265
- reservationStore: this.reservationStore,
266
- logger: this.logger
267
- })
268
- }
269
-
270
- /**
271
- * Filter check for all Multiaddrs that this transport can listen on
272
- */
273
- listenFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
274
- multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
275
-
276
- return multiaddrs.filter((ma) => {
277
- return CircuitListen.exactMatch(ma) || CircuitSearch.exactMatch(ma)
278
- })
279
- }
280
-
281
- /**
282
- * Filter check for all Multiaddrs that this transport can dial
283
- */
284
- dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
285
- multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
286
-
287
- return multiaddrs.filter((ma) => {
288
- return Circuit.exactMatch(ma)
289
- })
290
- }
291
-
292
- /**
293
- * An incoming STOP request means a remote peer wants to dial us via a relay
294
- */
295
- async onStop ({ connection, stream }: IncomingStreamData, signal: AbortSignal): Promise<void> {
296
- if (!this.reservationStore.hasReservation(connection.remotePeer)) {
297
- try {
298
- this.log('dialed via relay we did not have a reservation on, start listening on that relay address')
299
- await this.transportManager.listen([connection.remoteAddr.encapsulate('/p2p-circuit')])
300
- } catch (err: any) {
301
- // failed to refresh our hitherto unknown relay reservation but allow the connection attempt anyway
302
- this.log.error('failed to listen on a relay peer we were dialed via but did not have a reservation on', err)
303
- }
304
- }
305
-
306
- const pbstr = pbStream(stream).pb(StopMessage)
307
- const request = await pbstr.read({
308
- signal
309
- })
310
-
311
- this.log('new circuit relay v2 stop stream from %p with type %s', connection.remotePeer, request.type)
312
-
313
- if (request?.type === undefined) {
314
- this.log.error('type was missing from circuit v2 stop protocol request from %s', connection.remotePeer)
315
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
316
- signal
317
- })
318
- await stream.close()
319
- return
320
- }
321
-
322
- // Validate the STOP request has the required input
323
- if (request.type !== StopMessage.Type.CONNECT) {
324
- this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
325
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE }, {
326
- signal
327
- })
328
- await stream.close()
329
- return
330
- }
331
-
332
- if (!isValidStop(request)) {
333
- this.log.error('invalid stop connect request via peer %p', connection.remotePeer)
334
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE }, {
335
- signal
336
- })
337
- await stream.close({
338
- signal
339
- })
340
- return
341
- }
342
-
343
- const remotePeerId = peerIdFromMultihash(Digest.decode(request.peer.id))
344
-
345
- if ((await this.connectionGater.denyInboundRelayedConnection?.(connection.remotePeer, remotePeerId)) === true) {
346
- this.log.error('connection gater denied inbound relayed connection from %p', connection.remotePeer)
347
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, {
348
- signal
349
- })
350
- await stream.close({
351
- signal
352
- })
353
- return
354
- }
355
-
356
- this.log.trace('sending success response to %p', connection.remotePeer)
357
- await pbstr.write({ type: StopMessage.Type.STATUS, status: Status.OK }, {
358
- signal
359
- })
360
-
361
- const limits = new LimitTracker(request.limit)
362
- const remoteAddr = connection.remoteAddr.encapsulate(`/p2p-circuit/p2p/${remotePeerId.toString()}`)
363
- const localAddr = this.addressManager.getAddresses()[0]
364
- const maConn = streamToMaConnection({
365
- stream: pbstr.unwrap().unwrap(),
366
- remoteAddr,
367
- localAddr,
368
- log: this.log,
369
- onDataRead: limits.onData,
370
- onDataWrite: limits.onData
371
- })
372
-
373
- await this.upgrader.upgradeInbound(maConn, {
374
- limits: limits.getLimits(),
375
- signal
376
- })
377
-
378
- maConn.log('inbound relayed connection established to %p with limits %o, over connection %s', remotePeerId, request.limit ?? 'none', connection.id)
379
- }
380
- }