@libp2p/circuit-relay-v2 2.1.3-e99e8f448 → 2.1.4
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 +2 -2
- package/dist/src/constants.d.ts +4 -2
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +5 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +52 -50
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/transport/discovery.js +1 -1
- package/dist/src/transport/discovery.js.map +1 -1
- package/dist/src/transport/index.d.ts +2 -2
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/listener.d.ts.map +1 -1
- package/dist/src/transport/listener.js +23 -22
- package/dist/src/transport/listener.js.map +1 -1
- package/dist/src/transport/reservation-store.d.ts +4 -3
- package/dist/src/transport/reservation-store.d.ts.map +1 -1
- package/dist/src/transport/reservation-store.js +79 -40
- package/dist/src/transport/reservation-store.js.map +1 -1
- package/dist/typedoc-urls.json +23 -0
- package/package.json +10 -11
- package/src/constants.ts +7 -2
- package/src/index.ts +2 -2
- package/src/server/index.ts +54 -53
- package/src/transport/discovery.ts +1 -1
- package/src/transport/index.ts +2 -2
- package/src/transport/listener.ts +28 -25
- package/src/transport/reservation-store.ts +105 -47
@@ -66,7 +66,7 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
66
66
|
this.topologyId = await this.registrar.register(RELAY_V2_HOP_CODEC, {
|
67
67
|
filter: this.filter,
|
68
68
|
onConnect: (peerId) => {
|
69
|
-
this.log('discovered relay %p', peerId)
|
69
|
+
this.log.trace('discovered relay %p', peerId)
|
70
70
|
this.safeDispatchEvent('relay:discover', { detail: peerId })
|
71
71
|
}
|
72
72
|
})
|
package/src/transport/index.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { CircuitRelayTransport } from './transport.js'
|
2
2
|
import type { RelayDiscoveryComponents } from './discovery.js'
|
3
|
-
import type {
|
3
|
+
import type { ReservationStoreInit } from './reservation-store.js'
|
4
4
|
import type { Transport, Upgrader, Libp2pEvents, ConnectionGater, TypedEventTarget, PeerId, TopologyFilter } from '@libp2p/interface'
|
5
5
|
import type { AddressManager, Registrar } from '@libp2p/interface-internal'
|
6
6
|
|
@@ -16,7 +16,7 @@ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponent
|
|
16
16
|
/**
|
17
17
|
* RelayConfig configures the circuit v2 relay transport.
|
18
18
|
*/
|
19
|
-
export interface CircuitRelayTransportInit extends
|
19
|
+
export interface CircuitRelayTransportInit extends ReservationStoreInit {
|
20
20
|
/**
|
21
21
|
* The number of peers running diable relays to search for and connect to
|
22
22
|
*
|
@@ -14,7 +14,7 @@ export interface CircuitRelayTransportListenerComponents {
|
|
14
14
|
|
15
15
|
class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> implements Listener {
|
16
16
|
private readonly connectionManager: ConnectionManager
|
17
|
-
private readonly
|
17
|
+
private readonly reservationStore: ReservationStore
|
18
18
|
private readonly listeningAddrs: PeerMap<Multiaddr[]>
|
19
19
|
private readonly log: Logger
|
20
20
|
|
@@ -23,15 +23,26 @@ class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> im
|
|
23
23
|
|
24
24
|
this.log = components.logger.forComponent('libp2p:circuit-relay:transport:listener')
|
25
25
|
this.connectionManager = components.connectionManager
|
26
|
-
this.
|
26
|
+
this.reservationStore = components.relayStore
|
27
27
|
this.listeningAddrs = new PeerMap()
|
28
28
|
|
29
29
|
// remove listening addrs when a relay is removed
|
30
|
-
this.
|
30
|
+
this.reservationStore.addEventListener('relay:removed', this._onRemoveRelayPeer)
|
31
31
|
}
|
32
32
|
|
33
33
|
_onRemoveRelayPeer = (evt: CustomEvent<PeerId>): void => {
|
34
|
-
this
|
34
|
+
const had = this.listeningAddrs.has(evt.detail)
|
35
|
+
|
36
|
+
this.log('relay peer removed %p - had reservation', evt.detail, had)
|
37
|
+
|
38
|
+
if (!had) {
|
39
|
+
return
|
40
|
+
}
|
41
|
+
|
42
|
+
this.listeningAddrs.delete(evt.detail)
|
43
|
+
|
44
|
+
// announce listen addresses change
|
45
|
+
this.safeDispatchEvent('listening')
|
35
46
|
}
|
36
47
|
|
37
48
|
async listen (addr: Multiaddr): Promise<void> {
|
@@ -41,14 +52,14 @@ class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> im
|
|
41
52
|
const relayAddr = addr.decapsulate('/p2p-circuit')
|
42
53
|
const relayConn = await this.connectionManager.openConnection(relayAddr)
|
43
54
|
|
44
|
-
if (!this.
|
55
|
+
if (!this.reservationStore.hasReservation(relayConn.remotePeer)) {
|
45
56
|
this.log('making reservation on peer %p', relayConn.remotePeer)
|
46
57
|
// addRelay calls transportManager.listen which calls this listen method
|
47
|
-
await this.
|
58
|
+
await this.reservationStore.addRelay(relayConn.remotePeer, 'configured')
|
48
59
|
return
|
49
60
|
}
|
50
61
|
|
51
|
-
const reservation = this.
|
62
|
+
const reservation = this.reservationStore.getReservation(relayConn.remotePeer)
|
52
63
|
|
53
64
|
if (reservation == null) {
|
54
65
|
throw new ListenError('Did not have reservation after making reservation')
|
@@ -60,11 +71,11 @@ class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> im
|
|
60
71
|
}
|
61
72
|
|
62
73
|
// add all addresses from the relay reservation
|
63
|
-
this.listeningAddrs.set(relayConn.remotePeer, reservation.addrs
|
64
|
-
|
65
|
-
|
74
|
+
this.listeningAddrs.set(relayConn.remotePeer, reservation.addrs
|
75
|
+
.map(buf => multiaddr(buf).encapsulate('/p2p-circuit'))
|
76
|
+
)
|
66
77
|
|
67
|
-
this.safeDispatchEvent('listening'
|
78
|
+
this.safeDispatchEvent('listening')
|
68
79
|
}
|
69
80
|
|
70
81
|
getAddrs (): Multiaddr[] {
|
@@ -72,22 +83,14 @@ class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> im
|
|
72
83
|
}
|
73
84
|
|
74
85
|
async close (): Promise<void> {
|
86
|
+
await this.reservationStore.cancelReservations()
|
87
|
+
this.listeningAddrs.clear()
|
75
88
|
|
76
|
-
|
77
|
-
|
78
|
-
#removeRelayPeer (peerId: PeerId): void {
|
79
|
-
const had = this.listeningAddrs.has(peerId)
|
80
|
-
|
81
|
-
this.log('relay peer removed %p - had reservation', peerId, had)
|
89
|
+
// remove listener
|
90
|
+
this.reservationStore.removeEventListener('relay:removed', this._onRemoveRelayPeer)
|
82
91
|
|
83
|
-
|
84
|
-
|
85
|
-
if (had) {
|
86
|
-
this.log.trace('removing relay event listener for peer %p', peerId)
|
87
|
-
this.relayStore.removeEventListener('relay:removed', this._onRemoveRelayPeer)
|
88
|
-
// Announce listen addresses change
|
89
|
-
this.safeDispatchEvent('close', {})
|
90
|
-
}
|
92
|
+
// announce listen addresses change
|
93
|
+
this.safeDispatchEvent('close')
|
91
94
|
}
|
92
95
|
}
|
93
96
|
|
@@ -1,15 +1,14 @@
|
|
1
|
-
import {
|
1
|
+
import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
2
2
|
import { PeerMap } from '@libp2p/peer-collections'
|
3
3
|
import { createBloomFilter } from '@libp2p/utils/filters'
|
4
4
|
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
5
5
|
import { multiaddr } from '@multiformats/multiaddr'
|
6
6
|
import { pbStream } from 'it-protobuf-stream'
|
7
|
-
import {
|
8
|
-
import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
|
7
|
+
import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, KEEP_ALIVE_TAG, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
|
9
8
|
import { HopMessage, Status } from '../pb/index.js'
|
10
9
|
import { getExpirationMilliseconds } from '../utils.js'
|
11
10
|
import type { Reservation } from '../pb/index.js'
|
12
|
-
import type { TypedEventTarget, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, PeerId, PeerStore, Startable, Metrics } from '@libp2p/interface'
|
11
|
+
import type { TypedEventTarget, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, PeerId, PeerStore, Startable, Metrics, Peer } from '@libp2p/interface'
|
13
12
|
import type { ConnectionManager, TransportManager } from '@libp2p/interface-internal'
|
14
13
|
import type { Filter } from '@libp2p/utils/filters'
|
15
14
|
|
@@ -22,7 +21,7 @@ const REFRESH_TIMEOUT = (60 * 1000) * 5
|
|
22
21
|
// minimum duration before which a reservation must not be refreshed
|
23
22
|
const REFRESH_TIMEOUT_MIN = 30 * 1000
|
24
23
|
|
25
|
-
export interface
|
24
|
+
export interface ReservationStoreComponents {
|
26
25
|
peerId: PeerId
|
27
26
|
connectionManager: ConnectionManager
|
28
27
|
transportManager: TransportManager
|
@@ -32,7 +31,7 @@ export interface RelayStoreComponents {
|
|
32
31
|
metrics?: Metrics
|
33
32
|
}
|
34
33
|
|
35
|
-
export interface
|
34
|
+
export interface ReservationStoreInit {
|
36
35
|
/**
|
37
36
|
* Multiple relays may be discovered simultaneously - to prevent listening
|
38
37
|
* on too many relays, this value controls how many to attempt to reserve a
|
@@ -71,6 +70,11 @@ interface RelayEntry {
|
|
71
70
|
timeout: ReturnType<typeof setTimeout>
|
72
71
|
type: RelayType
|
73
72
|
reservation: Reservation
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Stores the id of the connection we have to the relay
|
76
|
+
*/
|
77
|
+
connection: string
|
74
78
|
}
|
75
79
|
|
76
80
|
export interface ReservationStoreEvents {
|
@@ -94,7 +98,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
94
98
|
private readonly log: Logger
|
95
99
|
private readonly relayFilter: Filter
|
96
100
|
|
97
|
-
constructor (components:
|
101
|
+
constructor (components: ReservationStoreComponents, init?: ReservationStoreInit) {
|
98
102
|
super()
|
99
103
|
|
100
104
|
this.log = components.logger.forComponent('libp2p:circuit-relay:transport:reservation-store')
|
@@ -117,11 +121,21 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
117
121
|
metrics: components.metrics
|
118
122
|
})
|
119
123
|
|
120
|
-
//
|
121
|
-
//
|
122
|
-
// for new relays
|
123
|
-
this.events.addEventListener('
|
124
|
-
this
|
124
|
+
// reservations are only valid while we are still connected to the relay.
|
125
|
+
// if we had a reservation opened via that connection, remove it and maybe
|
126
|
+
// trigger a search for new relays
|
127
|
+
this.events.addEventListener('connection:close', (evt) => {
|
128
|
+
const reservation = [...this.reservations.values()]
|
129
|
+
.find(reservation => reservation.connection === evt.detail.id)
|
130
|
+
|
131
|
+
if (reservation == null) {
|
132
|
+
return
|
133
|
+
}
|
134
|
+
|
135
|
+
this.#removeReservation(evt.detail.remotePeer, reservation)
|
136
|
+
.catch(err => {
|
137
|
+
this.log('could not remove relay %p - %e', evt.detail, err)
|
138
|
+
})
|
125
139
|
})
|
126
140
|
}
|
127
141
|
|
@@ -134,10 +148,37 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
134
148
|
}
|
135
149
|
|
136
150
|
afterStart (): void {
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
151
|
+
// remove old relay tags
|
152
|
+
void Promise.resolve()
|
153
|
+
.then(async () => {
|
154
|
+
const relayPeers: Peer[] = await this.peerStore.all({
|
155
|
+
filters: [(peer) => {
|
156
|
+
return peer.tags.has(RELAY_TAG)
|
157
|
+
}]
|
158
|
+
})
|
159
|
+
|
160
|
+
this.log('removing tag from %d old relays', relayPeers.length)
|
161
|
+
|
162
|
+
// remove old relay tag and redial
|
163
|
+
await Promise.all(
|
164
|
+
relayPeers.map(async peer => {
|
165
|
+
await this.peerStore.merge(peer.id, {
|
166
|
+
tags: {
|
167
|
+
[RELAY_TAG]: undefined,
|
168
|
+
[KEEP_ALIVE_TAG]: undefined
|
169
|
+
}
|
170
|
+
})
|
171
|
+
})
|
172
|
+
)
|
173
|
+
|
174
|
+
if (this.reservations.size < this.maxDiscoveredRelays) {
|
175
|
+
this.log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays)
|
176
|
+
this.safeDispatchEvent('relay:not-enough-relays', {})
|
177
|
+
}
|
178
|
+
})
|
179
|
+
.catch(err => {
|
180
|
+
this.log.error(err)
|
181
|
+
})
|
141
182
|
}
|
142
183
|
|
143
184
|
stop (): void {
|
@@ -157,26 +198,26 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
157
198
|
*/
|
158
199
|
async addRelay (peerId: PeerId, type: RelayType): Promise<void> {
|
159
200
|
if (this.peerId.equals(peerId)) {
|
160
|
-
this.log('not trying to use self as relay')
|
201
|
+
this.log.trace('not trying to use self as relay')
|
161
202
|
return
|
162
203
|
}
|
163
204
|
|
164
205
|
if (this.reserveQueue.size > this.maxReservationQueueLength) {
|
165
|
-
this.log('not adding potential relay peer %p as the queue is full', peerId)
|
206
|
+
this.log.trace('not adding potential relay peer %p as the queue is full', peerId)
|
166
207
|
return
|
167
208
|
}
|
168
209
|
|
169
210
|
if (this.reserveQueue.has(peerId)) {
|
170
|
-
this.log('potential relay peer %p is already in the reservation queue', peerId)
|
211
|
+
this.log.trace('potential relay peer %p is already in the reservation queue', peerId)
|
171
212
|
return
|
172
213
|
}
|
173
214
|
|
174
215
|
if (this.relayFilter.has(peerId.toMultihash().bytes)) {
|
175
|
-
this.log('potential relay peer %p has failed previously, not trying again', peerId)
|
216
|
+
this.log.trace('potential relay peer %p has failed previously, not trying again', peerId)
|
176
217
|
return
|
177
218
|
}
|
178
219
|
|
179
|
-
this.log('try to reserve relay slot with %p', peerId)
|
220
|
+
this.log.trace('try to reserve relay slot with %p', peerId)
|
180
221
|
|
181
222
|
await this.reserveQueue.add(async () => {
|
182
223
|
const start = Date.now()
|
@@ -186,13 +227,13 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
186
227
|
const existingReservation = this.reservations.get(peerId)
|
187
228
|
|
188
229
|
if (existingReservation != null) {
|
189
|
-
if (getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) {
|
190
|
-
this.log('already have reservation
|
230
|
+
if (this.connectionManager.getConnections(peerId).map(conn => conn.id).includes(existingReservation.connection) && getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) {
|
231
|
+
this.log('already have relay reservation with %p but we are still connected and it does not expire soon', peerId)
|
191
232
|
return
|
192
233
|
}
|
193
234
|
|
194
|
-
|
195
|
-
this
|
235
|
+
this.log('already have relay reservation with %p but the original connection is no longer open', peerId)
|
236
|
+
await this.#removeReservation(peerId, existingReservation)
|
196
237
|
}
|
197
238
|
|
198
239
|
if (type === 'discovered' && [...this.reservations.values()].reduce((acc, curr) => {
|
@@ -202,7 +243,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
202
243
|
|
203
244
|
return acc
|
204
245
|
}, 0) >= this.maxDiscoveredRelays) {
|
205
|
-
this.log('already have enough discovered relays')
|
246
|
+
this.log.trace('already have enough discovered relays')
|
206
247
|
return
|
207
248
|
}
|
208
249
|
|
@@ -240,7 +281,8 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
240
281
|
this.reservations.set(peerId, {
|
241
282
|
timeout,
|
242
283
|
reservation,
|
243
|
-
type
|
284
|
+
type,
|
285
|
+
connection: connection.id
|
244
286
|
})
|
245
287
|
|
246
288
|
// ensure we don't close the connection to the relay
|
@@ -250,7 +292,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
250
292
|
value: 1,
|
251
293
|
ttl: expiration
|
252
294
|
},
|
253
|
-
[
|
295
|
+
[KEEP_ALIVE_TAG]: {
|
254
296
|
value: 1,
|
255
297
|
ttl: expiration
|
256
298
|
}
|
@@ -296,6 +338,14 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
296
338
|
return this.reservations.size
|
297
339
|
}
|
298
340
|
|
341
|
+
async cancelReservations (): Promise<void> {
|
342
|
+
await Promise.all(
|
343
|
+
[...this.reservations.entries()].map(async ([peerId, reservation]) => {
|
344
|
+
await this.#removeReservation(peerId, reservation)
|
345
|
+
})
|
346
|
+
)
|
347
|
+
}
|
348
|
+
|
299
349
|
async #createReservation (connection: Connection, options: AbortOptions): Promise<Reservation> {
|
300
350
|
options.signal?.throwIfAborted()
|
301
351
|
|
@@ -318,24 +368,31 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
318
368
|
}
|
319
369
|
}
|
320
370
|
|
321
|
-
if (response.status === Status.OK &&
|
371
|
+
if (response.status === Status.OK && response.reservation != null) {
|
322
372
|
// check that the returned relay has the relay address - this can be
|
323
373
|
// omitted when requesting a reservation from a go-libp2p relay we
|
324
374
|
// already have a reservation on
|
325
|
-
|
326
|
-
|
375
|
+
const addresses = new Set<string>()
|
376
|
+
addresses.add(connection.remoteAddr.toString())
|
327
377
|
|
328
378
|
for (const buf of response.reservation.addrs) {
|
329
|
-
|
330
|
-
|
331
|
-
|
379
|
+
let ma = multiaddr(buf)
|
380
|
+
|
381
|
+
if (ma.getPeerId() == null) {
|
382
|
+
ma = ma.encapsulate(`/p2p/${connection.remotePeer}`)
|
332
383
|
}
|
333
|
-
}
|
334
384
|
|
335
|
-
|
336
|
-
|
385
|
+
// TODO: workaround for https://github.com/libp2p/go-libp2p/issues/3003
|
386
|
+
ma = multiaddr(ma.toString().replace(
|
387
|
+
`/p2p/${connection.remotePeer}/p2p/${connection.remotePeer}`,
|
388
|
+
`/p2p/${connection.remotePeer}`
|
389
|
+
))
|
390
|
+
|
391
|
+
addresses.add(ma.toString())
|
337
392
|
}
|
338
393
|
|
394
|
+
response.reservation.addrs = [...addresses].map(str => multiaddr(str).bytes)
|
395
|
+
|
339
396
|
return response.reservation
|
340
397
|
}
|
341
398
|
|
@@ -348,18 +405,19 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
348
405
|
/**
|
349
406
|
* Remove listen relay
|
350
407
|
*/
|
351
|
-
#
|
352
|
-
|
353
|
-
|
354
|
-
if (existingReservation == null) {
|
355
|
-
return
|
356
|
-
}
|
357
|
-
|
358
|
-
this.log('connection to relay %p closed, removing reservation from local store', peerId)
|
359
|
-
|
360
|
-
clearTimeout(existingReservation.timeout)
|
408
|
+
async #removeReservation (peerId: PeerId, reservation: RelayEntry): Promise<void> {
|
409
|
+
this.log('removing relay reservation with %p from local store', peerId)
|
410
|
+
clearTimeout(reservation.timeout)
|
361
411
|
this.reservations.delete(peerId)
|
362
412
|
|
413
|
+
// untag the relay
|
414
|
+
await this.peerStore.merge(peerId, {
|
415
|
+
tags: {
|
416
|
+
[RELAY_TAG]: undefined,
|
417
|
+
[KEEP_ALIVE_TAG]: undefined
|
418
|
+
}
|
419
|
+
})
|
420
|
+
|
363
421
|
this.safeDispatchEvent('relay:removed', { detail: peerId })
|
364
422
|
|
365
423
|
if (this.reservations.size < this.maxDiscoveredRelays) {
|