@libp2p/circuit-relay-v2 2.1.4 → 2.1.5-4521cf1f7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
}
|