@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.
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +0 -4
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +0 -4
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +170 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/server/index.d.ts +41 -45
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +30 -47
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/server/reservation-store.d.ts +2 -36
- package/dist/src/server/reservation-store.d.ts.map +1 -1
- package/dist/src/server/reservation-store.js.map +1 -1
- package/dist/src/transport/discovery.d.ts +2 -17
- package/dist/src/transport/discovery.d.ts.map +1 -1
- package/dist/src/transport/discovery.js +2 -2
- package/dist/src/transport/discovery.js.map +1 -1
- package/dist/src/transport/index.d.ts +34 -42
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/index.js +291 -5
- package/dist/src/transport/index.js.map +1 -1
- package/dist/src/transport/reservation-store.d.ts +3 -37
- package/dist/src/transport/reservation-store.d.ts.map +1 -1
- package/dist/src/transport/reservation-store.js +4 -6
- package/dist/src/transport/reservation-store.js.map +1 -1
- package/dist/src/transport/stream-to-conn.d.ts +19 -0
- package/dist/src/transport/stream-to-conn.d.ts.map +1 -0
- package/dist/src/transport/stream-to-conn.js +60 -0
- package/dist/src/transport/stream-to-conn.js.map +1 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +36 -59
- package/dist/src/utils.js.map +1 -1
- package/package.json +19 -24
- package/src/constants.ts +0 -5
- package/src/index.ts +207 -8
- package/src/server/index.ts +35 -100
- package/src/server/reservation-store.ts +2 -42
- package/src/transport/discovery.ts +4 -22
- package/src/transport/index.ts +338 -46
- package/src/transport/reservation-store.ts +8 -46
- package/src/transport/stream-to-conn.ts +91 -0
- package/src/utils.ts +37 -66
- package/dist/src/transport/transport.d.ts +0 -54
- package/dist/src/transport/transport.d.ts.map +0 -1
- package/dist/src/transport/transport.js +0 -314
- package/dist/src/transport/transport.js.map +0 -1
- package/dist/typedoc-urls.json +0 -23
- 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
|
-
}
|