@libp2p/circuit-relay-v2 1.0.24 → 1.0.25-44791342
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 +5 -5
- package/dist/src/constants.d.ts +13 -12
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +13 -12
- package/dist/src/constants.js.map +1 -1
- package/dist/src/pb/index.d.ts +7 -7
- package/dist/src/pb/index.d.ts.map +1 -1
- package/dist/src/pb/index.js +94 -52
- package/dist/src/pb/index.js.map +1 -1
- package/dist/src/server/index.d.ts +1 -7
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +10 -19
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/server/reservation-store.d.ts +2 -3
- package/dist/src/server/reservation-store.d.ts.map +1 -1
- package/dist/src/server/reservation-store.js.map +1 -1
- package/dist/src/transport/discovery.d.ts +15 -10
- package/dist/src/transport/discovery.d.ts.map +1 -1
- package/dist/src/transport/discovery.js +103 -51
- package/dist/src/transport/discovery.js.map +1 -1
- package/dist/src/transport/index.d.ts +27 -18
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/index.js +0 -3
- package/dist/src/transport/index.js.map +1 -1
- package/dist/src/transport/reservation-store.d.ts +7 -3
- package/dist/src/transport/reservation-store.d.ts.map +1 -1
- package/dist/src/transport/reservation-store.js +37 -13
- package/dist/src/transport/reservation-store.js.map +1 -1
- package/dist/src/transport/transport.d.ts +0 -1
- package/dist/src/transport/transport.d.ts.map +1 -1
- package/dist/src/transport/transport.js +20 -19
- package/dist/src/transport/transport.js.map +1 -1
- package/package.json +12 -13
- package/src/constants.ts +16 -15
- package/src/pb/index.ts +96 -53
- package/src/server/index.ts +11 -29
- package/src/server/reservation-store.ts +2 -4
- package/src/transport/discovery.ts +121 -56
- package/src/transport/index.ts +28 -18
- package/src/transport/reservation-store.ts +45 -13
- package/src/transport/transport.ts +21 -21
- package/dist/src/server/advert-service.d.ts +0 -44
- package/dist/src/server/advert-service.d.ts.map +0 -1
- package/dist/src/server/advert-service.js +0 -72
- package/dist/src/server/advert-service.js.map +0 -1
- package/dist/typedoc-urls.json +0 -12
- package/src/server/advert-service.ts +0 -107
@@ -1,24 +1,28 @@
|
|
1
|
-
import { TypedEventEmitter } from '@libp2p/interface'
|
1
|
+
import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
2
|
+
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
3
|
+
import { anySignal } from 'any-signal'
|
4
|
+
import { raceSignal } from 'race-signal'
|
2
5
|
import {
|
3
|
-
RELAY_RENDEZVOUS_NS,
|
4
6
|
RELAY_V2_HOP_CODEC
|
5
7
|
} from '../constants.js'
|
6
|
-
import {
|
7
|
-
import type {
|
8
|
-
import type { ConnectionManager, Registrar, TransportManager } from '@libp2p/interface-internal'
|
8
|
+
import type { ComponentLogger, Logger, PeerId, PeerStore, Startable, TopologyFilter } from '@libp2p/interface'
|
9
|
+
import type { ConnectionManager, RandomWalk, Registrar, TransportManager } from '@libp2p/interface-internal'
|
9
10
|
|
10
11
|
export interface RelayDiscoveryEvents {
|
11
12
|
'relay:discover': CustomEvent<PeerId>
|
12
13
|
}
|
13
14
|
|
14
15
|
export interface RelayDiscoveryComponents {
|
15
|
-
peerId: PeerId
|
16
16
|
peerStore: PeerStore
|
17
17
|
connectionManager: ConnectionManager
|
18
18
|
transportManager: TransportManager
|
19
|
-
contentRouting: ContentRouting
|
20
19
|
registrar: Registrar
|
21
20
|
logger: ComponentLogger
|
21
|
+
randomWalk: RandomWalk
|
22
|
+
}
|
23
|
+
|
24
|
+
export interface RelayDiscoveryInit {
|
25
|
+
filter?: TopologyFilter
|
22
26
|
}
|
23
27
|
|
24
28
|
/**
|
@@ -26,23 +30,30 @@ export interface RelayDiscoveryComponents {
|
|
26
30
|
* peers that support the circuit v2 HOP protocol.
|
27
31
|
*/
|
28
32
|
export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> implements Startable {
|
29
|
-
private readonly peerId: PeerId
|
30
33
|
private readonly peerStore: PeerStore
|
31
|
-
private readonly contentRouting: ContentRouting
|
32
34
|
private readonly registrar: Registrar
|
35
|
+
private readonly connectionManager: ConnectionManager
|
36
|
+
private readonly randomWalk: RandomWalk
|
33
37
|
private started: boolean
|
38
|
+
private running: boolean
|
34
39
|
private topologyId?: string
|
35
40
|
private readonly log: Logger
|
41
|
+
private discoveryController: AbortController
|
42
|
+
private readonly filter?: TopologyFilter
|
36
43
|
|
37
|
-
constructor (components: RelayDiscoveryComponents) {
|
44
|
+
constructor (components: RelayDiscoveryComponents, init: RelayDiscoveryInit = {}) {
|
38
45
|
super()
|
39
46
|
|
40
47
|
this.log = components.logger.forComponent('libp2p:circuit-relay:discover-relays')
|
41
48
|
this.started = false
|
42
|
-
this.
|
49
|
+
this.running = false
|
43
50
|
this.peerStore = components.peerStore
|
44
|
-
this.contentRouting = components.contentRouting
|
45
51
|
this.registrar = components.registrar
|
52
|
+
this.connectionManager = components.connectionManager
|
53
|
+
this.randomWalk = components.randomWalk
|
54
|
+
this.filter = init.filter
|
55
|
+
this.discoveryController = new AbortController()
|
56
|
+
setMaxListeners(Infinity, this.discoveryController.signal)
|
46
57
|
}
|
47
58
|
|
48
59
|
isStarted (): boolean {
|
@@ -53,8 +64,9 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
53
64
|
// register a topology listener for when new peers are encountered
|
54
65
|
// that support the hop protocol
|
55
66
|
this.topologyId = await this.registrar.register(RELAY_V2_HOP_CODEC, {
|
56
|
-
|
67
|
+
filter: this.filter,
|
57
68
|
onConnect: (peerId) => {
|
69
|
+
this.log('discovered relay %p', peerId)
|
58
70
|
this.safeDispatchEvent('relay:discover', { detail: peerId })
|
59
71
|
}
|
60
72
|
})
|
@@ -62,18 +74,12 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
62
74
|
this.started = true
|
63
75
|
}
|
64
76
|
|
65
|
-
afterStart (): void {
|
66
|
-
void this.discover()
|
67
|
-
.catch(err => {
|
68
|
-
this.log.error('error discovering relays', err)
|
69
|
-
})
|
70
|
-
}
|
71
|
-
|
72
77
|
stop (): void {
|
73
78
|
if (this.topologyId != null) {
|
74
79
|
this.registrar.unregister(this.topologyId)
|
75
80
|
}
|
76
81
|
|
82
|
+
this.discoveryController?.abort()
|
77
83
|
this.started = false
|
78
84
|
}
|
79
85
|
|
@@ -81,54 +87,113 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
81
87
|
* Try to listen on available hop relay connections.
|
82
88
|
* The following order will happen while we do not have enough relays:
|
83
89
|
*
|
84
|
-
* 1. Check the metadata store for known relays, try to listen on the ones we are already connected
|
90
|
+
* 1. Check the metadata store for known relays, try to listen on the ones we are already connected to
|
85
91
|
* 2. Dial and try to listen on the peers we know that support hop but are not connected
|
86
92
|
* 3. Search the network
|
87
93
|
*/
|
88
|
-
|
89
|
-
this.
|
90
|
-
|
91
|
-
filters: [
|
92
|
-
// filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on
|
93
|
-
(peer) => {
|
94
|
-
return peer.protocols.includes(RELAY_V2_HOP_CODEC)
|
95
|
-
}
|
96
|
-
],
|
97
|
-
orders: [
|
98
|
-
() => Math.random() < 0.5 ? 1 : -1
|
99
|
-
]
|
100
|
-
}))
|
101
|
-
|
102
|
-
for (const peer of peers) {
|
103
|
-
this.log('found relay peer %p in content peer store', peer.id)
|
104
|
-
this.safeDispatchEvent('relay:discover', { detail: peer.id })
|
94
|
+
startDiscovery (): void {
|
95
|
+
if (this.running) {
|
96
|
+
return
|
105
97
|
}
|
106
98
|
|
107
|
-
this.log('
|
99
|
+
this.log('start discovery')
|
100
|
+
this.running = true
|
101
|
+
this.discoveryController = new AbortController()
|
102
|
+
setMaxListeners(Infinity, this.discoveryController.signal)
|
103
|
+
|
104
|
+
Promise.resolve()
|
105
|
+
.then(async () => {
|
106
|
+
this.log('searching peer store for relays')
|
107
|
+
|
108
|
+
const peers = (await this.peerStore.all({
|
109
|
+
filters: [
|
110
|
+
// filter by a list of peers supporting RELAY_V2_HOP and ones we are not listening on
|
111
|
+
(peer) => {
|
112
|
+
return peer.protocols.includes(RELAY_V2_HOP_CODEC)
|
113
|
+
}
|
114
|
+
],
|
115
|
+
orders: [
|
116
|
+
() => Math.random() < 0.5 ? 1 : -1
|
117
|
+
]
|
118
|
+
}))
|
119
|
+
|
120
|
+
for (const peer of peers) {
|
121
|
+
this.log.trace('found relay peer %p in peer store', peer.id)
|
122
|
+
this.safeDispatchEvent('relay:discover', { detail: peer.id })
|
123
|
+
}
|
124
|
+
|
125
|
+
this.log('found %d relay peers in peer store', peers.length)
|
126
|
+
|
127
|
+
// perform random walk and dial peers - after identify has run, the network
|
128
|
+
// topology will be notified of new relays
|
129
|
+
const queue = new PeerQueue({
|
130
|
+
concurrency: 5
|
131
|
+
})
|
132
|
+
|
133
|
+
this.log('start random walk')
|
134
|
+
for await (const peer of this.randomWalk.walk({ signal: this.discoveryController.signal })) {
|
135
|
+
this.log.trace('found random peer %p', peer.id)
|
136
|
+
|
137
|
+
if (queue.has(peer.id)) {
|
138
|
+
this.log.trace('random peer %p was already in queue', peer.id)
|
139
|
+
|
140
|
+
// skip peers already in the queue
|
141
|
+
continue
|
142
|
+
}
|
108
143
|
|
109
|
-
|
110
|
-
|
111
|
-
const cid = await namespaceToCid(RELAY_RENDEZVOUS_NS)
|
144
|
+
if (this.connectionManager.getConnections(peer.id)?.length > 0) {
|
145
|
+
this.log.trace('random peer %p was already connected', peer.id)
|
112
146
|
|
113
|
-
|
147
|
+
// skip peers we are already connected to
|
148
|
+
continue
|
149
|
+
}
|
114
150
|
|
115
|
-
|
116
|
-
|
117
|
-
const peerId = provider.id
|
151
|
+
if (!(await this.connectionManager.isDialable(peer.multiaddrs))) {
|
152
|
+
this.log.trace('random peer %p was not dialable', peer.id, peer.multiaddrs.map(ma => ma.toString()))
|
118
153
|
|
119
|
-
|
120
|
-
|
121
|
-
|
154
|
+
// skip peers we can't dial
|
155
|
+
continue
|
156
|
+
}
|
157
|
+
|
158
|
+
this.log.trace('wait for space in queue for %p', peer.id)
|
159
|
+
|
160
|
+
// pause the random walk until there is space in the queue
|
161
|
+
await raceSignal(queue.onSizeLessThan(10), this.discoveryController.signal)
|
162
|
+
|
163
|
+
this.log('adding random peer %p to dial queue (length: %d)', peer.id, queue.size)
|
164
|
+
|
165
|
+
// dial the peer - this will cause identify to run and our topology to
|
166
|
+
// be notified and we'll attempt to create reservations
|
167
|
+
queue.add(async () => {
|
168
|
+
const signal = anySignal([this.discoveryController.signal, AbortSignal.timeout(5000)])
|
169
|
+
setMaxListeners(Infinity, signal)
|
170
|
+
|
171
|
+
try {
|
172
|
+
await this.connectionManager.openConnection(peer.id, { signal })
|
173
|
+
} finally {
|
174
|
+
signal.clear()
|
175
|
+
}
|
176
|
+
}, {
|
177
|
+
peerId: peer.id,
|
178
|
+
signal: this.discoveryController.signal
|
122
179
|
})
|
180
|
+
.catch(err => {
|
181
|
+
this.log.error('error opening connection to random peer %p', peer.id, err)
|
182
|
+
})
|
183
|
+
}
|
123
184
|
|
124
|
-
|
125
|
-
|
185
|
+
await queue.onIdle()
|
186
|
+
})
|
187
|
+
.catch(err => {
|
188
|
+
if (!this.discoveryController.signal.aborted) {
|
189
|
+
this.log.error('failed when finding relays on the network', err)
|
126
190
|
}
|
127
|
-
}
|
191
|
+
})
|
192
|
+
}
|
128
193
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
194
|
+
stopDiscovery (): void {
|
195
|
+
this.log('stop discovery')
|
196
|
+
this.running = false
|
197
|
+
this.discoveryController?.abort()
|
133
198
|
}
|
134
199
|
}
|
package/src/transport/index.ts
CHANGED
@@ -1,20 +1,16 @@
|
|
1
|
-
import { type Transport, type Upgrader, type Libp2pEvents, type ComponentLogger, type ConnectionGater, type ContentRouting, type TypedEventTarget, type PeerId, type PeerStore } from '@libp2p/interface'
|
2
|
-
import { type RelayDiscoveryComponents } from './discovery.js'
|
3
|
-
import { type RelayStoreInit } from './reservation-store.js'
|
4
1
|
import { CircuitRelayTransport } from './transport.js'
|
5
|
-
import type {
|
2
|
+
import type { RelayDiscoveryComponents } from './discovery.js'
|
3
|
+
import type { RelayStoreInit } from './reservation-store.js'
|
4
|
+
import type { Transport, Upgrader, Libp2pEvents, ConnectionGater, TypedEventTarget, PeerId, TopologyFilter } from '@libp2p/interface'
|
5
|
+
import type { AddressManager, Registrar } from '@libp2p/interface-internal'
|
6
6
|
|
7
7
|
export interface CircuitRelayTransportComponents extends RelayDiscoveryComponents {
|
8
8
|
peerId: PeerId
|
9
|
-
peerStore: PeerStore
|
10
9
|
registrar: Registrar
|
11
|
-
connectionManager: ConnectionManager
|
12
10
|
upgrader: Upgrader
|
13
11
|
addressManager: AddressManager
|
14
|
-
contentRouting: ContentRouting
|
15
12
|
connectionGater: ConnectionGater
|
16
13
|
events: TypedEventTarget<Libp2pEvents>
|
17
|
-
logger: ComponentLogger
|
18
14
|
}
|
19
15
|
|
20
16
|
/**
|
@@ -22,34 +18,48 @@ export interface CircuitRelayTransportComponents extends RelayDiscoveryComponent
|
|
22
18
|
*/
|
23
19
|
export interface CircuitRelayTransportInit extends RelayStoreInit {
|
24
20
|
/**
|
25
|
-
* The number of peers running diable relays to search for and
|
26
|
-
*
|
21
|
+
* The number of peers running diable relays to search for and connect to
|
22
|
+
*
|
23
|
+
* @default 0
|
27
24
|
*/
|
28
25
|
discoverRelays?: number
|
29
26
|
|
27
|
+
/**
|
28
|
+
* An optional filter used to prevent duplicate attempts to reserve relay
|
29
|
+
* slots on the same peer
|
30
|
+
*/
|
31
|
+
discoveryFilter?: TopologyFilter
|
32
|
+
|
30
33
|
/**
|
31
34
|
* The maximum number of simultaneous STOP inbound streams that can be open at
|
32
|
-
* once - each inbound relayed connection uses a STOP stream
|
35
|
+
* once - each inbound relayed connection uses a STOP stream
|
36
|
+
*
|
37
|
+
* @default 300
|
33
38
|
*/
|
34
39
|
maxInboundStopStreams?: number
|
35
40
|
|
36
41
|
/**
|
37
|
-
* The maximum number of simultaneous STOP outbound streams that can be open
|
38
|
-
* once. If this transport is used along with the relay server these
|
39
|
-
* should be set to the same value
|
42
|
+
* The maximum number of simultaneous STOP outbound streams that can be open
|
43
|
+
* at once. If this transport is used along with the relay server these
|
44
|
+
* settings should be set to the same value
|
45
|
+
*
|
46
|
+
* @default 300
|
40
47
|
*/
|
41
48
|
maxOutboundStopStreams?: number
|
42
49
|
|
43
50
|
/**
|
44
|
-
* Incoming STOP requests (e.g. when a remote peer wants to dial us via a
|
45
|
-
* must finish the initial protocol negotiation within this timeout in
|
46
|
-
*
|
51
|
+
* Incoming STOP requests (e.g. when a remote peer wants to dial us via a
|
52
|
+
* relay) must finish the initial protocol negotiation within this timeout in
|
53
|
+
* ms
|
54
|
+
*
|
55
|
+
* @default 30000
|
47
56
|
*/
|
48
57
|
stopTimeout?: number
|
49
58
|
|
50
59
|
/**
|
51
60
|
* When creating a reservation it must complete within this number of ms
|
52
|
-
*
|
61
|
+
*
|
62
|
+
* @default 10000
|
53
63
|
*/
|
54
64
|
reservationCompletionTimeout?: number
|
55
65
|
}
|
@@ -1,15 +1,17 @@
|
|
1
|
-
import { TypedEventEmitter } from '@libp2p/interface'
|
1
|
+
import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
2
2
|
import { PeerMap } from '@libp2p/peer-collections'
|
3
|
+
import { createBloomFilter } from '@libp2p/utils/filters'
|
3
4
|
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
4
5
|
import { multiaddr } from '@multiformats/multiaddr'
|
5
6
|
import { pbStream } from 'it-protobuf-stream'
|
6
7
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
7
|
-
import { DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
|
8
|
+
import { DEFAULT_MAX_RESERVATION_QUEUE_LENGTH, DEFAULT_RESERVATION_COMPLETION_TIMEOUT, DEFAULT_RESERVATION_CONCURRENCY, RELAY_TAG, RELAY_V2_HOP_CODEC } from '../constants.js'
|
8
9
|
import { HopMessage, Status } from '../pb/index.js'
|
9
10
|
import { getExpirationMilliseconds } from '../utils.js'
|
10
11
|
import type { Reservation } from '../pb/index.js'
|
11
12
|
import type { TypedEventTarget, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, PeerId, PeerStore, Startable, Metrics } from '@libp2p/interface'
|
12
13
|
import type { ConnectionManager, TransportManager } from '@libp2p/interface-internal'
|
14
|
+
import type { Filter } from '@libp2p/utils/filters'
|
13
15
|
|
14
16
|
// allow refreshing a relay reservation if it will expire in the next 10 minutes
|
15
17
|
const REFRESH_WINDOW = (60 * 1000) * 10
|
@@ -69,6 +71,7 @@ interface RelayEntry {
|
|
69
71
|
export interface ReservationStoreEvents {
|
70
72
|
'relay:not-enough-relays': CustomEvent
|
71
73
|
'relay:removed': CustomEvent<PeerId>
|
74
|
+
'relay:created-reservation': CustomEvent<PeerId>
|
72
75
|
}
|
73
76
|
|
74
77
|
export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents> implements Startable {
|
@@ -84,6 +87,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
84
87
|
private readonly reservationCompletionTimeout: number
|
85
88
|
private started: boolean
|
86
89
|
private readonly log: Logger
|
90
|
+
private readonly relayFilter: Filter
|
87
91
|
|
88
92
|
constructor (components: RelayStoreComponents, init?: RelayStoreInit) {
|
89
93
|
super()
|
@@ -96,9 +100,10 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
96
100
|
this.events = components.events
|
97
101
|
this.reservations = new PeerMap()
|
98
102
|
this.maxDiscoveredRelays = init?.discoverRelays ?? 0
|
99
|
-
this.maxReservationQueueLength = init?.maxReservationQueueLength ??
|
100
|
-
this.reservationCompletionTimeout = init?.reservationCompletionTimeout ??
|
103
|
+
this.maxReservationQueueLength = init?.maxReservationQueueLength ?? DEFAULT_MAX_RESERVATION_QUEUE_LENGTH
|
104
|
+
this.reservationCompletionTimeout = init?.reservationCompletionTimeout ?? DEFAULT_RESERVATION_COMPLETION_TIMEOUT
|
101
105
|
this.started = false
|
106
|
+
this.relayFilter = createBloomFilter(100)
|
102
107
|
|
103
108
|
// ensure we don't listen on multiple relays simultaneously
|
104
109
|
this.reserveQueue = new PeerQueue({
|
@@ -123,6 +128,13 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
123
128
|
this.started = true
|
124
129
|
}
|
125
130
|
|
131
|
+
afterStart (): void {
|
132
|
+
if (this.reservations.size < this.maxDiscoveredRelays) {
|
133
|
+
this.log('not enough relays %d/%d', this.reservations.size, this.maxDiscoveredRelays)
|
134
|
+
this.safeDispatchEvent('relay:not-enough-relays', {})
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
126
138
|
stop (): void {
|
127
139
|
this.reserveQueue.clear()
|
128
140
|
this.reservations.forEach(({ timeout }) => {
|
@@ -134,9 +146,9 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
134
146
|
|
135
147
|
/**
|
136
148
|
* If the number of current relays is beneath the configured `maxReservations`
|
137
|
-
* value, and the passed peer id is not our own, and we have a non-relayed
|
138
|
-
* to the remote, and the remote peer speaks the hop protocol, try
|
139
|
-
* on the remote peer
|
149
|
+
* value, and the passed peer id is not our own, and we have a non-relayed
|
150
|
+
* connection to the remote, and the remote peer speaks the hop protocol, try
|
151
|
+
* to reserve a slot on the remote peer
|
140
152
|
*/
|
141
153
|
async addRelay (peerId: PeerId, type: RelayType): Promise<void> {
|
142
154
|
if (this.peerId.equals(peerId)) {
|
@@ -145,18 +157,25 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
145
157
|
}
|
146
158
|
|
147
159
|
if (this.reserveQueue.size > this.maxReservationQueueLength) {
|
148
|
-
this.log('not adding relay as the queue is full')
|
160
|
+
this.log('not adding potential relay peer %p as the queue is full', peerId)
|
149
161
|
return
|
150
162
|
}
|
151
163
|
|
152
164
|
if (this.reserveQueue.has(peerId)) {
|
153
|
-
this.log('relay peer is already in the reservation queue')
|
165
|
+
this.log('potential relay peer %p is already in the reservation queue', peerId)
|
166
|
+
return
|
167
|
+
}
|
168
|
+
|
169
|
+
if (this.relayFilter.has(peerId.toBytes())) {
|
170
|
+
this.log('potential relay peer %p has failed previously, not trying again', peerId, new Error('where').stack)
|
154
171
|
return
|
155
172
|
}
|
156
173
|
|
157
|
-
this.log('
|
174
|
+
this.log('try to reserve relay slot with %p', peerId)
|
158
175
|
|
159
176
|
await this.reserveQueue.add(async () => {
|
177
|
+
const start = Date.now()
|
178
|
+
|
160
179
|
try {
|
161
180
|
// allow refresh of an existing reservation if it is about to expire
|
162
181
|
const existingReservation = this.reservations.get(peerId)
|
@@ -183,6 +202,7 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
183
202
|
}
|
184
203
|
|
185
204
|
const signal = AbortSignal.timeout(this.reservationCompletionTimeout)
|
205
|
+
setMaxListeners(Infinity, signal)
|
186
206
|
|
187
207
|
const connection = await this.connectionManager.openConnection(peerId, {
|
188
208
|
signal
|
@@ -230,8 +250,12 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
230
250
|
|
231
251
|
// listen on multiaddr that only the circuit transport is listening for
|
232
252
|
await this.transportManager.listen([multiaddr(`/p2p/${peerId.toString()}/p2p-circuit`)])
|
253
|
+
|
254
|
+
this.safeDispatchEvent('relay:created-reservation', {
|
255
|
+
detail: peerId
|
256
|
+
})
|
233
257
|
} catch (err) {
|
234
|
-
this.log.error('could not reserve slot on %p', peerId, err)
|
258
|
+
this.log.error('could not reserve slot on %p after %dms', peerId, Date.now() - start, err)
|
235
259
|
|
236
260
|
// cancel the renewal timeout if it's been set
|
237
261
|
const reservation = this.reservations.get(peerId)
|
@@ -242,6 +266,9 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
242
266
|
|
243
267
|
// if listening failed, remove the reservation
|
244
268
|
this.reservations.delete(peerId)
|
269
|
+
|
270
|
+
// don't try this peer again
|
271
|
+
this.relayFilter.add(peerId.toBytes())
|
245
272
|
}
|
246
273
|
}, {
|
247
274
|
peerId
|
@@ -256,6 +283,10 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
256
283
|
return this.reservations.get(peerId)?.reservation
|
257
284
|
}
|
258
285
|
|
286
|
+
reservationCount (): number {
|
287
|
+
return this.reservations.size
|
288
|
+
}
|
289
|
+
|
259
290
|
async #createReservation (connection: Connection, options: AbortOptions): Promise<Reservation> {
|
260
291
|
options.signal?.throwIfAborted()
|
261
292
|
|
@@ -270,11 +301,12 @@ export class ReservationStore extends TypedEventEmitter<ReservationStoreEvents>
|
|
270
301
|
try {
|
271
302
|
response = await hopstr.read(options)
|
272
303
|
} catch (err: any) {
|
273
|
-
this.log.error('error parsing reserve message response from %p because', connection.remotePeer, err)
|
274
304
|
stream.abort(err)
|
275
305
|
throw err
|
276
306
|
} finally {
|
277
|
-
|
307
|
+
if (stream.status !== 'closed') {
|
308
|
+
await stream.close(options)
|
309
|
+
}
|
278
310
|
}
|
279
311
|
|
280
312
|
if (response.status === Status.OK && (response.reservation != null)) {
|
@@ -1,11 +1,12 @@
|
|
1
|
-
import { CodeError } from '@libp2p/interface'
|
1
|
+
import { CodeError, start, stop } from '@libp2p/interface'
|
2
2
|
import { transportSymbol, type Transport, type CreateListenerOptions, type Listener, type Upgrader, type AbortOptions, type ComponentLogger, type Logger, type Connection, type Stream, type ConnectionGater, type PeerId, type PeerStore } from '@libp2p/interface'
|
3
|
+
import { peerFilter } from '@libp2p/peer-collections'
|
3
4
|
import { peerIdFromBytes, peerIdFromString } from '@libp2p/peer-id'
|
4
5
|
import { streamToMaConnection } from '@libp2p/utils/stream-to-ma-conn'
|
5
6
|
import * as mafmt from '@multiformats/mafmt'
|
6
7
|
import { multiaddr } from '@multiformats/multiaddr'
|
7
8
|
import { pbStream } from 'it-protobuf-stream'
|
8
|
-
import { CIRCUIT_PROTO_CODE, ERR_HOP_REQUEST_FAILED, ERR_RELAYED_DIAL, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
|
9
|
+
import { CIRCUIT_PROTO_CODE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE, DEFAULT_DISCOVERY_FILTER_SIZE, ERR_HOP_REQUEST_FAILED, ERR_RELAYED_DIAL, MAX_CONNECTIONS, RELAY_V2_HOP_CODEC, RELAY_V2_STOP_CODEC } from '../constants.js'
|
9
10
|
import { StopMessage, HopMessage, Status } from '../pb/index.js'
|
10
11
|
import { RelayDiscovery } from './discovery.js'
|
11
12
|
import { createListener } from './listener.js'
|
@@ -77,8 +78,12 @@ export class CircuitRelayTransport implements Transport {
|
|
77
78
|
this.maxOutboundStopStreams = init.maxOutboundStopStreams ?? defaults.maxOutboundStopStreams
|
78
79
|
this.stopTimeout = init.stopTimeout ?? defaults.stopTimeout
|
79
80
|
|
80
|
-
|
81
|
-
|
81
|
+
const discoverRelays = init.discoverRelays ?? 0
|
82
|
+
|
83
|
+
if (discoverRelays > 0) {
|
84
|
+
this.discovery = new RelayDiscovery(components, {
|
85
|
+
filter: init.discoveryFilter ?? peerFilter(DEFAULT_DISCOVERY_FILTER_SIZE, DEFAULT_DISCOVERY_FILTER_ERROR_RATE)
|
86
|
+
})
|
82
87
|
this.discovery.addEventListener('relay:discover', (evt) => {
|
83
88
|
this.reservationStore.addRelay(evt.detail, 'discovered')
|
84
89
|
.catch(err => {
|
@@ -89,10 +94,12 @@ export class CircuitRelayTransport implements Transport {
|
|
89
94
|
|
90
95
|
this.reservationStore = new ReservationStore(components, init)
|
91
96
|
this.reservationStore.addEventListener('relay:not-enough-relays', () => {
|
92
|
-
this.discovery?.
|
93
|
-
|
94
|
-
|
95
|
-
|
97
|
+
this.discovery?.startDiscovery()
|
98
|
+
})
|
99
|
+
this.reservationStore.addEventListener('relay:created-reservation', () => {
|
100
|
+
if (this.reservationStore.reservationCount() >= discoverRelays) {
|
101
|
+
this.discovery?.stopDiscovery()
|
102
|
+
}
|
96
103
|
})
|
97
104
|
|
98
105
|
this.started = false
|
@@ -103,8 +110,6 @@ export class CircuitRelayTransport implements Transport {
|
|
103
110
|
}
|
104
111
|
|
105
112
|
async start (): Promise<void> {
|
106
|
-
this.reservationStore.start()
|
107
|
-
|
108
113
|
await this.registrar.handle(RELAY_V2_STOP_CODEC, (data) => {
|
109
114
|
void this.onStop(data).catch(err => {
|
110
115
|
this.log.error('error while handling STOP protocol', err)
|
@@ -116,18 +121,13 @@ export class CircuitRelayTransport implements Transport {
|
|
116
121
|
runOnTransientConnection: true
|
117
122
|
})
|
118
123
|
|
119
|
-
await this.discovery
|
124
|
+
await start(this.discovery, this.reservationStore)
|
120
125
|
|
121
126
|
this.started = true
|
122
127
|
}
|
123
128
|
|
124
|
-
afterStart (): void {
|
125
|
-
this.discovery?.afterStart()
|
126
|
-
}
|
127
|
-
|
128
129
|
async stop (): Promise<void> {
|
129
|
-
this.discovery
|
130
|
-
this.reservationStore.stop()
|
130
|
+
await stop(this.discovery, this.reservationStore)
|
131
131
|
await this.registrar.unhandle(RELAY_V2_STOP_CODEC)
|
132
132
|
|
133
133
|
this.started = false
|
@@ -231,9 +231,9 @@ export class CircuitRelayTransport implements Transport {
|
|
231
231
|
logger: this.logger
|
232
232
|
})
|
233
233
|
|
234
|
-
this.log('new outbound
|
234
|
+
this.log('new outbound relayed connection %a', maConn.remoteAddr)
|
235
235
|
return await this.upgrader.upgradeOutbound(maConn, {
|
236
|
-
transient:
|
236
|
+
transient: status.limit != null
|
237
237
|
})
|
238
238
|
} catch (err: any) {
|
239
239
|
this.log.error(`Circuit relay dial to destination ${destinationPeer.toString()} via relay ${connection.remotePeer.toString()} failed`, err)
|
@@ -346,9 +346,9 @@ export class CircuitRelayTransport implements Transport {
|
|
346
346
|
logger: this.logger
|
347
347
|
})
|
348
348
|
|
349
|
-
this.log('new inbound
|
349
|
+
this.log('new inbound relayed connection %a', maConn.remoteAddr)
|
350
350
|
await this.upgrader.upgradeInbound(maConn, {
|
351
|
-
transient:
|
351
|
+
transient: request.limit != null
|
352
352
|
})
|
353
353
|
this.log('%s connection %a upgraded', 'inbound', maConn.remoteAddr)
|
354
354
|
}
|
@@ -1,44 +0,0 @@
|
|
1
|
-
import { TypedEventEmitter } from '@libp2p/interface';
|
2
|
-
import type { ComponentLogger, ContentRouting, Startable } from '@libp2p/interface';
|
3
|
-
export interface AdvertServiceInit {
|
4
|
-
/**
|
5
|
-
* How long to wait after startup to begin advertising the service
|
6
|
-
* - if some configured content routers take a while to warm up (for
|
7
|
-
* example, the DHT needs some peers to be able to publish) this
|
8
|
-
* value should be high enough that they will have warmed up
|
9
|
-
*/
|
10
|
-
bootDelay?: number;
|
11
|
-
}
|
12
|
-
export interface AdvertServiceComponents {
|
13
|
-
contentRouting: ContentRouting;
|
14
|
-
logger: ComponentLogger;
|
15
|
-
}
|
16
|
-
export interface AdvertServiceEvents {
|
17
|
-
'advert:success': CustomEvent<unknown>;
|
18
|
-
'advert:error': CustomEvent<Error>;
|
19
|
-
}
|
20
|
-
export declare class AdvertService extends TypedEventEmitter<AdvertServiceEvents> implements Startable {
|
21
|
-
private readonly contentRouting;
|
22
|
-
private timeout?;
|
23
|
-
private started;
|
24
|
-
private readonly bootDelay;
|
25
|
-
private readonly log;
|
26
|
-
/**
|
27
|
-
* Creates an instance of Relay
|
28
|
-
*/
|
29
|
-
constructor(components: AdvertServiceComponents, init?: AdvertServiceInit);
|
30
|
-
isStarted(): boolean;
|
31
|
-
/**
|
32
|
-
* Start Relay service
|
33
|
-
*/
|
34
|
-
start(): void;
|
35
|
-
/**
|
36
|
-
* Stop Relay service
|
37
|
-
*/
|
38
|
-
stop(): void;
|
39
|
-
/**
|
40
|
-
* Advertise hop relay service in the network.
|
41
|
-
*/
|
42
|
-
_advertiseService(): Promise<void>;
|
43
|
-
}
|
44
|
-
//# sourceMappingURL=advert-service.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"advert-service.d.ts","sourceRoot":"","sources":["../../../src/server/advert-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAQrD,OAAO,KAAK,EAAE,eAAe,EAAU,cAAc,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE3F,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,cAAc,CAAA;IAC9B,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,gBAAgB,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IACtC,cAAc,EAAE,WAAW,CAAC,KAAK,CAAC,CAAA;CACnC;AAED,qBAAa,aAAc,SAAQ,iBAAiB,CAAC,mBAAmB,CAAE,YAAW,SAAS;IAC5F,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAK;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAE5B;;OAEG;gBACU,UAAU,EAAE,uBAAuB,EAAE,IAAI,CAAC,EAAE,iBAAiB;IAS1E,SAAS,IAAK,OAAO;IAIrB;;OAEG;IACH,KAAK,IAAK,IAAI;IAed;;OAEG;IACH,IAAI,IAAK,IAAI;IAQb;;OAEG;IACG,iBAAiB,IAAK,OAAO,CAAC,IAAI,CAAC;CAqB1C"}
|