@libp2p/circuit-relay-v2 2.1.4 → 2.1.5-e6b4158c6
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 +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 +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/reservation-store.d.ts.map +1 -1
- package/dist/src/transport/reservation-store.js +36 -11
- package/dist/src/transport/reservation-store.js.map +1 -1
- package/dist/src/transport/transport.d.ts.map +1 -1
- package/dist/src/transport/transport.js +11 -8
- package/dist/src/transport/transport.js.map +1 -1
- package/dist/src/utils.d.ts +7 -1
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +19 -8
- package/dist/src/utils.js.map +1 -1
- package/package.json +12 -11
- package/src/constants.ts +0 -5
- 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/reservation-store.ts +47 -15
- package/src/transport/transport.ts +12 -8
- package/src/utils.ts +21 -8
- package/dist/typedoc-urls.json +0 -23
package/src/pb/index.ts
CHANGED
@@ -318,7 +318,7 @@ export namespace Peer {
|
|
318
318
|
export interface Reservation {
|
319
319
|
expire: bigint
|
320
320
|
addrs: Uint8Array[]
|
321
|
-
voucher?:
|
321
|
+
voucher?: Envelope
|
322
322
|
}
|
323
323
|
|
324
324
|
export namespace Reservation {
|
@@ -345,7 +345,7 @@ export namespace Reservation {
|
|
345
345
|
|
346
346
|
if (obj.voucher != null) {
|
347
347
|
w.uint32(26)
|
348
|
-
|
348
|
+
Envelope.codec().encode(obj.voucher, w)
|
349
349
|
}
|
350
350
|
|
351
351
|
if (opts.lengthDelimited !== false) {
|
@@ -376,7 +376,9 @@ export namespace Reservation {
|
|
376
376
|
break
|
377
377
|
}
|
378
378
|
case 3: {
|
379
|
-
obj.voucher = reader.
|
379
|
+
obj.voucher = Envelope.codec().decode(reader, reader.uint32(), {
|
380
|
+
limits: opts.limits?.voucher
|
381
|
+
})
|
380
382
|
break
|
381
383
|
}
|
382
384
|
default: {
|
@@ -580,3 +582,97 @@ export namespace ReservationVoucher {
|
|
580
582
|
return decodeMessage(buf, ReservationVoucher.codec(), opts)
|
581
583
|
}
|
582
584
|
}
|
585
|
+
|
586
|
+
export interface Envelope {
|
587
|
+
publicKey: Uint8Array
|
588
|
+
payloadType: Uint8Array
|
589
|
+
payload?: ReservationVoucher
|
590
|
+
signature: Uint8Array
|
591
|
+
}
|
592
|
+
|
593
|
+
export namespace Envelope {
|
594
|
+
let _codec: Codec<Envelope>
|
595
|
+
|
596
|
+
export const codec = (): Codec<Envelope> => {
|
597
|
+
if (_codec == null) {
|
598
|
+
_codec = message<Envelope>((obj, w, opts = {}) => {
|
599
|
+
if (opts.lengthDelimited !== false) {
|
600
|
+
w.fork()
|
601
|
+
}
|
602
|
+
|
603
|
+
if ((obj.publicKey != null && obj.publicKey.byteLength > 0)) {
|
604
|
+
w.uint32(10)
|
605
|
+
w.bytes(obj.publicKey)
|
606
|
+
}
|
607
|
+
|
608
|
+
if ((obj.payloadType != null && obj.payloadType.byteLength > 0)) {
|
609
|
+
w.uint32(18)
|
610
|
+
w.bytes(obj.payloadType)
|
611
|
+
}
|
612
|
+
|
613
|
+
if (obj.payload != null) {
|
614
|
+
w.uint32(26)
|
615
|
+
ReservationVoucher.codec().encode(obj.payload, w)
|
616
|
+
}
|
617
|
+
|
618
|
+
if ((obj.signature != null && obj.signature.byteLength > 0)) {
|
619
|
+
w.uint32(42)
|
620
|
+
w.bytes(obj.signature)
|
621
|
+
}
|
622
|
+
|
623
|
+
if (opts.lengthDelimited !== false) {
|
624
|
+
w.ldelim()
|
625
|
+
}
|
626
|
+
}, (reader, length, opts = {}) => {
|
627
|
+
const obj: any = {
|
628
|
+
publicKey: uint8ArrayAlloc(0),
|
629
|
+
payloadType: uint8ArrayAlloc(0),
|
630
|
+
signature: uint8ArrayAlloc(0)
|
631
|
+
}
|
632
|
+
|
633
|
+
const end = length == null ? reader.len : reader.pos + length
|
634
|
+
|
635
|
+
while (reader.pos < end) {
|
636
|
+
const tag = reader.uint32()
|
637
|
+
|
638
|
+
switch (tag >>> 3) {
|
639
|
+
case 1: {
|
640
|
+
obj.publicKey = reader.bytes()
|
641
|
+
break
|
642
|
+
}
|
643
|
+
case 2: {
|
644
|
+
obj.payloadType = reader.bytes()
|
645
|
+
break
|
646
|
+
}
|
647
|
+
case 3: {
|
648
|
+
obj.payload = ReservationVoucher.codec().decode(reader, reader.uint32(), {
|
649
|
+
limits: opts.limits?.payload
|
650
|
+
})
|
651
|
+
break
|
652
|
+
}
|
653
|
+
case 5: {
|
654
|
+
obj.signature = reader.bytes()
|
655
|
+
break
|
656
|
+
}
|
657
|
+
default: {
|
658
|
+
reader.skipType(tag & 7)
|
659
|
+
break
|
660
|
+
}
|
661
|
+
}
|
662
|
+
}
|
663
|
+
|
664
|
+
return obj
|
665
|
+
})
|
666
|
+
}
|
667
|
+
|
668
|
+
return _codec
|
669
|
+
}
|
670
|
+
|
671
|
+
export const encode = (obj: Partial<Envelope>): Uint8Array => {
|
672
|
+
return encodeMessage(obj, Envelope.codec())
|
673
|
+
}
|
674
|
+
|
675
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Envelope>): Envelope => {
|
676
|
+
return decodeMessage(buf, Envelope.codec(), opts)
|
677
|
+
}
|
678
|
+
}
|
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
|
@@ -227,12 +227,23 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
227
227
|
const existingReservation = this.reservations.get(peerId)
|
228
228
|
|
229
229
|
if (existingReservation != null) {
|
230
|
-
|
230
|
+
const connections = this.connectionManager.getConnections(peerId)
|
231
|
+
let connected = false
|
232
|
+
|
233
|
+
if (connections.length === 0) {
|
234
|
+
this.log('already have relay reservation with %p but we are no longer connected', peerId)
|
235
|
+
}
|
236
|
+
|
237
|
+
if (connections.map(conn => conn.id).includes(existingReservation.connection)) {
|
238
|
+
this.log('already have relay reservation with %p and the original connection is still open', peerId)
|
239
|
+
connected = true
|
240
|
+
}
|
241
|
+
|
242
|
+
if (connected && getExpirationMilliseconds(existingReservation.reservation.expire) > REFRESH_WINDOW) {
|
231
243
|
this.log('already have relay reservation with %p but we are still connected and it does not expire soon', peerId)
|
232
244
|
return
|
233
245
|
}
|
234
246
|
|
235
|
-
this.log('already have relay reservation with %p but the original connection is no longer open', peerId)
|
236
247
|
await this.#removeReservation(peerId, existingReservation)
|
237
248
|
}
|
238
249
|
|
@@ -263,18 +274,33 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
263
274
|
signal
|
264
275
|
})
|
265
276
|
|
266
|
-
this.log('created reservation on relay peer %p', peerId)
|
267
|
-
|
268
277
|
const expiration = getExpirationMilliseconds(reservation.expire)
|
269
278
|
|
279
|
+
this.log('created reservation on relay peer %p, expiry date is %s', peerId, new Date(Date.now() + expiration).toString())
|
280
|
+
|
270
281
|
// sets a lower bound on the timeout, and also don't let it go over
|
271
282
|
// 2^31 - 1 (setTimeout will only accept signed 32 bit integers)
|
272
283
|
const timeoutDuration = Math.min(Math.max(expiration - REFRESH_TIMEOUT, REFRESH_TIMEOUT_MIN), Math.pow(2, 31) - 1)
|
273
284
|
|
274
285
|
const timeout = setTimeout(() => {
|
275
|
-
this.
|
276
|
-
|
277
|
-
|
286
|
+
this.log('refresh reservation to relay %p', peerId)
|
287
|
+
|
288
|
+
this.addRelay(peerId, type)
|
289
|
+
.catch(async err => {
|
290
|
+
this.log.error('could not refresh reservation to relay %p - %e', peerId, err)
|
291
|
+
|
292
|
+
const reservation = this.reservations.get(peerId)
|
293
|
+
|
294
|
+
if (reservation == null) {
|
295
|
+
this.log.error('did not have reservation after refreshing reservation failed %p', peerId)
|
296
|
+
return
|
297
|
+
}
|
298
|
+
|
299
|
+
await this.#removeReservation(peerId, reservation)
|
300
|
+
})
|
301
|
+
.catch(err => {
|
302
|
+
this.log.error('could not remove expired reservation to relay %p - %e', peerId, err)
|
303
|
+
})
|
278
304
|
}, timeoutDuration)
|
279
305
|
|
280
306
|
// we've managed to create a reservation successfully
|
@@ -300,7 +326,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
300
326
|
})
|
301
327
|
|
302
328
|
// listen on multiaddr that only the circuit transport is listening for
|
303
|
-
await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit`)])
|
329
|
+
await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit/p2p/${this.peerId.toString()}`)])
|
304
330
|
|
305
331
|
this.safeDispatchEvent('relay:created-reservation', {
|
306
332
|
detail: peerId
|
@@ -308,18 +334,19 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
308
334
|
} catch (err) {
|
309
335
|
this.log.error('could not reserve slot on %p after %dms', peerId, Date.now() - start, err)
|
310
336
|
|
337
|
+
// don't try this peer again
|
338
|
+
this.relayFilter.add(peerId.toMultihash().bytes)
|
339
|
+
|
311
340
|
// cancel the renewal timeout if it's been set
|
312
341
|
const reservation = this.reservations.get(peerId)
|
313
342
|
|
343
|
+
// if listening failed, remove the reservation
|
314
344
|
if (reservation != null) {
|
315
|
-
|
345
|
+
this.#removeReservation(peerId, reservation)
|
346
|
+
.catch(err => {
|
347
|
+
this.log.error('could not remove reservation on %p after reserving slot failed - %e', peerId, err)
|
348
|
+
})
|
316
349
|
}
|
317
|
-
|
318
|
-
// if listening failed, remove the reservation
|
319
|
-
this.reservations.delete(peerId)
|
320
|
-
|
321
|
-
// don't try this peer again
|
322
|
-
this.relayFilter.add(peerId.toMultihash().bytes)
|
323
350
|
}
|
324
351
|
}, {
|
325
352
|
peerId
|
@@ -406,6 +433,11 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
406
433
|
* Remove listen relay
|
407
434
|
*/
|
408
435
|
async #removeReservation (peerId: PeerId, reservation: RelayEntry): Promise<void> {
|
436
|
+
if (!this.reservations.has(peerId)) {
|
437
|
+
this.log('not removing relay reservation with %p from local store as we do not have a reservation with this peer', peerId)
|
438
|
+
return
|
439
|
+
}
|
440
|
+
|
409
441
|
this.log('removing relay reservation with %p from local store', peerId)
|
410
442
|
clearTimeout(reservation.timeout)
|
411
443
|
this.reservations.delete(peerId)
|
@@ -2,14 +2,14 @@ import { DialError, InvalidMessageError, serviceCapabilities, serviceDependencie
|
|
2
2
|
import { peerFilter } from '@libp2p/peer-collections'
|
3
3
|
import { peerIdFromMultihash, peerIdFromString } from '@libp2p/peer-id'
|
4
4
|
import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
|
5
|
-
import * as mafmt from '@multiformats/mafmt'
|
6
5
|
import { multiaddr } from '@multiformats/multiaddr'
|
6
|
+
import { Circuit } from '@multiformats/multiaddr-matcher'
|
7
7
|
import { pbStream } from 'it-protobuf-stream'
|
8
8
|
import * as Digest from 'multiformats/hashes/digest'
|
9
9
|
import { CustomProgressEvent } from 'progress-events'
|
10
10
|
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'
|
11
11
|
import { StopMessage, HopMessage, Status } from '../pb/index.js'
|
12
|
-
import { LimitTracker } from '../utils.js'
|
12
|
+
import { CircuitListen, LimitTracker } from '../utils.js'
|
13
13
|
import { RelayDiscovery } from './discovery.js'
|
14
14
|
import { createListener } from './listener.js'
|
15
15
|
import { ReservationStore } from './reservation-store.js'
|
@@ -184,9 +184,9 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
|
|
184
184
|
const destinationId = destinationAddr.getPeerId()
|
185
185
|
|
186
186
|
if (relayId == null || destinationId == null) {
|
187
|
-
const errMsg = `
|
188
|
-
this.log.error(errMsg)
|
189
|
-
throw new DialError(errMsg)
|
187
|
+
const errMsg = `ircuit relay dial to ${ma.toString()} failed as address did not have both relay and destination PeerIDs`
|
188
|
+
this.log.error(`c${errMsg}`)
|
189
|
+
throw new DialError(`C${errMsg}`)
|
190
190
|
}
|
191
191
|
|
192
192
|
const relayPeer = peerIdFromString(relayId)
|
@@ -281,7 +281,7 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
|
|
281
281
|
onProgress
|
282
282
|
})
|
283
283
|
} catch (err: any) {
|
284
|
-
this.log.error(`
|
284
|
+
this.log.error(`circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err)
|
285
285
|
disconnectOnFailure && await connection.close()
|
286
286
|
throw err
|
287
287
|
}
|
@@ -305,7 +305,7 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
|
|
305
305
|
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
|
306
306
|
|
307
307
|
return multiaddrs.filter((ma) => {
|
308
|
-
return
|
308
|
+
return CircuitListen.exactMatch(ma)
|
309
309
|
})
|
310
310
|
}
|
311
311
|
|
@@ -313,7 +313,11 @@ export class CircuitRelayTransport implements Transport<CircuitRelayDialEvents>
|
|
313
313
|
* Filter check for all Multiaddrs that this transport can dial
|
314
314
|
*/
|
315
315
|
dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
|
316
|
-
|
316
|
+
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
|
317
|
+
|
318
|
+
return multiaddrs.filter((ma) => {
|
319
|
+
return Circuit.exactMatch(ma)
|
320
|
+
})
|
317
321
|
}
|
318
322
|
|
319
323
|
/**
|
package/src/utils.ts
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
import { P2P } from '@multiformats/multiaddr-matcher'
|
2
|
+
import { fmt, peerId, literal, optional, and } from '@multiformats/multiaddr-matcher/utils'
|
1
3
|
import { anySignal } from 'any-signal'
|
2
4
|
import { CID } from 'multiformats/cid'
|
3
5
|
import { sha256 } from 'multiformats/hashes/sha2'
|
4
6
|
import { DurationLimitError, TransferLimitError } from './errors.js'
|
7
|
+
import type { RelayReservation } from './index.js'
|
5
8
|
import type { Limit } from './pb/index.js'
|
6
9
|
import type { ConnectionLimits, LoggerOptions, Stream } from '@libp2p/interface'
|
7
10
|
import type { Source } from 'it-stream-types'
|
@@ -34,16 +37,18 @@ async function * countStreamBytes (source: Source<Uint8Array | Uint8ArrayList>,
|
|
34
37
|
}
|
35
38
|
}
|
36
39
|
|
37
|
-
export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal,
|
40
|
+
export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: AbortSignal, reservation: RelayReservation, options: LoggerOptions): void {
|
38
41
|
function abortStreams (err: Error): void {
|
39
42
|
src.abort(err)
|
40
43
|
dst.abort(err)
|
41
44
|
}
|
42
45
|
|
43
|
-
|
46
|
+
// combine shutdown signal and reservation expiry signal
|
47
|
+
const signals = [abortSignal, reservation.signal]
|
44
48
|
|
45
|
-
if (limit?.duration != null) {
|
46
|
-
|
49
|
+
if (reservation.limit?.duration != null) {
|
50
|
+
options.log('limiting relayed connection duration to %dms', reservation.limit.duration)
|
51
|
+
signals.push(AbortSignal.timeout(reservation.limit.duration))
|
47
52
|
}
|
48
53
|
|
49
54
|
const signal = anySignal(signals)
|
@@ -53,15 +58,16 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort
|
|
53
58
|
|
54
59
|
let dataLimit: { remaining: bigint } | undefined
|
55
60
|
|
56
|
-
if (limit?.data != null) {
|
61
|
+
if (reservation.limit?.data != null) {
|
57
62
|
dataLimit = {
|
58
|
-
remaining: limit.data
|
63
|
+
remaining: reservation.limit.data
|
59
64
|
}
|
60
65
|
}
|
61
66
|
|
62
67
|
queueMicrotask(() => {
|
63
68
|
const onAbort = (): void => {
|
64
|
-
|
69
|
+
options.log('relayed connection reached time limit')
|
70
|
+
dst.abort(new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`))
|
65
71
|
}
|
66
72
|
|
67
73
|
signal.addEventListener('abort', onAbort, { once: true })
|
@@ -83,7 +89,8 @@ export function createLimitedRelay (src: Stream, dst: Stream, abortSignal: Abort
|
|
83
89
|
|
84
90
|
queueMicrotask(() => {
|
85
91
|
const onAbort = (): void => {
|
86
|
-
|
92
|
+
options.log('relayed connection reached time limit')
|
93
|
+
src.abort(new DurationLimitError(`duration limit of ${reservation.limit?.duration} ms exceeded`))
|
87
94
|
}
|
88
95
|
|
89
96
|
signal.addEventListener('abort', onAbort, { once: true })
|
@@ -185,3 +192,9 @@ export class LimitTracker {
|
|
185
192
|
return output
|
186
193
|
}
|
187
194
|
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* A custom matcher that makes the `/p2p/peer-id` part of circuit relay
|
198
|
+
* addresses optional
|
199
|
+
*/
|
200
|
+
export const CircuitListen = fmt(and(P2P.matchers[0], literal('p2p-circuit'), optional(peerId())))
|
package/dist/typedoc-urls.json
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"codec": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.Limit.codec.html",
|
3
|
-
"decode": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.Limit.decode.html",
|
4
|
-
"encode": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.Limit.encode.html",
|
5
|
-
"CircuitRelayServerComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServerComponents.html",
|
6
|
-
"CircuitRelayServerInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServerInit.html",
|
7
|
-
"CircuitRelayService": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayService.html",
|
8
|
-
".:CircuitRelayService": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayService.html",
|
9
|
-
"CircuitRelayServiceEvents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServiceEvents.html",
|
10
|
-
".:CircuitRelayServiceEvents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayServiceEvents.html",
|
11
|
-
"CircuitRelayTransportComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayTransportComponents.html",
|
12
|
-
"CircuitRelayTransportInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.CircuitRelayTransportInit.html",
|
13
|
-
"Limit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.Limit-1.html",
|
14
|
-
"RelayDiscoveryComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayDiscoveryComponents.html",
|
15
|
-
"RelayReservation": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayReservation.html",
|
16
|
-
".:RelayReservation": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.RelayReservation.html",
|
17
|
-
"ServerReservationStoreInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.ServerReservationStoreInit.html",
|
18
|
-
"TransportReservationStoreInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_circuit_relay_v2.TransportReservationStoreInit.html",
|
19
|
-
"RELAY_V2_HOP_CODEC": "https://libp2p.github.io/js-libp2p/variables/_libp2p_circuit_relay_v2.RELAY_V2_HOP_CODEC.html",
|
20
|
-
"RELAY_V2_STOP_CODEC": "https://libp2p.github.io/js-libp2p/variables/_libp2p_circuit_relay_v2.RELAY_V2_STOP_CODEC.html",
|
21
|
-
"circuitRelayServer": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.circuitRelayServer.html",
|
22
|
-
"circuitRelayTransport": "https://libp2p.github.io/js-libp2p/functions/_libp2p_circuit_relay_v2.circuitRelayTransport.html"
|
23
|
-
}
|