@libp2p/circuit-relay-v2 2.1.4 → 2.1.5-3bc9769b8
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.min.js +4 -4
- package/dist/src/constants.d.ts +0 -5
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +0 -5
- package/dist/src/constants.js.map +1 -1
- package/dist/src/errors.d.ts +22 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +22 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +16 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/pb/index.d.ts +12 -1
- package/dist/src/pb/index.d.ts.map +1 -1
- package/dist/src/pb/index.js +78 -2
- package/dist/src/pb/index.js.map +1 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +19 -10
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/server/reservation-store.d.ts +5 -9
- package/dist/src/server/reservation-store.d.ts.map +1 -1
- package/dist/src/server/reservation-store.js +32 -33
- package/dist/src/server/reservation-store.js.map +1 -1
- package/dist/src/server/reservation-voucher.d.ts +1 -1
- package/dist/src/transport/discovery.d.ts +1 -0
- package/dist/src/transport/discovery.d.ts.map +1 -1
- package/dist/src/transport/discovery.js +38 -8
- package/dist/src/transport/discovery.js.map +1 -1
- package/dist/src/transport/index.d.ts +0 -6
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/index.js.map +1 -1
- package/dist/src/transport/listener.d.ts +3 -0
- package/dist/src/transport/listener.d.ts.map +1 -1
- package/dist/src/transport/listener.js +57 -27
- package/dist/src/transport/listener.js.map +1 -1
- package/dist/src/transport/reservation-store.d.ts +37 -14
- package/dist/src/transport/reservation-store.d.ts.map +1 -1
- package/dist/src/transport/reservation-store.js +144 -74
- package/dist/src/transport/reservation-store.js.map +1 -1
- package/dist/src/transport/transport.d.ts +2 -13
- package/dist/src/transport/transport.d.ts.map +1 -1
- package/dist/src/transport/transport.js +30 -54
- package/dist/src/transport/transport.js.map +1 -1
- package/dist/src/utils.d.ts +10 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +22 -8
- package/dist/src/utils.js.map +1 -1
- package/package.json +14 -12
- package/src/constants.ts +0 -7
- package/src/errors.ts +25 -0
- package/src/index.ts +19 -1
- package/src/pb/index.proto +20 -1
- package/src/pb/index.ts +99 -3
- package/src/server/index.ts +21 -11
- package/src/server/reservation-store.ts +38 -42
- package/src/server/reservation-voucher.ts +2 -2
- package/src/transport/discovery.ts +47 -9
- package/src/transport/index.ts +0 -7
- package/src/transport/listener.ts +75 -35
- package/src/transport/reservation-store.ts +209 -95
- package/src/transport/transport.ts +34 -76
- package/src/utils.ts +29 -8
- package/dist/typedoc-urls.json +0 -23
package/src/server/index.ts
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import { publicKeyToProtobuf } from '@libp2p/crypto/keys'
|
1
2
|
import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
2
3
|
import { peerIdFromMultihash } from '@libp2p/peer-id'
|
3
4
|
import { RecordEnvelope } from '@libp2p/peer-record'
|
@@ -156,8 +157,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
156
157
|
runOnLimitedConnection: true
|
157
158
|
})
|
158
159
|
|
159
|
-
this.reservationStore.start()
|
160
|
-
|
161
160
|
this.started = true
|
162
161
|
}
|
163
162
|
|
@@ -165,7 +164,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
165
164
|
* Stop Relay service
|
166
165
|
*/
|
167
166
|
async stop (): Promise<void> {
|
168
|
-
this.reservationStore.
|
167
|
+
this.reservationStore.clear()
|
169
168
|
this.shutdownController.abort()
|
170
169
|
await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
|
171
170
|
|
@@ -290,16 +289,25 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
290
289
|
addrs.push(relayAddr.bytes)
|
291
290
|
}
|
292
291
|
|
293
|
-
const
|
292
|
+
const envelope = await RecordEnvelope.seal(new ReservationVoucherRecord({
|
294
293
|
peer: remotePeer,
|
295
294
|
relay: this.peerId,
|
296
|
-
expiration:
|
295
|
+
expiration: expire
|
297
296
|
}), this.privateKey)
|
298
297
|
|
299
298
|
return {
|
300
299
|
addrs,
|
301
300
|
expire,
|
302
|
-
voucher:
|
301
|
+
voucher: {
|
302
|
+
publicKey: publicKeyToProtobuf(envelope.publicKey),
|
303
|
+
payloadType: envelope.payloadType,
|
304
|
+
payload: {
|
305
|
+
peer: remotePeer.toMultihash().bytes,
|
306
|
+
relay: this.peerId.toMultihash().bytes,
|
307
|
+
expiration: expire
|
308
|
+
},
|
309
|
+
signature: envelope.signature
|
310
|
+
}
|
303
311
|
}
|
304
312
|
}
|
305
313
|
|
@@ -330,7 +338,9 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
330
338
|
return
|
331
339
|
}
|
332
340
|
|
333
|
-
|
341
|
+
const reservation = this.reservationStore.get(dstPeer)
|
342
|
+
|
343
|
+
if (reservation == null) {
|
334
344
|
this.log.error('hop connect denied for destination peer %p not having a reservation for %p with status %s', dstPeer, connection.remotePeer, Status.NO_RESERVATION)
|
335
345
|
await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION }, options)
|
336
346
|
return
|
@@ -350,7 +360,6 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
350
360
|
return
|
351
361
|
}
|
352
362
|
|
353
|
-
const limit = this.reservationStore.get(dstPeer)?.limit
|
354
363
|
const destinationConnection = connections[0]
|
355
364
|
|
356
365
|
const destinationStream = await this.stopHop({
|
@@ -361,7 +370,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
361
370
|
id: connection.remotePeer.toMultihash().bytes,
|
362
371
|
addrs: []
|
363
372
|
},
|
364
|
-
limit
|
373
|
+
limit: reservation?.limit
|
365
374
|
}
|
366
375
|
}, options)
|
367
376
|
|
@@ -374,13 +383,14 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
374
383
|
await hopstr.write({
|
375
384
|
type: HopMessage.Type.STATUS,
|
376
385
|
status: Status.OK,
|
377
|
-
limit
|
386
|
+
limit: reservation?.limit
|
378
387
|
}, options)
|
379
388
|
const sourceStream = stream.unwrap()
|
380
389
|
|
381
390
|
this.log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer)
|
391
|
+
|
382
392
|
// Short circuit the two streams to create the relayed connection
|
383
|
-
createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal,
|
393
|
+
createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, reservation, {
|
384
394
|
log: this.log
|
385
395
|
})
|
386
396
|
}
|
@@ -1,14 +1,16 @@
|
|
1
1
|
import { trackedPeerMap } from '@libp2p/peer-collections'
|
2
|
-
import {
|
2
|
+
import { retimeableSignal } from 'retimeable-signal'
|
3
|
+
import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
|
3
4
|
import { type Limit, Status } from '../pb/index.js'
|
4
5
|
import type { RelayReservation } from '../index.js'
|
5
|
-
import type { Metrics, PeerId
|
6
|
+
import type { ComponentLogger, Logger, Metrics, PeerId } from '@libp2p/interface'
|
6
7
|
import type { PeerMap } from '@libp2p/peer-collections'
|
7
8
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
8
9
|
|
9
10
|
export type ReservationStatus = Status.OK | Status.PERMISSION_DENIED | Status.RESERVATION_REFUSED
|
10
11
|
|
11
12
|
export interface ReservationStoreComponents {
|
13
|
+
logger: ComponentLogger
|
12
14
|
metrics?: Metrics
|
13
15
|
}
|
14
16
|
|
@@ -52,20 +54,18 @@ export interface ReservationStoreInit {
|
|
52
54
|
defaultDataLimit?: bigint
|
53
55
|
}
|
54
56
|
|
55
|
-
export class ReservationStore
|
57
|
+
export class ReservationStore {
|
56
58
|
public readonly reservations: PeerMap<RelayReservation>
|
57
|
-
private _started = false
|
58
|
-
private interval: any
|
59
59
|
private readonly maxReservations: number
|
60
|
-
private readonly reservationClearInterval: number
|
61
60
|
private readonly applyDefaultLimit: boolean
|
62
61
|
private readonly reservationTtl: number
|
63
62
|
private readonly defaultDurationLimit: number
|
64
63
|
private readonly defaultDataLimit: bigint
|
64
|
+
private readonly log: Logger
|
65
65
|
|
66
66
|
constructor (components: ReservationStoreComponents, init: ReservationStoreInit = {}) {
|
67
|
+
this.log = components.logger.forComponent('libp2p:circuit-relay:server:reservation-store')
|
67
68
|
this.maxReservations = init.maxReservations ?? DEFAULT_MAX_RESERVATION_STORE_SIZE
|
68
|
-
this.reservationClearInterval = init.reservationClearInterval ?? DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL
|
69
69
|
this.applyDefaultLimit = init.applyDefaultLimit !== false
|
70
70
|
this.reservationTtl = init.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL
|
71
71
|
this.defaultDurationLimit = init.defaultDurationLimit ?? DEFAULT_DURATION_LIMIT
|
@@ -77,59 +77,55 @@ export class ReservationStore implements Startable {
|
|
77
77
|
})
|
78
78
|
}
|
79
79
|
|
80
|
-
isStarted (): boolean {
|
81
|
-
return this._started
|
82
|
-
}
|
83
|
-
|
84
|
-
start (): void {
|
85
|
-
if (this._started) {
|
86
|
-
return
|
87
|
-
}
|
88
|
-
this._started = true
|
89
|
-
this.interval = setInterval(
|
90
|
-
() => {
|
91
|
-
const now = (new Date()).getTime()
|
92
|
-
this.reservations.forEach((r, k) => {
|
93
|
-
if (r.expire.getTime() < now) {
|
94
|
-
this.reservations.delete(k)
|
95
|
-
}
|
96
|
-
})
|
97
|
-
},
|
98
|
-
this.reservationClearInterval
|
99
|
-
)
|
100
|
-
}
|
101
|
-
|
102
|
-
stop (): void {
|
103
|
-
clearInterval(this.interval)
|
104
|
-
}
|
105
|
-
|
106
80
|
reserve (peer: PeerId, addr: Multiaddr, limit?: Limit): { status: ReservationStatus, expire?: number } {
|
107
|
-
|
81
|
+
let reservation = this.reservations.get(peer)
|
82
|
+
|
83
|
+
if (this.reservations.size >= this.maxReservations && reservation == null) {
|
108
84
|
return { status: Status.RESERVATION_REFUSED }
|
109
85
|
}
|
110
86
|
|
111
|
-
const
|
87
|
+
const expiry = new Date(Date.now() + this.reservationTtl)
|
112
88
|
let checkedLimit: Limit | undefined
|
113
89
|
|
114
90
|
if (this.applyDefaultLimit) {
|
115
|
-
checkedLimit = limit ?? {
|
91
|
+
checkedLimit = limit ?? {
|
92
|
+
data: this.defaultDataLimit,
|
93
|
+
duration: this.defaultDurationLimit
|
94
|
+
}
|
116
95
|
}
|
117
96
|
|
118
|
-
|
97
|
+
if (reservation != null) {
|
98
|
+
this.log('refreshing reservation for client %p', peer)
|
99
|
+
reservation.signal.reset(this.reservationTtl)
|
100
|
+
} else {
|
101
|
+
this.log('creating new reservation for client %p', peer)
|
102
|
+
reservation = {
|
103
|
+
addr,
|
104
|
+
expiry,
|
105
|
+
limit: checkedLimit,
|
106
|
+
signal: retimeableSignal(this.reservationTtl)
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
this.reservations.set(peer, reservation)
|
111
|
+
|
112
|
+
reservation.signal.addEventListener('abort', () => {
|
113
|
+
this.reservations.delete(peer)
|
114
|
+
})
|
119
115
|
|
120
116
|
// return expiry time in seconds
|
121
|
-
return { status: Status.OK, expire: Math.round(
|
117
|
+
return { status: Status.OK, expire: Math.round(expiry.getTime() / 1000) }
|
122
118
|
}
|
123
119
|
|
124
120
|
removeReservation (peer: PeerId): void {
|
125
121
|
this.reservations.delete(peer)
|
126
122
|
}
|
127
123
|
|
128
|
-
hasReservation (dst: PeerId): boolean {
|
129
|
-
return this.reservations.has(dst)
|
130
|
-
}
|
131
|
-
|
132
124
|
get (peer: PeerId): RelayReservation | undefined {
|
133
125
|
return this.reservations.get(peer)
|
134
126
|
}
|
127
|
+
|
128
|
+
clear (): void {
|
129
|
+
this.reservations.clear()
|
130
|
+
}
|
135
131
|
}
|
@@ -4,7 +4,7 @@ import type { PeerId, Record } from '@libp2p/interface'
|
|
4
4
|
export interface ReservationVoucherOptions {
|
5
5
|
relay: PeerId
|
6
6
|
peer: PeerId
|
7
|
-
expiration:
|
7
|
+
expiration: bigint
|
8
8
|
}
|
9
9
|
|
10
10
|
export class ReservationVoucherRecord implements Record {
|
@@ -13,7 +13,7 @@ export class ReservationVoucherRecord implements Record {
|
|
13
13
|
|
14
14
|
private readonly relay: PeerId
|
15
15
|
private readonly peer: PeerId
|
16
|
-
private readonly expiration:
|
16
|
+
private readonly expiration: bigint
|
17
17
|
|
18
18
|
constructor ({ relay, peer, expiration }: ReservationVoucherOptions) {
|
19
19
|
this.relay = relay
|
@@ -1,11 +1,11 @@
|
|
1
1
|
import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
2
2
|
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
3
3
|
import { anySignal } from 'any-signal'
|
4
|
-
import {
|
4
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
5
5
|
import {
|
6
6
|
RELAY_V2_HOP_CODEC
|
7
7
|
} from '../constants.js'
|
8
|
-
import type { ComponentLogger, Logger, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
|
8
|
+
import type { ComponentLogger, Logger, Peer, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
|
9
9
|
import type { ConnectionManager, RandomWalk, Registrar, TransportManager } from '@libp2p/interface-internal'
|
10
10
|
|
11
11
|
export interface RelayDiscoveryEvents {
|
@@ -40,6 +40,7 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
40
40
|
private readonly log: Logger
|
41
41
|
private discoveryController: AbortController
|
42
42
|
private readonly filter?: TopologyFilter
|
43
|
+
private queue?: PeerQueue
|
43
44
|
|
44
45
|
constructor (components: RelayDiscoveryComponents, init: RelayDiscoveryInit = {}) {
|
45
46
|
super()
|
@@ -66,7 +67,7 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
66
67
|
this.topologyId = await this.registrar.register(RELAY_V2_HOP_CODEC, {
|
67
68
|
filter: this.filter,
|
68
69
|
onConnect: (peerId) => {
|
69
|
-
this.log.trace('discovered relay %p', peerId)
|
70
|
+
this.log.trace('discovered relay %p queue (length: %d, active %d)', peerId, this.queue?.size, this.queue?.running)
|
70
71
|
this.safeDispatchEvent('relay:discover', { detail: peerId })
|
71
72
|
}
|
72
73
|
})
|
@@ -113,7 +114,23 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
113
114
|
}
|
114
115
|
],
|
115
116
|
orders: [
|
116
|
-
|
117
|
+
// randomise
|
118
|
+
() => Math.random() < 0.5 ? 1 : -1,
|
119
|
+
// prefer peers we've connected to in the past
|
120
|
+
(a, b) => {
|
121
|
+
const lastDialA = getLastDial(a)
|
122
|
+
const lastDialB = getLastDial(b)
|
123
|
+
|
124
|
+
if (lastDialA > lastDialB) {
|
125
|
+
return -1
|
126
|
+
}
|
127
|
+
|
128
|
+
if (lastDialB > lastDialA) {
|
129
|
+
return 1
|
130
|
+
}
|
131
|
+
|
132
|
+
return 0
|
133
|
+
}
|
117
134
|
]
|
118
135
|
}))
|
119
136
|
|
@@ -126,11 +143,12 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
126
143
|
|
127
144
|
// perform random walk and dial peers - after identify has run, the network
|
128
145
|
// topology will be notified of new relays
|
129
|
-
const queue = new PeerQueue({
|
146
|
+
const queue = this.queue = new PeerQueue({
|
130
147
|
concurrency: 5
|
131
148
|
})
|
132
149
|
|
133
150
|
this.log('start random walk')
|
151
|
+
|
134
152
|
for await (const peer of this.randomWalk.walk({ signal: this.discoveryController.signal })) {
|
135
153
|
this.log.trace('found random peer %p', peer.id)
|
136
154
|
|
@@ -155,12 +173,16 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
155
173
|
continue
|
156
174
|
}
|
157
175
|
|
158
|
-
|
176
|
+
if (queue.queued > 10) {
|
177
|
+
this.log.trace('wait for space in queue for %p', peer.id)
|
159
178
|
|
160
|
-
|
161
|
-
|
179
|
+
// pause the random walk until there is space in the queue
|
180
|
+
await queue.onSizeLessThan(10, {
|
181
|
+
signal: this.discoveryController.signal
|
182
|
+
})
|
183
|
+
}
|
162
184
|
|
163
|
-
this.log('adding random peer %p to dial queue (length: %d)', peer.id, queue.size)
|
185
|
+
this.log('adding random peer %p to dial queue (length: %d, active %d)', peer.id, queue.size, queue.running)
|
164
186
|
|
165
187
|
// dial the peer - this will cause identify to run and our topology to
|
166
188
|
// be notified and we'll attempt to create reservations
|
@@ -182,6 +204,8 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
182
204
|
})
|
183
205
|
}
|
184
206
|
|
207
|
+
this.log('stop random walk')
|
208
|
+
|
185
209
|
await queue.onIdle()
|
186
210
|
})
|
187
211
|
.catch(err => {
|
@@ -197,3 +221,17 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
197
221
|
this.discoveryController?.abort()
|
198
222
|
}
|
199
223
|
}
|
224
|
+
|
225
|
+
/**
|
226
|
+
* Returns the timestamp of the last time we connected to this peer, if we've
|
227
|
+
* not connected to them before return 0
|
228
|
+
*/
|
229
|
+
function getLastDial (peer: Peer): number {
|
230
|
+
const lastDial = peer.metadata.get('last-dial-success')
|
231
|
+
|
232
|
+
if (lastDial == null) {
|
233
|
+
return 0
|
234
|
+
}
|
235
|
+
|
236
|
+
return new Date(uint8ArrayToString(lastDial)).getTime()
|
237
|
+
}
|
package/src/transport/index.ts
CHANGED
@@ -17,13 +17,6 @@ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponent
|
|
17
17
|
* RelayConfig configures the circuit v2 relay transport.
|
18
18
|
*/
|
19
19
|
export interface CircuitRelayTransportInit extends ReservationStoreInit {
|
20
|
-
/**
|
21
|
-
* The number of peers running diable relays to search for and connect to
|
22
|
-
*
|
23
|
-
* @default 0
|
24
|
-
*/
|
25
|
-
discoverRelays?: number
|
26
|
-
|
27
20
|
/**
|
28
21
|
* An optional filter used to prevent duplicate attempts to reserve relay
|
29
22
|
* slots on the same peer
|
@@ -1,8 +1,10 @@
|
|
1
|
-
import { ListenError, TypedEventEmitter } from '@libp2p/interface'
|
2
|
-
import { PeerMap } from '@libp2p/peer-collections'
|
1
|
+
import { ListenError, TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
3
2
|
import { multiaddr } from '@multiformats/multiaddr'
|
4
|
-
import
|
5
|
-
import
|
3
|
+
import { DEFAULT_RESERVATION_COMPLETION_TIMEOUT } from '../constants.js'
|
4
|
+
import { CircuitListen, CircuitSearch } from '../utils.js'
|
5
|
+
import type { RelayDiscovery } from './discovery.js'
|
6
|
+
import type { RelayReservation, ReservationStore } from './reservation-store.js'
|
7
|
+
import type { ComponentLogger, Logger, Listener, ListenerEvents, PeerId } from '@libp2p/interface'
|
6
8
|
import type { ConnectionManager } from '@libp2p/interface-internal'
|
7
9
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
8
10
|
|
@@ -12,79 +14,117 @@ export interface CircuitRelayTransportListenerComponents {
|
|
12
14
|
logger: ComponentLogger
|
13
15
|
}
|
14
16
|
|
17
|
+
export interface CircuitRelayTransportListenerInit {
|
18
|
+
listenTimeout?: number
|
19
|
+
}
|
20
|
+
|
15
21
|
class CircuitRelayTransportListener extends TypedEventEmitter<ListenerEvents> implements Listener {
|
16
22
|
private readonly connectionManager: ConnectionManager
|
17
23
|
private readonly reservationStore: ReservationStore
|
18
|
-
private readonly
|
24
|
+
private readonly discovery?: RelayDiscovery
|
25
|
+
private listeningAddrs: Multiaddr[]
|
19
26
|
private readonly log: Logger
|
27
|
+
private readonly listenTimeout: number
|
28
|
+
private reservationId?: string
|
29
|
+
private relay?: PeerId
|
20
30
|
|
21
|
-
constructor (components: CircuitRelayTransportListenerComponents) {
|
31
|
+
constructor (components: CircuitRelayTransportListenerComponents, init: CircuitRelayTransportListenerInit = {}) {
|
22
32
|
super()
|
23
33
|
|
24
34
|
this.log = components.logger.forComponent('libp2p:circuit-relay:transport:listener')
|
25
35
|
this.connectionManager = components.connectionManager
|
26
36
|
this.reservationStore = components.relayStore
|
27
|
-
this.listeningAddrs =
|
37
|
+
this.listeningAddrs = []
|
38
|
+
this.listenTimeout = init.listenTimeout ?? DEFAULT_RESERVATION_COMPLETION_TIMEOUT
|
28
39
|
|
29
40
|
// remove listening addrs when a relay is removed
|
30
41
|
this.reservationStore.addEventListener('relay:removed', this._onRemoveRelayPeer)
|
42
|
+
this.reservationStore.addEventListener('relay:created-reservation', this._onAddRelayPeer)
|
31
43
|
}
|
32
44
|
|
33
|
-
_onRemoveRelayPeer = (evt: CustomEvent<
|
34
|
-
|
35
|
-
|
36
|
-
this.log('relay peer removed %p - had reservation', evt.detail, had)
|
45
|
+
_onRemoveRelayPeer = (evt: CustomEvent<RelayReservation>): void => {
|
46
|
+
this.log('relay removed %p our relay %p', evt.detail.relay, this.relay, this.relay?.equals(evt.detail.relay))
|
37
47
|
|
38
|
-
if (
|
48
|
+
if (this.relay?.equals(evt.detail.relay) !== true) {
|
39
49
|
return
|
40
50
|
}
|
41
51
|
|
42
|
-
this.
|
52
|
+
this.log('relay peer removed %p', evt.detail.relay)
|
53
|
+
|
54
|
+
this.listeningAddrs = []
|
43
55
|
|
44
56
|
// announce listen addresses change
|
45
57
|
this.safeDispatchEvent('listening')
|
46
58
|
}
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
const relayAddr = addr.decapsulate('/p2p-circuit')
|
53
|
-
const relayConn = await this.connectionManager.openConnection(relayAddr)
|
60
|
+
_onAddRelayPeer = (evt: CustomEvent<RelayReservation>): void => {
|
61
|
+
const {
|
62
|
+
relay, details
|
63
|
+
} = evt.detail
|
54
64
|
|
55
|
-
if (
|
56
|
-
this.log('making reservation on peer %p', relayConn.remotePeer)
|
57
|
-
// addRelay calls transportManager.listen which calls this listen method
|
58
|
-
await this.reservationStore.addRelay(relayConn.remotePeer, 'configured')
|
65
|
+
if (details.type === 'configured') {
|
59
66
|
return
|
60
67
|
}
|
61
68
|
|
62
|
-
|
63
|
-
|
64
|
-
if (reservation == null) {
|
65
|
-
throw new ListenError('Did not have reservation after making reservation')
|
66
|
-
}
|
67
|
-
|
68
|
-
if (this.listeningAddrs.has(relayConn.remotePeer)) {
|
69
|
-
this.log('already listening on relay %p', relayConn.remotePeer)
|
69
|
+
if (details.id !== this.reservationId) {
|
70
70
|
return
|
71
71
|
}
|
72
72
|
|
73
|
+
this.log('relay peer added %p', relay)
|
74
|
+
|
75
|
+
this.relay = relay
|
76
|
+
|
73
77
|
// add all addresses from the relay reservation
|
74
|
-
this.listeningAddrs.
|
78
|
+
this.listeningAddrs = details.reservation.addrs
|
75
79
|
.map(buf => multiaddr(buf).encapsulate('/p2p-circuit'))
|
76
|
-
)
|
77
80
|
|
81
|
+
// announce listen addresses change
|
78
82
|
this.safeDispatchEvent('listening')
|
79
83
|
}
|
80
84
|
|
85
|
+
async listen (addr: Multiaddr): Promise<void> {
|
86
|
+
this.log('listen on %a', addr)
|
87
|
+
|
88
|
+
if (CircuitSearch.exactMatch(addr)) {
|
89
|
+
// start relay discovery
|
90
|
+
this.reservationId = this.reservationStore.reserveRelay()
|
91
|
+
} else if (CircuitListen.exactMatch(addr)) {
|
92
|
+
const signal = AbortSignal.timeout(this.listenTimeout)
|
93
|
+
setMaxListeners(Infinity, signal)
|
94
|
+
|
95
|
+
// try to make a reservation on one particular relay
|
96
|
+
// remove the circuit part to get the peer id of the relay
|
97
|
+
const relayAddr = addr.decapsulate('/p2p-circuit')
|
98
|
+
const relayConn = await this.connectionManager.openConnection(relayAddr, {
|
99
|
+
signal
|
100
|
+
})
|
101
|
+
|
102
|
+
if (!this.reservationStore.hasReservation(relayConn.remotePeer)) {
|
103
|
+
this.log('making reservation on peer %p', relayConn.remotePeer)
|
104
|
+
const reservation = await this.reservationStore.addRelay(relayConn.remotePeer, 'configured')
|
105
|
+
this.log('made reservation on peer %p', relayConn.remotePeer)
|
106
|
+
|
107
|
+
this.relay = reservation.relay
|
108
|
+
|
109
|
+
// add all addresses from the relay reservation
|
110
|
+
this.listeningAddrs = reservation.details.reservation.addrs
|
111
|
+
.map(buf => multiaddr(buf).encapsulate('/p2p-circuit'))
|
112
|
+
|
113
|
+
// if that succeeded announce listen addresses change
|
114
|
+
this.safeDispatchEvent('listening')
|
115
|
+
}
|
116
|
+
} else {
|
117
|
+
throw new ListenError(`Could not listen on p2p-circuit address "${addr}"`)
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
81
121
|
getAddrs (): Multiaddr[] {
|
82
122
|
return [...this.listeningAddrs.values()].flat()
|
83
123
|
}
|
84
124
|
|
85
125
|
async close (): Promise<void> {
|
86
|
-
|
87
|
-
this.listeningAddrs
|
126
|
+
this.reservationStore.cancelReservations()
|
127
|
+
this.listeningAddrs = []
|
88
128
|
|
89
129
|
// remove listener
|
90
130
|
this.reservationStore.removeEventListener('relay:removed', this._onRemoveRelayPeer)
|