libp2p 0.42.2 → 0.43.0
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/README.md +4 -4
- package/dist/index.min.js +29 -29
- package/dist/src/address-manager/index.d.ts +1 -10
- package/dist/src/address-manager/index.d.ts.map +1 -1
- package/dist/src/address-manager/index.js +46 -29
- package/dist/src/address-manager/index.js.map +1 -1
- package/dist/src/autonat/constants.d.ts +4 -0
- package/dist/src/autonat/constants.d.ts.map +1 -0
- package/dist/src/autonat/constants.js +4 -0
- package/dist/src/autonat/constants.js.map +1 -0
- package/dist/src/autonat/index.d.ts +64 -0
- package/dist/src/autonat/index.d.ts.map +1 -0
- package/dist/src/autonat/index.js +417 -0
- package/dist/src/autonat/index.js.map +1 -0
- package/dist/src/autonat/pb/index.d.ts +57 -0
- package/dist/src/autonat/pb/index.d.ts.map +1 -0
- package/dist/src/autonat/pb/index.js +250 -0
- package/dist/src/autonat/pb/index.js.map +1 -0
- package/dist/src/circuit/constants.d.ts +31 -6
- package/dist/src/circuit/constants.d.ts.map +1 -1
- package/dist/src/circuit/constants.js +37 -7
- package/dist/src/circuit/constants.js.map +1 -1
- package/dist/src/circuit/index.d.ts +16 -61
- package/dist/src/circuit/index.d.ts.map +1 -1
- package/dist/src/circuit/index.js +2 -68
- package/dist/src/circuit/index.js.map +1 -1
- package/dist/src/circuit/pb/index.d.ts +83 -43
- package/dist/src/circuit/pb/index.d.ts.map +1 -1
- package/dist/src/circuit/pb/index.js +363 -127
- package/dist/src/circuit/pb/index.js.map +1 -1
- package/dist/src/circuit/server/advert-service.d.ts +43 -0
- package/dist/src/circuit/server/advert-service.d.ts.map +1 -0
- package/dist/src/circuit/server/advert-service.js +69 -0
- package/dist/src/circuit/server/advert-service.js.map +1 -0
- package/dist/src/circuit/server/index.d.ts +60 -0
- package/dist/src/circuit/server/index.d.ts.map +1 -0
- package/dist/src/circuit/server/index.js +287 -0
- package/dist/src/circuit/server/index.js.map +1 -0
- package/dist/src/circuit/server/reservation-store.d.ts +49 -0
- package/dist/src/circuit/server/reservation-store.d.ts.map +1 -0
- package/dist/src/circuit/server/reservation-store.js +57 -0
- package/dist/src/circuit/server/reservation-store.js.map +1 -0
- package/dist/src/circuit/server/reservation-voucher.d.ts +18 -0
- package/dist/src/circuit/server/reservation-voucher.d.ts.map +1 -0
- package/dist/src/circuit/server/reservation-voucher.js +33 -0
- package/dist/src/circuit/server/reservation-voucher.js.map +1 -0
- package/dist/src/circuit/transport/discovery.d.ts +45 -0
- package/dist/src/circuit/transport/discovery.d.ts.map +1 -0
- package/dist/src/circuit/transport/discovery.js +82 -0
- package/dist/src/circuit/transport/discovery.js.map +1 -0
- package/dist/src/circuit/transport/index.d.ts +33 -0
- package/dist/src/circuit/transport/index.d.ts.map +1 -0
- package/dist/src/circuit/transport/index.js +225 -0
- package/dist/src/circuit/transport/index.js.map +1 -0
- package/dist/src/circuit/transport/listener.d.ts +9 -0
- package/dist/src/circuit/transport/listener.d.ts.map +1 -0
- package/dist/src/circuit/transport/listener.js +76 -0
- package/dist/src/circuit/transport/listener.js.map +1 -0
- package/dist/src/circuit/transport/reservation-store.d.ts +60 -0
- package/dist/src/circuit/transport/reservation-store.d.ts.map +1 -0
- package/dist/src/circuit/transport/reservation-store.js +165 -0
- package/dist/src/circuit/transport/reservation-store.js.map +1 -0
- package/dist/src/circuit/utils.d.ts +7 -0
- package/dist/src/circuit/utils.d.ts.map +1 -1
- package/dist/src/circuit/utils.js +87 -0
- package/dist/src/circuit/utils.js.map +1 -1
- package/dist/src/components.d.ts +2 -1
- package/dist/src/components.d.ts.map +1 -1
- package/dist/src/components.js +13 -13
- package/dist/src/components.js.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +21 -25
- package/dist/src/config.js.map +1 -1
- package/dist/src/connection/index.d.ts +2 -2
- package/dist/src/connection/index.d.ts.map +1 -1
- package/dist/src/connection/index.js +4 -4
- package/dist/src/connection/index.js.map +1 -1
- package/dist/src/connection-manager/auto-dialler.d.ts +0 -4
- package/dist/src/connection-manager/auto-dialler.d.ts.map +1 -1
- package/dist/src/connection-manager/auto-dialler.js +18 -18
- package/dist/src/connection-manager/auto-dialler.js.map +1 -1
- package/dist/src/connection-manager/dialer/dial-request.d.ts.map +1 -1
- package/dist/src/connection-manager/dialer/dial-request.js +13 -6
- package/dist/src/connection-manager/dialer/dial-request.js.map +1 -1
- package/dist/src/connection-manager/dialer/index.d.ts +10 -1
- package/dist/src/connection-manager/dialer/index.d.ts.map +1 -1
- package/dist/src/connection-manager/dialer/index.js +24 -9
- package/dist/src/connection-manager/dialer/index.js.map +1 -1
- package/dist/src/connection-manager/index.d.ts +1 -4
- package/dist/src/connection-manager/index.d.ts.map +1 -1
- package/dist/src/connection-manager/index.js +8 -5
- package/dist/src/connection-manager/index.js.map +1 -1
- package/dist/src/connection-manager/latency-monitor.d.ts.map +1 -1
- package/dist/src/connection-manager/latency-monitor.js +1 -1
- package/dist/src/connection-manager/latency-monitor.js.map +1 -1
- package/dist/src/connection-manager/visibility-change-emitter.d.ts.map +1 -1
- package/dist/src/content-routing/index.d.ts +6 -4
- package/dist/src/content-routing/index.d.ts.map +1 -1
- package/dist/src/content-routing/index.js +9 -9
- package/dist/src/content-routing/index.js.map +1 -1
- package/dist/src/content-routing/utils.d.ts +3 -3
- package/dist/src/content-routing/utils.d.ts.map +1 -1
- package/dist/src/content-routing/utils.js +2 -2
- package/dist/src/content-routing/utils.js.map +1 -1
- package/dist/src/dht/dht-content-routing.d.ts +2 -1
- package/dist/src/dht/dht-content-routing.d.ts.map +1 -1
- package/dist/src/dht/dht-content-routing.js +2 -2
- package/dist/src/dht/dht-content-routing.js.map +1 -1
- package/dist/src/dht/dht-peer-routing.d.ts +3 -2
- package/dist/src/dht/dht-peer-routing.d.ts.map +1 -1
- package/dist/src/dht/dht-peer-routing.js +2 -2
- package/dist/src/dht/dht-peer-routing.js.map +1 -1
- package/dist/src/dht/dummy-dht.d.ts +1 -1
- package/dist/src/dht/dummy-dht.d.ts.map +1 -1
- package/dist/src/dht/dummy-dht.js +12 -12
- package/dist/src/dht/dummy-dht.js.map +1 -1
- package/dist/src/errors.d.ts +2 -1
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +1 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/fetch/index.d.ts.map +1 -1
- package/dist/src/fetch/index.js +6 -6
- package/dist/src/fetch/index.js.map +1 -1
- package/dist/src/fetch/pb/proto.d.ts +3 -3
- package/dist/src/fetch/pb/proto.d.ts.map +1 -1
- package/dist/src/fetch/pb/proto.js +4 -3
- package/dist/src/fetch/pb/proto.js.map +1 -1
- package/dist/src/get-peer.js +2 -2
- package/dist/src/get-peer.js.map +1 -1
- package/dist/src/identify/index.d.ts +3 -1
- package/dist/src/identify/index.d.ts.map +1 -1
- package/dist/src/identify/index.js +19 -14
- package/dist/src/identify/index.js.map +1 -1
- package/dist/src/identify/pb/message.d.ts +2 -2
- package/dist/src/identify/pb/message.d.ts.map +1 -1
- package/dist/src/identify/pb/message.js +1 -0
- package/dist/src/identify/pb/message.js.map +1 -1
- package/dist/src/index.d.ts +14 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/insecure/index.d.ts.map +1 -1
- package/dist/src/insecure/index.js.map +1 -1
- package/dist/src/insecure/pb/proto.d.ts +3 -3
- package/dist/src/insecure/pb/proto.d.ts.map +1 -1
- package/dist/src/insecure/pb/proto.js +4 -5
- package/dist/src/insecure/pb/proto.js.map +1 -1
- package/dist/src/libp2p.d.ts +7 -3
- package/dist/src/libp2p.d.ts.map +1 -1
- package/dist/src/libp2p.js +35 -35
- package/dist/src/libp2p.js.map +1 -1
- package/dist/src/nat-manager.d.ts.map +1 -1
- package/dist/src/nat-manager.js +5 -3
- package/dist/src/nat-manager.js.map +1 -1
- package/dist/src/peer-record-updater.d.ts.map +1 -1
- package/dist/src/peer-routing.d.ts.map +1 -1
- package/dist/src/peer-routing.js +5 -5
- package/dist/src/peer-routing.js.map +1 -1
- package/dist/src/ping/index.d.ts.map +1 -1
- package/dist/src/ping/index.js +2 -2
- package/dist/src/ping/index.js.map +1 -1
- package/dist/src/pnet/crypto.d.ts +1 -1
- package/dist/src/pnet/crypto.d.ts.map +1 -1
- package/dist/src/pnet/crypto.js +1 -1
- package/dist/src/pnet/crypto.js.map +1 -1
- package/dist/src/pnet/index.js +3 -3
- package/dist/src/pnet/index.js.map +1 -1
- package/dist/src/pnet/key-generator.d.ts.map +1 -1
- package/dist/src/pubsub/dummy-pubsub.js +9 -9
- package/dist/src/pubsub/dummy-pubsub.js.map +1 -1
- package/dist/src/registrar.d.ts.map +1 -1
- package/dist/src/registrar.js +4 -4
- package/dist/src/registrar.js.map +1 -1
- package/dist/src/transport-manager.d.ts.map +1 -1
- package/dist/src/transport-manager.js +8 -9
- package/dist/src/transport-manager.js.map +1 -1
- package/dist/src/upgrader.d.ts +2 -1
- package/dist/src/upgrader.d.ts.map +1 -1
- package/dist/src/upgrader.js +38 -33
- package/dist/src/upgrader.js.map +1 -1
- package/dist/typedoc-urls.json +19 -6
- package/package.json +36 -33
- package/src/address-manager/index.ts +62 -35
- package/src/autonat/constants.ts +4 -0
- package/src/autonat/index.ts +567 -0
- package/src/autonat/pb/index.proto +35 -0
- package/src/autonat/pb/index.ts +320 -0
- package/src/circuit/constants.ts +48 -7
- package/src/circuit/index.ts +17 -129
- package/src/circuit/pb/index.proto +57 -32
- package/src/circuit/pb/index.ts +448 -138
- package/src/circuit/server/advert-service.ts +108 -0
- package/src/circuit/server/index.ts +421 -0
- package/src/circuit/server/reservation-store.ts +111 -0
- package/src/circuit/server/reservation-voucher.ts +51 -0
- package/src/circuit/transport/discovery.ts +127 -0
- package/src/circuit/transport/index.ts +325 -0
- package/src/circuit/transport/listener.ts +99 -0
- package/src/circuit/transport/reservation-store.ts +249 -0
- package/src/circuit/utils.ts +108 -0
- package/src/components.ts +22 -21
- package/src/config.ts +21 -25
- package/src/connection/index.ts +10 -10
- package/src/connection-manager/auto-dialler.ts +36 -40
- package/src/connection-manager/dialer/dial-request.ts +13 -6
- package/src/connection-manager/dialer/index.ts +34 -15
- package/src/connection-manager/index.ts +22 -23
- package/src/connection-manager/latency-monitor.ts +9 -9
- package/src/connection-manager/visibility-change-emitter.ts +4 -4
- package/src/content-routing/index.ts +18 -16
- package/src/content-routing/utils.ts +5 -5
- package/src/dht/dht-content-routing.ts +5 -4
- package/src/dht/dht-peer-routing.ts +5 -4
- package/src/dht/dummy-dht.ts +13 -13
- package/src/errors.ts +2 -1
- package/src/fetch/index.ts +13 -13
- package/src/fetch/pb/proto.ts +8 -7
- package/src/get-peer.ts +3 -3
- package/src/identify/index.ts +29 -24
- package/src/identify/pb/message.ts +3 -2
- package/src/index.ts +15 -8
- package/src/insecure/index.ts +2 -1
- package/src/insecure/pb/proto.ts +8 -9
- package/src/libp2p.ts +52 -46
- package/src/nat-manager.ts +11 -9
- package/src/peer-record-updater.ts +4 -4
- package/src/peer-routing.ts +9 -9
- package/src/ping/index.ts +6 -6
- package/src/pnet/crypto.ts +3 -3
- package/src/pnet/index.ts +3 -3
- package/src/pnet/key-generator.ts +1 -1
- package/src/pubsub/dummy-pubsub.ts +9 -9
- package/src/registrar.ts +12 -12
- package/src/transport-manager.ts +17 -18
- package/src/upgrader.ts +43 -42
- package/src/version.ts +1 -1
- package/dist/src/circuit/auto-relay.d.ts +0 -46
- package/dist/src/circuit/auto-relay.d.ts.map +0 -1
- package/dist/src/circuit/auto-relay.js +0 -220
- package/dist/src/circuit/auto-relay.js.map +0 -1
- package/dist/src/circuit/circuit/hop.d.ts +0 -42
- package/dist/src/circuit/circuit/hop.d.ts.map +0 -1
- package/dist/src/circuit/circuit/hop.js +0 -142
- package/dist/src/circuit/circuit/hop.js.map +0 -1
- package/dist/src/circuit/circuit/stop.d.ts +0 -24
- package/dist/src/circuit/circuit/stop.d.ts.map +0 -1
- package/dist/src/circuit/circuit/stop.js +0 -51
- package/dist/src/circuit/circuit/stop.js.map +0 -1
- package/dist/src/circuit/circuit/stream-handler.d.ts +0 -40
- package/dist/src/circuit/circuit/stream-handler.d.ts.map +0 -1
- package/dist/src/circuit/circuit/stream-handler.js +0 -59
- package/dist/src/circuit/circuit/stream-handler.js.map +0 -1
- package/dist/src/circuit/circuit/utils.d.ts +0 -7
- package/dist/src/circuit/circuit/utils.d.ts.map +0 -1
- package/dist/src/circuit/circuit/utils.js +0 -43
- package/dist/src/circuit/circuit/utils.js.map +0 -1
- package/dist/src/circuit/listener.d.ts +0 -9
- package/dist/src/circuit/listener.d.ts.map +0 -1
- package/dist/src/circuit/listener.js +0 -62
- package/dist/src/circuit/listener.js.map +0 -1
- package/dist/src/circuit/multicodec.d.ts +0 -2
- package/dist/src/circuit/multicodec.d.ts.map +0 -1
- package/dist/src/circuit/multicodec.js +0 -2
- package/dist/src/circuit/multicodec.js.map +0 -1
- package/dist/src/circuit/transport.d.ts +0 -51
- package/dist/src/circuit/transport.d.ts.map +0 -1
- package/dist/src/circuit/transport.js +0 -223
- package/dist/src/circuit/transport.js.map +0 -1
- package/dist/src/keychain/cms.d.ts +0 -33
- package/dist/src/keychain/cms.d.ts.map +0 -1
- package/dist/src/keychain/cms.js +0 -129
- package/dist/src/keychain/cms.js.map +0 -1
- package/dist/src/keychain/index.d.ts +0 -151
- package/dist/src/keychain/index.d.ts.map +0 -1
- package/dist/src/keychain/index.js +0 -519
- package/dist/src/keychain/index.js.map +0 -1
- package/dist/src/keychain/util.d.ts +0 -21
- package/dist/src/keychain/util.d.ts.map +0 -1
- package/dist/src/keychain/util.js +0 -79
- package/dist/src/keychain/util.js.map +0 -1
- package/src/circuit/IMPLEMENTATION_NOTES.md +0 -128
- package/src/circuit/README.md +0 -111
- package/src/circuit/auto-relay.ts +0 -292
- package/src/circuit/circuit/hop.ts +0 -220
- package/src/circuit/circuit/stop.ts +0 -83
- package/src/circuit/circuit/stream-handler.ts +0 -87
- package/src/circuit/circuit/utils.ts +0 -44
- package/src/circuit/listener.ts +0 -83
- package/src/circuit/multicodec.ts +0 -2
- package/src/circuit/transport.ts +0 -278
- package/src/keychain/README.md +0 -55
- package/src/keychain/cms.ts +0 -150
- package/src/keychain/doc/private-key.png +0 -0
- package/src/keychain/doc/private-key.xml +0 -1
- package/src/keychain/index.ts +0 -629
- package/src/keychain/util.ts +0 -82
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { logger } from '@libp2p/logger'
|
|
2
|
+
import { codes } from '../../errors.js'
|
|
3
|
+
import { namespaceToCid } from '../utils.js'
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_ADVERT_BOOT_DELAY,
|
|
6
|
+
RELAY_RENDEZVOUS_NS
|
|
7
|
+
} from '../constants.js'
|
|
8
|
+
import type { Startable } from '@libp2p/interfaces/startable'
|
|
9
|
+
import type { ContentRouting } from '@libp2p/interface-content-routing'
|
|
10
|
+
import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
|
|
11
|
+
import pRetry from 'p-retry'
|
|
12
|
+
|
|
13
|
+
const log = logger('libp2p:circuit-relay:advert-service')
|
|
14
|
+
|
|
15
|
+
export interface AdvertServiceInit {
|
|
16
|
+
/**
|
|
17
|
+
* How long to wait after startup to begin advertising the service
|
|
18
|
+
* - if some configured content routers take a while to warm up (for
|
|
19
|
+
* example, the DHT needs some peers to be able to publish) this
|
|
20
|
+
* value should be high enough that they will have warmed up
|
|
21
|
+
*/
|
|
22
|
+
bootDelay?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AdvertServiceComponents {
|
|
26
|
+
contentRouting: ContentRouting
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AdvertServiceEvents {
|
|
30
|
+
'advert:success': CustomEvent<unknown>
|
|
31
|
+
'advert:error': CustomEvent<Error>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class AdvertService extends EventEmitter<AdvertServiceEvents> implements Startable {
|
|
35
|
+
private readonly contentRouting: ContentRouting
|
|
36
|
+
private timeout?: any
|
|
37
|
+
private started: boolean
|
|
38
|
+
private readonly bootDelay: number
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates an instance of Relay
|
|
42
|
+
*/
|
|
43
|
+
constructor (components: AdvertServiceComponents, init?: AdvertServiceInit) {
|
|
44
|
+
super()
|
|
45
|
+
|
|
46
|
+
this.contentRouting = components.contentRouting
|
|
47
|
+
this.bootDelay = init?.bootDelay ?? DEFAULT_ADVERT_BOOT_DELAY
|
|
48
|
+
this.started = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
isStarted (): boolean {
|
|
52
|
+
return this.started
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Start Relay service
|
|
57
|
+
*/
|
|
58
|
+
start (): void {
|
|
59
|
+
if (this.started) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Advertise service if HOP enabled and advertising enabled
|
|
64
|
+
this.timeout = setTimeout(() => {
|
|
65
|
+
this._advertiseService().catch(err => {
|
|
66
|
+
log.error('could not advertise service', err)
|
|
67
|
+
})
|
|
68
|
+
}, this.bootDelay)
|
|
69
|
+
|
|
70
|
+
this.started = true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Stop Relay service
|
|
75
|
+
*/
|
|
76
|
+
stop (): void {
|
|
77
|
+
try {
|
|
78
|
+
clearTimeout(this.timeout)
|
|
79
|
+
} catch (err) { }
|
|
80
|
+
|
|
81
|
+
this.started = false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Advertise hop relay service in the network.
|
|
86
|
+
*/
|
|
87
|
+
async _advertiseService (): Promise<void> {
|
|
88
|
+
await pRetry(async () => {
|
|
89
|
+
try {
|
|
90
|
+
const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
|
|
91
|
+
await this.contentRouting.provide(cid)
|
|
92
|
+
|
|
93
|
+
this.dispatchEvent(new CustomEvent('advert:success'))
|
|
94
|
+
} catch (err: any) {
|
|
95
|
+
this.safeDispatchEvent('advert:error', { detail: err })
|
|
96
|
+
|
|
97
|
+
if (err.code === codes.ERR_NO_ROUTERS_AVAILABLE) {
|
|
98
|
+
log.error('a content router, such as a DHT, must be provided in order to advertise the relay service', err)
|
|
99
|
+
this.stop()
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
log.error('could not advertise service', err)
|
|
104
|
+
throw err
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { logger } from '@libp2p/logger'
|
|
2
|
+
import { createLimitedRelay } from '../utils.js'
|
|
3
|
+
import {
|
|
4
|
+
CIRCUIT_PROTO_CODE,
|
|
5
|
+
DEFAULT_HOP_TIMEOUT,
|
|
6
|
+
RELAY_SOURCE_TAG
|
|
7
|
+
, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC
|
|
8
|
+
} from '../constants.js'
|
|
9
|
+
import type { PeerStore } from '@libp2p/interface-peer-store'
|
|
10
|
+
import type { Startable } from '@libp2p/interfaces/startable'
|
|
11
|
+
import { ReservationStore, ReservationStoreInit } from './reservation-store.js'
|
|
12
|
+
import type { IncomingStreamData, Registrar } from '@libp2p/interface-registrar'
|
|
13
|
+
import { AdvertService, AdvertServiceComponents, AdvertServiceInit } from './advert-service.js'
|
|
14
|
+
import pDefer from 'p-defer'
|
|
15
|
+
import { pbStream, ProtobufStream } from 'it-pb-stream'
|
|
16
|
+
import { HopMessage, Reservation, Status, StopMessage } from '../pb/index.js'
|
|
17
|
+
import { Multiaddr, multiaddr } from '@multiformats/multiaddr'
|
|
18
|
+
import type { Connection, Stream } from '@libp2p/interface-connection'
|
|
19
|
+
import type { ConnectionGater } from '@libp2p/interface-connection-gater'
|
|
20
|
+
import { peerIdFromBytes } from '@libp2p/peer-id'
|
|
21
|
+
import type { PeerId } from '@libp2p/interface-peer-id'
|
|
22
|
+
import { RecordEnvelope } from '@libp2p/peer-record'
|
|
23
|
+
import { ReservationVoucherRecord } from './reservation-voucher.js'
|
|
24
|
+
import type { AddressManager } from '@libp2p/interface-address-manager'
|
|
25
|
+
import type { ConnectionManager } from '@libp2p/interface-connection-manager'
|
|
26
|
+
import type { CircuitRelayService, RelayReservation } from '../index.js'
|
|
27
|
+
import { CustomEvent, EventEmitter } from '@libp2p/interfaces/events'
|
|
28
|
+
import { setMaxListeners } from 'events'
|
|
29
|
+
import type { PeerMap } from '@libp2p/peer-collections'
|
|
30
|
+
|
|
31
|
+
const log = logger('libp2p:circuit-relay:server')
|
|
32
|
+
|
|
33
|
+
const isRelayAddr = (ma: Multiaddr): boolean => ma.protoCodes().includes(CIRCUIT_PROTO_CODE)
|
|
34
|
+
|
|
35
|
+
export interface CircuitRelayServerInit {
|
|
36
|
+
/**
|
|
37
|
+
* Incoming hop requests must complete within this time in ms otherwise
|
|
38
|
+
* the stream will be reset (default: 30s)
|
|
39
|
+
*/
|
|
40
|
+
hopTimeout?: number
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* If true, advertise this service via libp2p content routing to allow
|
|
44
|
+
* peers to locate us on the network (default: false)
|
|
45
|
+
*/
|
|
46
|
+
advertise?: boolean | AdvertServiceInit
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Configuration of reservations
|
|
50
|
+
*/
|
|
51
|
+
reservations?: ReservationStoreInit
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The maximum number of simultaneous HOP inbound streams that can be open at once
|
|
55
|
+
*/
|
|
56
|
+
maxInboundHopStreams?: number
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The maximum number of simultaneous HOP outbound streams that can be open at once
|
|
60
|
+
*/
|
|
61
|
+
maxOutboundHopStreams?: number
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface HopProtocolOptions {
|
|
65
|
+
connection: Connection
|
|
66
|
+
request: HopMessage
|
|
67
|
+
stream: ProtobufStream<Stream>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface StopOptions {
|
|
71
|
+
connection: Connection
|
|
72
|
+
request: StopMessage
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface CircuitRelayServerComponents extends AdvertServiceComponents {
|
|
76
|
+
registrar: Registrar
|
|
77
|
+
peerStore: PeerStore
|
|
78
|
+
addressManager: AddressManager
|
|
79
|
+
peerId: PeerId
|
|
80
|
+
connectionManager: ConnectionManager
|
|
81
|
+
connectionGater: ConnectionGater
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface RelayServerEvents {
|
|
85
|
+
'relay:reservation': CustomEvent<RelayReservation>
|
|
86
|
+
'relay:advert:success': CustomEvent<unknown>
|
|
87
|
+
'relay:advert:error': CustomEvent<Error>
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class CircuitRelayServer extends EventEmitter<RelayServerEvents> implements Startable, CircuitRelayService {
|
|
91
|
+
private readonly registrar: Registrar
|
|
92
|
+
private readonly peerStore: PeerStore
|
|
93
|
+
private readonly addressManager: AddressManager
|
|
94
|
+
private readonly peerId: PeerId
|
|
95
|
+
private readonly connectionManager: ConnectionManager
|
|
96
|
+
private readonly connectionGater: ConnectionGater
|
|
97
|
+
private readonly reservationStore: ReservationStore
|
|
98
|
+
private readonly advertService: AdvertService | undefined
|
|
99
|
+
private started: boolean
|
|
100
|
+
private readonly hopTimeout: number
|
|
101
|
+
private readonly shutdownController: AbortController
|
|
102
|
+
private readonly maxInboundHopStreams?: number
|
|
103
|
+
private readonly maxOutboundHopStreams?: number
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Creates an instance of Relay
|
|
107
|
+
*/
|
|
108
|
+
constructor (components: CircuitRelayServerComponents, init: CircuitRelayServerInit = {}) {
|
|
109
|
+
super()
|
|
110
|
+
|
|
111
|
+
this.registrar = components.registrar
|
|
112
|
+
this.peerStore = components.peerStore
|
|
113
|
+
this.addressManager = components.addressManager
|
|
114
|
+
this.peerId = components.peerId
|
|
115
|
+
this.connectionManager = components.connectionManager
|
|
116
|
+
this.connectionGater = components.connectionGater
|
|
117
|
+
this.started = false
|
|
118
|
+
this.hopTimeout = init?.hopTimeout ?? DEFAULT_HOP_TIMEOUT
|
|
119
|
+
this.shutdownController = new AbortController()
|
|
120
|
+
this.maxInboundHopStreams = init.maxInboundHopStreams
|
|
121
|
+
this.maxOutboundHopStreams = init.maxOutboundHopStreams
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// fails on node < 15.4
|
|
125
|
+
setMaxListeners?.(Infinity, this.shutdownController.signal)
|
|
126
|
+
} catch { }
|
|
127
|
+
|
|
128
|
+
if (init.advertise != null && init.advertise !== false) {
|
|
129
|
+
this.advertService = new AdvertService(components, init.advertise === true ? undefined : init.advertise)
|
|
130
|
+
this.advertService.addEventListener('advert:success', () => {
|
|
131
|
+
this.dispatchEvent(new CustomEvent('relay:advert:success'))
|
|
132
|
+
})
|
|
133
|
+
this.advertService.addEventListener('advert:error', (evt) => {
|
|
134
|
+
this.safeDispatchEvent('relay:advert:error', { detail: evt.detail })
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.reservationStore = new ReservationStore(init.reservations)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isStarted (): boolean {
|
|
142
|
+
return this.started
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Start Relay service
|
|
147
|
+
*/
|
|
148
|
+
async start (): Promise<void> {
|
|
149
|
+
if (this.started) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Advertise service if HOP enabled and advertising enabled
|
|
154
|
+
this.advertService?.start()
|
|
155
|
+
|
|
156
|
+
await this.registrar.handle(RELAY_V2_HOP_CODEC, (data) => {
|
|
157
|
+
void this.onHop(data).catch(err => {
|
|
158
|
+
log.error(err)
|
|
159
|
+
})
|
|
160
|
+
}, {
|
|
161
|
+
maxInboundStreams: this.maxInboundHopStreams,
|
|
162
|
+
maxOutboundStreams: this.maxOutboundHopStreams
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
this.reservationStore.start()
|
|
166
|
+
|
|
167
|
+
this.started = true
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Stop Relay service
|
|
172
|
+
*/
|
|
173
|
+
async stop (): Promise<void> {
|
|
174
|
+
this.advertService?.stop()
|
|
175
|
+
this.reservationStore.stop()
|
|
176
|
+
this.shutdownController.abort()
|
|
177
|
+
await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
|
|
178
|
+
|
|
179
|
+
this.started = false
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async onHop ({ connection, stream }: IncomingStreamData): Promise<void> {
|
|
183
|
+
log('received circuit v2 hop protocol stream from %s', connection.remotePeer)
|
|
184
|
+
|
|
185
|
+
const hopTimeoutPromise = pDefer<HopMessage>()
|
|
186
|
+
const timeout = setTimeout(() => {
|
|
187
|
+
hopTimeoutPromise.reject('timed out')
|
|
188
|
+
}, this.hopTimeout)
|
|
189
|
+
const pbstr = pbStream(stream)
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const request: HopMessage = await Promise.race([
|
|
193
|
+
pbstr.pb(HopMessage).read(),
|
|
194
|
+
hopTimeoutPromise.promise
|
|
195
|
+
])
|
|
196
|
+
|
|
197
|
+
if (request?.type == null) {
|
|
198
|
+
throw new Error('request was invalid, could not read from stream')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
log('received', request.type)
|
|
202
|
+
|
|
203
|
+
await Promise.race([
|
|
204
|
+
this.handleHopProtocol({
|
|
205
|
+
connection,
|
|
206
|
+
stream: pbstr,
|
|
207
|
+
request
|
|
208
|
+
}),
|
|
209
|
+
hopTimeoutPromise.promise
|
|
210
|
+
])
|
|
211
|
+
} catch (err: any) {
|
|
212
|
+
log.error('error while handling hop', err)
|
|
213
|
+
pbstr.pb(HopMessage).write({
|
|
214
|
+
type: HopMessage.Type.STATUS,
|
|
215
|
+
status: Status.MALFORMED_MESSAGE
|
|
216
|
+
})
|
|
217
|
+
stream.abort(err)
|
|
218
|
+
} finally {
|
|
219
|
+
clearTimeout(timeout)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async handleHopProtocol ({ stream, request, connection }: HopProtocolOptions): Promise<void> {
|
|
224
|
+
log('received hop message')
|
|
225
|
+
switch (request.type) {
|
|
226
|
+
case HopMessage.Type.RESERVE: await this.handleReserve({ stream, request, connection }); break
|
|
227
|
+
case HopMessage.Type.CONNECT: await this.handleConnect({ stream, request, connection }); break
|
|
228
|
+
default: {
|
|
229
|
+
log.error('invalid hop request type %s via peer %s', request.type, connection.remotePeer)
|
|
230
|
+
stream.pb(HopMessage).write({ type: HopMessage.Type.STATUS, status: Status.UNEXPECTED_MESSAGE })
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async handleReserve ({ stream, request, connection }: HopProtocolOptions): Promise<void> {
|
|
236
|
+
const hopstr = stream.pb(HopMessage)
|
|
237
|
+
log('hop reserve request from %s', connection.remotePeer)
|
|
238
|
+
|
|
239
|
+
if (isRelayAddr(connection.remoteAddr)) {
|
|
240
|
+
log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer)
|
|
241
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if ((await this.connectionGater.denyInboundRelayReservation?.(connection.remotePeer)) === true) {
|
|
246
|
+
log.error('reservation for %p denied by connection gater', connection.remotePeer)
|
|
247
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const result = this.reservationStore.reserve(connection.remotePeer, connection.remoteAddr)
|
|
252
|
+
|
|
253
|
+
if (result.status !== Status.OK) {
|
|
254
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: result.status })
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
// tag relay target peer
|
|
260
|
+
// result.expire is non-null if `ReservationStore.reserve` returns with status == OK
|
|
261
|
+
if (result.expire != null) {
|
|
262
|
+
const ttl = new Date().getTime() - result.expire
|
|
263
|
+
await this.peerStore.tagPeer(connection.remotePeer, RELAY_SOURCE_TAG, { value: 1, ttl })
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
hopstr.write({
|
|
267
|
+
type: HopMessage.Type.STATUS,
|
|
268
|
+
status: Status.OK,
|
|
269
|
+
reservation: await this.makeReservation(connection.remotePeer, BigInt(result.expire ?? 0)),
|
|
270
|
+
limit: this.reservationStore.get(connection.remotePeer)?.limit
|
|
271
|
+
})
|
|
272
|
+
log('sent confirmation response to %s', connection.remotePeer)
|
|
273
|
+
} catch (err) {
|
|
274
|
+
log.error('failed to send confirmation response to %p', connection.remotePeer, err)
|
|
275
|
+
this.reservationStore.removeReservation(connection.remotePeer)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async makeReservation (
|
|
280
|
+
remotePeer: PeerId,
|
|
281
|
+
expire: bigint
|
|
282
|
+
): Promise<Reservation> {
|
|
283
|
+
const addrs = []
|
|
284
|
+
|
|
285
|
+
for (const relayAddr of this.addressManager.getAddresses()) {
|
|
286
|
+
addrs.push(relayAddr.bytes)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const voucher = await RecordEnvelope.seal(new ReservationVoucherRecord({
|
|
290
|
+
peer: remotePeer,
|
|
291
|
+
relay: this.peerId,
|
|
292
|
+
expiration: Number(expire)
|
|
293
|
+
}), this.peerId)
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
addrs,
|
|
297
|
+
expire,
|
|
298
|
+
voucher: voucher.marshal()
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async handleConnect ({ stream, request, connection }: HopProtocolOptions): Promise<void> {
|
|
303
|
+
const hopstr = stream.pb(HopMessage)
|
|
304
|
+
|
|
305
|
+
if (isRelayAddr(connection.remoteAddr)) {
|
|
306
|
+
log.error('relay reservation over circuit connection denied for peer: %p', connection.remotePeer)
|
|
307
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
log('hop connect request from %s', connection.remotePeer)
|
|
312
|
+
|
|
313
|
+
let dstPeer: PeerId
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
if (request.peer == null) {
|
|
317
|
+
log.error('no peer info in hop connect request')
|
|
318
|
+
throw new Error('no peer info in request')
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
request.peer.addrs.forEach(multiaddr)
|
|
322
|
+
dstPeer = peerIdFromBytes(request.peer.id)
|
|
323
|
+
} catch (err) {
|
|
324
|
+
log.error('invalid hop connect request via peer %p %s', connection.remotePeer, err)
|
|
325
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.MALFORMED_MESSAGE })
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!this.reservationStore.hasReservation(dstPeer)) {
|
|
330
|
+
log.error('hop connect denied for destination peer %p not having a reservation for %p with status %s', dstPeer, connection.remotePeer, Status.NO_RESERVATION)
|
|
331
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION })
|
|
332
|
+
return
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if ((await this.connectionGater.denyOutboundRelayedConnection?.(connection.remotePeer, dstPeer)) === true) {
|
|
336
|
+
log.error('hop connect for %p to %p denied by connection gater', connection.remotePeer, dstPeer)
|
|
337
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED })
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const connections = this.connectionManager.getConnections(dstPeer)
|
|
342
|
+
|
|
343
|
+
if (connections.length === 0) {
|
|
344
|
+
log('hop connect denied for destination peer %p not having a connection for %p as there is no destination connection', dstPeer, connection.remotePeer)
|
|
345
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.NO_RESERVATION })
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const destinationConnection = connections[0]
|
|
350
|
+
|
|
351
|
+
const destinationStream = await this.stopHop({
|
|
352
|
+
connection: destinationConnection,
|
|
353
|
+
request: {
|
|
354
|
+
type: StopMessage.Type.CONNECT,
|
|
355
|
+
peer: {
|
|
356
|
+
id: connection.remotePeer.toBytes(),
|
|
357
|
+
addrs: []
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
if (destinationStream == null) {
|
|
363
|
+
log.error('failed to open stream to destination peer %s', destinationConnection?.remotePeer)
|
|
364
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.CONNECTION_FAILED })
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
hopstr.write({ type: HopMessage.Type.STATUS, status: Status.OK })
|
|
369
|
+
const sourceStream = stream.unwrap()
|
|
370
|
+
|
|
371
|
+
log('connection from %p to %p established - merging streans', connection.remotePeer, dstPeer)
|
|
372
|
+
const limit = this.reservationStore.get(dstPeer)?.limit
|
|
373
|
+
// Short circuit the two streams to create the relayed connection
|
|
374
|
+
createLimitedRelay(sourceStream, destinationStream, this.shutdownController.signal, limit)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Send a STOP request to the target peer that the dialing peer wants to contact
|
|
379
|
+
*/
|
|
380
|
+
async stopHop ({
|
|
381
|
+
connection,
|
|
382
|
+
request
|
|
383
|
+
}: StopOptions): Promise<Stream | undefined> {
|
|
384
|
+
log('starting circuit relay v2 stop request to %s', connection.remotePeer)
|
|
385
|
+
const stream = await connection.newStream([RELAY_V2_STOP_CODEC])
|
|
386
|
+
const pbstr = pbStream(stream)
|
|
387
|
+
const stopstr = pbstr.pb(StopMessage)
|
|
388
|
+
stopstr.write(request)
|
|
389
|
+
let response
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
response = await stopstr.read()
|
|
393
|
+
} catch (err) {
|
|
394
|
+
log.error('error parsing stop message response from %s', connection.remotePeer)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (response == null) {
|
|
398
|
+
log.error('could not read response from %s', connection.remotePeer)
|
|
399
|
+
stream.close()
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (response.status === Status.OK) {
|
|
404
|
+
log('stop request to %s was successful', connection.remotePeer)
|
|
405
|
+
return pbstr.unwrap()
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
log('stop request failed with code %d', response.status)
|
|
409
|
+
stream.close()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
get reservations (): PeerMap<RelayReservation> {
|
|
413
|
+
return this.reservationStore.reservations
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService {
|
|
418
|
+
return (components) => {
|
|
419
|
+
return new CircuitRelayServer(components, init)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Limit, Status } from '../pb/index.js'
|
|
2
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
3
|
+
import type { PeerId } from '@libp2p/interface-peer-id'
|
|
4
|
+
import type { Startable } from '@libp2p/interfaces/startable'
|
|
5
|
+
import { PeerMap } from '@libp2p/peer-collections'
|
|
6
|
+
import type { RecursivePartial } from '@libp2p/interfaces'
|
|
7
|
+
import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
|
|
8
|
+
import type { RelayReservation } from '../index.js'
|
|
9
|
+
|
|
10
|
+
export type ReservationStatus = Status.OK | Status.PERMISSION_DENIED | Status.RESERVATION_REFUSED
|
|
11
|
+
|
|
12
|
+
export interface ReservationStoreInit {
|
|
13
|
+
/*
|
|
14
|
+
* maximum number of reservations allowed, default: 15
|
|
15
|
+
*/
|
|
16
|
+
maxReservations?: number
|
|
17
|
+
/*
|
|
18
|
+
* interval after which stale reservations are cleared, default: 300s
|
|
19
|
+
*/
|
|
20
|
+
reservationClearInterval?: number
|
|
21
|
+
/*
|
|
22
|
+
* apply default relay limits to a new reservation, default: true
|
|
23
|
+
*/
|
|
24
|
+
applyDefaultLimit?: boolean
|
|
25
|
+
/**
|
|
26
|
+
* reservation ttl, default: 2 hours
|
|
27
|
+
*/
|
|
28
|
+
reservationTtl?: number
|
|
29
|
+
/**
|
|
30
|
+
* The maximum time a relayed connection can be open for
|
|
31
|
+
*/
|
|
32
|
+
defaultDurationLimit?: number
|
|
33
|
+
/**
|
|
34
|
+
* The maximum amount of data allowed to be transferred over a relayed connection
|
|
35
|
+
*/
|
|
36
|
+
defaultDataLimit?: bigint
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type ReservationStoreOptions = RecursivePartial<ReservationStoreInit>
|
|
40
|
+
|
|
41
|
+
export class ReservationStore implements Startable {
|
|
42
|
+
public readonly reservations = new PeerMap<RelayReservation>()
|
|
43
|
+
private _started = false
|
|
44
|
+
private interval: any
|
|
45
|
+
private readonly maxReservations: number
|
|
46
|
+
private readonly reservationClearInterval: number
|
|
47
|
+
private readonly applyDefaultLimit: boolean
|
|
48
|
+
private readonly reservationTtl: number
|
|
49
|
+
private readonly defaultDurationLimit: number
|
|
50
|
+
private readonly defaultDataLimit: bigint
|
|
51
|
+
|
|
52
|
+
constructor (options: ReservationStoreOptions = {}) {
|
|
53
|
+
this.maxReservations = options.maxReservations ?? DEFAULT_MAX_RESERVATION_STORE_SIZE
|
|
54
|
+
this.reservationClearInterval = options.reservationClearInterval ?? DEFAULT_MAX_RESERVATION_CLEAR_INTERVAL
|
|
55
|
+
this.applyDefaultLimit = options.applyDefaultLimit !== false
|
|
56
|
+
this.reservationTtl = options.reservationTtl ?? DEFAULT_MAX_RESERVATION_TTL
|
|
57
|
+
this.defaultDurationLimit = options.defaultDurationLimit ?? DEFAULT_DURATION_LIMIT
|
|
58
|
+
this.defaultDataLimit = options.defaultDataLimit ?? DEFAULT_DATA_LIMIT
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
isStarted (): boolean {
|
|
62
|
+
return this._started
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
start (): void {
|
|
66
|
+
if (this._started) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
this._started = true
|
|
70
|
+
this.interval = setInterval(
|
|
71
|
+
() => {
|
|
72
|
+
const now = (new Date()).getTime()
|
|
73
|
+
this.reservations.forEach((r, k) => {
|
|
74
|
+
if (r.expire.getTime() < now) {
|
|
75
|
+
this.reservations.delete(k)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
},
|
|
79
|
+
this.reservationClearInterval
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
stop (): void {
|
|
84
|
+
clearInterval(this.interval)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
reserve (peer: PeerId, addr: Multiaddr, limit?: Limit): { status: ReservationStatus, expire?: number } {
|
|
88
|
+
if (this.reservations.size >= this.maxReservations && !this.reservations.has(peer)) {
|
|
89
|
+
return { status: Status.RESERVATION_REFUSED }
|
|
90
|
+
}
|
|
91
|
+
const expire = new Date(Date.now() + this.reservationTtl)
|
|
92
|
+
let checkedLimit: Limit | undefined
|
|
93
|
+
if (this.applyDefaultLimit) {
|
|
94
|
+
checkedLimit = limit ?? { data: this.defaultDataLimit, duration: this.defaultDurationLimit }
|
|
95
|
+
}
|
|
96
|
+
this.reservations.set(peer, { addr, expire, limit: checkedLimit })
|
|
97
|
+
return { status: Status.OK, expire: expire.getTime() }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
removeReservation (peer: PeerId): void {
|
|
101
|
+
this.reservations.delete(peer)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
hasReservation (dst: PeerId): boolean {
|
|
105
|
+
return this.reservations.has(dst)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get (peer: PeerId): RelayReservation | undefined {
|
|
109
|
+
return this.reservations.get(peer)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { PeerId } from '@libp2p/interface-peer-id'
|
|
2
|
+
import type { Record } from '@libp2p/interface-record'
|
|
3
|
+
import { ReservationVoucher } from '../pb/index.js'
|
|
4
|
+
|
|
5
|
+
export interface ReservationVoucherOptions {
|
|
6
|
+
relay: PeerId
|
|
7
|
+
peer: PeerId
|
|
8
|
+
expiration: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ReservationVoucherRecord implements Record {
|
|
12
|
+
public readonly domain = 'libp2p-relay-rsvp'
|
|
13
|
+
public readonly codec = new Uint8Array([0x03, 0x02])
|
|
14
|
+
|
|
15
|
+
private readonly relay: PeerId
|
|
16
|
+
private readonly peer: PeerId
|
|
17
|
+
private readonly expiration: number
|
|
18
|
+
|
|
19
|
+
constructor ({ relay, peer, expiration }: ReservationVoucherOptions) {
|
|
20
|
+
this.relay = relay
|
|
21
|
+
this.peer = peer
|
|
22
|
+
this.expiration = expiration
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
marshal (): Uint8Array {
|
|
26
|
+
return ReservationVoucher.encode({
|
|
27
|
+
relay: this.relay.toBytes(),
|
|
28
|
+
peer: this.peer.toBytes(),
|
|
29
|
+
expiration: BigInt(this.expiration)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
equals (other: Record): boolean {
|
|
34
|
+
if (!(other instanceof ReservationVoucherRecord)) {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
if (!this.peer.equals(other.peer)) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!this.relay.equals(other.relay)) {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (this.expiration !== other.expiration) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
}
|