@libp2p/circuit-relay-v2 3.2.23-cf9aab5c8 → 3.2.24-a02cb0461
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 +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +170 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/server/index.d.ts +41 -45
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +26 -43
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/server/reservation-store.d.ts +2 -36
- 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 +2 -17
- package/dist/src/transport/discovery.d.ts.map +1 -1
- package/dist/src/transport/discovery.js +2 -2
- package/dist/src/transport/discovery.js.map +1 -1
- package/dist/src/transport/index.d.ts +34 -42
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/index.js +296 -5
- package/dist/src/transport/index.js.map +1 -1
- package/dist/src/transport/reservation-store.d.ts +3 -37
- package/dist/src/transport/reservation-store.d.ts.map +1 -1
- package/dist/src/transport/reservation-store.js +2 -4
- package/dist/src/transport/reservation-store.js.map +1 -1
- package/dist/src/transport/stream-to-conn.d.ts +19 -0
- package/dist/src/transport/stream-to-conn.d.ts.map +1 -0
- package/dist/src/transport/stream-to-conn.js +60 -0
- package/dist/src/transport/stream-to-conn.js.map +1 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +36 -59
- package/dist/src/utils.js.map +1 -1
- package/package.json +19 -24
- package/src/index.ts +207 -8
- package/src/server/index.ts +32 -94
- package/src/server/reservation-store.ts +2 -42
- package/src/transport/discovery.ts +4 -22
- package/src/transport/index.ts +344 -46
- package/src/transport/reservation-store.ts +6 -44
- package/src/transport/stream-to-conn.ts +91 -0
- package/src/utils.ts +37 -66
- package/dist/src/transport/transport.d.ts +0 -54
- package/dist/src/transport/transport.d.ts.map +0 -1
- package/dist/src/transport/transport.js +0 -314
- package/dist/src/transport/transport.js.map +0 -1
- package/src/transport/transport.ts +0 -380
package/src/server/index.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { publicKeyToProtobuf } from '@libp2p/crypto/keys'
|
2
2
|
import { peerIdFromMultihash } from '@libp2p/peer-id'
|
3
3
|
import { RecordEnvelope } from '@libp2p/peer-record'
|
4
|
+
import { pbStream } from '@libp2p/utils'
|
4
5
|
import { multiaddr } from '@multiformats/multiaddr'
|
5
|
-
import { pbStream } from 'it-protobuf-stream'
|
6
6
|
import { TypedEventEmitter, setMaxListeners } from 'main-event'
|
7
7
|
import * as Digest from 'multiformats/hashes/digest'
|
8
8
|
import {
|
@@ -18,50 +18,15 @@ import { HopMessage, Status, StopMessage } from '../pb/index.js'
|
|
18
18
|
import { createLimitedRelay } from '../utils.js'
|
19
19
|
import { ReservationStore } from './reservation-store.js'
|
20
20
|
import { ReservationVoucherRecord } from './reservation-voucher.js'
|
21
|
-
import type {
|
22
|
-
import type { CircuitRelayService, RelayReservation } from '../index.js'
|
21
|
+
import type { CircuitRelayServerComponents, CircuitRelayServerInit, CircuitRelayService, RelayReservation } from '../index.js'
|
23
22
|
import type { Reservation } from '../pb/index.js'
|
24
|
-
import type {
|
25
|
-
import type { AddressManager, ConnectionManager, Registrar } from '@libp2p/interface-internal'
|
23
|
+
import type { Logger, Connection, Stream, PeerId, Startable, AbortOptions } from '@libp2p/interface'
|
26
24
|
import type { PeerMap } from '@libp2p/peer-collections'
|
25
|
+
import type { ProtobufStream } from '@libp2p/utils'
|
27
26
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
28
|
-
import type { ProtobufStream } from 'it-protobuf-stream'
|
29
27
|
|
30
28
|
const isRelayAddr = (ma: Multiaddr): boolean => ma.protoCodes().includes(CIRCUIT_PROTO_CODE)
|
31
29
|
|
32
|
-
export interface CircuitRelayServerInit {
|
33
|
-
/**
|
34
|
-
* Incoming hop requests must complete within this time in ms otherwise
|
35
|
-
* the stream will be reset
|
36
|
-
*
|
37
|
-
* @default 30000
|
38
|
-
*/
|
39
|
-
hopTimeout?: number
|
40
|
-
|
41
|
-
/**
|
42
|
-
* Configuration of reservations
|
43
|
-
*/
|
44
|
-
reservations?: ReservationStoreInit
|
45
|
-
|
46
|
-
/**
|
47
|
-
* The maximum number of simultaneous HOP inbound streams that can be open at once
|
48
|
-
*/
|
49
|
-
maxInboundHopStreams?: number
|
50
|
-
|
51
|
-
/**
|
52
|
-
* The maximum number of simultaneous HOP outbound streams that can be open at once
|
53
|
-
*/
|
54
|
-
maxOutboundHopStreams?: number
|
55
|
-
|
56
|
-
/**
|
57
|
-
* The maximum number of simultaneous STOP outbound streams that can be open at
|
58
|
-
* once.
|
59
|
-
*
|
60
|
-
* @default 300
|
61
|
-
*/
|
62
|
-
maxOutboundStopStreams?: number
|
63
|
-
}
|
64
|
-
|
65
30
|
export interface HopProtocolOptions {
|
66
31
|
connection: Connection
|
67
32
|
request: HopMessage
|
@@ -73,18 +38,6 @@ export interface StopOptions {
|
|
73
38
|
request: StopMessage
|
74
39
|
}
|
75
40
|
|
76
|
-
export interface CircuitRelayServerComponents {
|
77
|
-
registrar: Registrar
|
78
|
-
peerStore: PeerStore
|
79
|
-
addressManager: AddressManager
|
80
|
-
peerId: PeerId
|
81
|
-
privateKey: PrivateKey
|
82
|
-
connectionManager: ConnectionManager
|
83
|
-
connectionGater: ConnectionGater
|
84
|
-
logger: ComponentLogger
|
85
|
-
metrics?: Metrics
|
86
|
-
}
|
87
|
-
|
88
41
|
export interface RelayServerEvents {
|
89
42
|
'relay:reservation': CustomEvent<RelayReservation>
|
90
43
|
'relay:advert:success': CustomEvent<unknown>
|
@@ -95,14 +48,8 @@ const defaults = {
|
|
95
48
|
maxOutboundStopStreams: MAX_CONNECTIONS
|
96
49
|
}
|
97
50
|
|
98
|
-
class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements Startable, CircuitRelayService {
|
99
|
-
private readonly
|
100
|
-
private readonly peerStore: PeerStore
|
101
|
-
private readonly addressManager: AddressManager
|
102
|
-
private readonly peerId: PeerId
|
103
|
-
private readonly privateKey: PrivateKey
|
104
|
-
private readonly connectionManager: ConnectionManager
|
105
|
-
private readonly connectionGater: ConnectionGater
|
51
|
+
export class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements Startable, CircuitRelayService {
|
52
|
+
private readonly components: CircuitRelayServerComponents
|
106
53
|
private readonly reservationStore: ReservationStore
|
107
54
|
private started: boolean
|
108
55
|
private readonly hopTimeout: number
|
@@ -119,13 +66,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
119
66
|
super()
|
120
67
|
|
121
68
|
this.log = components.logger.forComponent('libp2p:circuit-relay:server')
|
122
|
-
this.
|
123
|
-
this.peerStore = components.peerStore
|
124
|
-
this.addressManager = components.addressManager
|
125
|
-
this.peerId = components.peerId
|
126
|
-
this.privateKey = components.privateKey
|
127
|
-
this.connectionManager = components.connectionManager
|
128
|
-
this.connectionGater = components.connectionGater
|
69
|
+
this.components = components
|
129
70
|
this.started = false
|
130
71
|
this.hopTimeout = init?.hopTimeout ?? DEFAULT_HOP_TIMEOUT
|
131
72
|
this.maxInboundHopStreams = init.maxInboundHopStreams
|
@@ -135,6 +76,8 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
135
76
|
|
136
77
|
this.shutdownController = new AbortController()
|
137
78
|
setMaxListeners(Infinity, this.shutdownController.signal)
|
79
|
+
|
80
|
+
this.onHop = this.onHop.bind(this)
|
138
81
|
}
|
139
82
|
|
140
83
|
readonly [Symbol.toStringTag] = '@libp2p/circuit-relay-v2-server'
|
@@ -151,11 +94,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
151
94
|
return
|
152
95
|
}
|
153
96
|
|
154
|
-
await this.registrar.handle(RELAY_V2_HOP_CODEC,
|
155
|
-
void this.onHop(data).catch(err => {
|
156
|
-
this.log.error(err)
|
157
|
-
})
|
158
|
-
}, {
|
97
|
+
await this.components.registrar.handle(RELAY_V2_HOP_CODEC, this.onHop, {
|
159
98
|
maxInboundStreams: this.maxInboundHopStreams,
|
160
99
|
maxOutboundStreams: this.maxOutboundHopStreams,
|
161
100
|
runOnLimitedConnection: true
|
@@ -170,16 +109,19 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
170
109
|
async stop (): Promise<void> {
|
171
110
|
this.reservationStore.clear()
|
172
111
|
this.shutdownController.abort()
|
173
|
-
await this.registrar.unhandle(RELAY_V2_HOP_CODEC)
|
112
|
+
await this.components.registrar.unhandle(RELAY_V2_HOP_CODEC)
|
174
113
|
|
175
114
|
this.started = false
|
176
115
|
}
|
177
116
|
|
178
|
-
async onHop (
|
117
|
+
async onHop (stream: Stream, connection: Connection): Promise<void> {
|
179
118
|
this.log('received circuit v2 hop protocol stream from %p', connection.remotePeer)
|
180
119
|
|
120
|
+
const signal = AbortSignal.timeout(this.hopTimeout)
|
121
|
+
setMaxListeners(Infinity, signal)
|
122
|
+
|
181
123
|
const options = {
|
182
|
-
signal
|
124
|
+
signal
|
183
125
|
}
|
184
126
|
const pbstr = pbStream(stream)
|
185
127
|
|
@@ -229,7 +171,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
229
171
|
return
|
230
172
|
}
|
231
173
|
|
232
|
-
if ((await this.connectionGater.denyInboundRelayReservation?.(connection.remotePeer)) === true) {
|
174
|
+
if ((await this.components.connectionGater.denyInboundRelayReservation?.(connection.remotePeer)) === true) {
|
233
175
|
this.log.error('reservation for %p denied by connection gater', connection.remotePeer)
|
234
176
|
await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, options)
|
235
177
|
return
|
@@ -247,12 +189,12 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
247
189
|
// result.expire is non-null if `ReservationStore.reserve` returns with status == OK
|
248
190
|
if (result.expire != null) {
|
249
191
|
const ttl = (result.expire * 1000) - Date.now()
|
250
|
-
await this.peerStore.merge(connection.remotePeer, {
|
192
|
+
await this.components.peerStore.merge(connection.remotePeer, {
|
251
193
|
tags: {
|
252
194
|
[RELAY_SOURCE_TAG]: { value: 1, ttl },
|
253
195
|
[KEEP_ALIVE_SOURCE_TAG]: { value: 1, ttl }
|
254
196
|
}
|
255
|
-
})
|
197
|
+
}, options)
|
256
198
|
}
|
257
199
|
|
258
200
|
await hopstr.write({
|
@@ -262,17 +204,20 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
262
204
|
limit: this.reservationStore.get(connection.remotePeer)?.limit
|
263
205
|
}, options)
|
264
206
|
this.log('sent confirmation response to %s', connection.remotePeer)
|
207
|
+
|
208
|
+
// close writable end of stream
|
209
|
+
await hopstr.unwrap().unwrap().close(options)
|
265
210
|
} catch (err) {
|
266
211
|
this.log.error('failed to send confirmation response to %p - %e', connection.remotePeer, err)
|
267
212
|
this.reservationStore.removeReservation(connection.remotePeer)
|
268
213
|
|
269
214
|
try {
|
270
|
-
await this.peerStore.merge(connection.remotePeer, {
|
215
|
+
await this.components.peerStore.merge(connection.remotePeer, {
|
271
216
|
tags: {
|
272
217
|
[RELAY_SOURCE_TAG]: undefined,
|
273
218
|
[KEEP_ALIVE_SOURCE_TAG]: undefined
|
274
219
|
}
|
275
|
-
})
|
220
|
+
}, options)
|
276
221
|
} catch (err) {
|
277
222
|
this.log.error('failed to untag relay source peer %p - %e', connection.remotePeer, err)
|
278
223
|
}
|
@@ -285,7 +230,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
285
230
|
): Promise<Reservation> {
|
286
231
|
const addrs = []
|
287
232
|
|
288
|
-
for (const relayAddr of this.addressManager.getAddresses()) {
|
233
|
+
for (const relayAddr of this.components.addressManager.getAddresses()) {
|
289
234
|
if (relayAddr.toString().includes('/p2p-circuit')) {
|
290
235
|
continue
|
291
236
|
}
|
@@ -295,9 +240,9 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
295
240
|
|
296
241
|
const envelope = await RecordEnvelope.seal(new ReservationVoucherRecord({
|
297
242
|
peer: remotePeer,
|
298
|
-
relay: this.peerId,
|
243
|
+
relay: this.components.peerId,
|
299
244
|
expiration: expire
|
300
|
-
}), this.privateKey)
|
245
|
+
}), this.components.privateKey)
|
301
246
|
|
302
247
|
return {
|
303
248
|
addrs,
|
@@ -307,7 +252,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
307
252
|
payloadType: envelope.payloadType,
|
308
253
|
payload: {
|
309
254
|
peer: remotePeer.toMultihash().bytes,
|
310
|
-
relay: this.peerId.toMultihash().bytes,
|
255
|
+
relay: this.components.peerId.toMultihash().bytes,
|
311
256
|
expiration: expire
|
312
257
|
},
|
313
258
|
signature: envelope.signature
|
@@ -350,13 +295,13 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
350
295
|
return
|
351
296
|
}
|
352
297
|
|
353
|
-
if ((await this.connectionGater.denyOutboundRelayedConnection?.(connection.remotePeer, dstPeer)) === true) {
|
298
|
+
if ((await this.components.connectionGater.denyOutboundRelayedConnection?.(connection.remotePeer, dstPeer)) === true) {
|
354
299
|
this.log.error('hop connect for %p to %p denied by connection gater', connection.remotePeer, dstPeer)
|
355
300
|
await hopstr.write({ type: HopMessage.Type.STATUS, status: Status.PERMISSION_DENIED }, options)
|
356
301
|
return
|
357
302
|
}
|
358
303
|
|
359
|
-
const connections = this.connectionManager.getConnections(dstPeer)
|
304
|
+
const connections = this.components.connectionManager.getConnections(dstPeer)
|
360
305
|
|
361
306
|
if (connections.length === 0) {
|
362
307
|
this.log('hop connect denied for destination peer %p not having a connection for %p as there is no destination connection', dstPeer, connection.remotePeer)
|
@@ -389,12 +334,11 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
389
334
|
status: Status.OK,
|
390
335
|
limit: reservation?.limit
|
391
336
|
}, options)
|
392
|
-
const sourceStream = stream.unwrap()
|
393
337
|
|
394
338
|
this.log('connection from %p to %p established - merging streams', connection.remotePeer, dstPeer)
|
395
339
|
|
396
340
|
// Short circuit the two streams to create the relayed connection
|
397
|
-
createLimitedRelay(
|
341
|
+
createLimitedRelay(stream.unwrap(), destinationStream, this.shutdownController.signal, reservation, {
|
398
342
|
log: this.log
|
399
343
|
})
|
400
344
|
}
|
@@ -404,7 +348,7 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
404
348
|
*/
|
405
349
|
async stopHop ({ connection, request }: StopOptions, options: AbortOptions): Promise<Stream | undefined> {
|
406
350
|
this.log('starting circuit relay v2 stop request to %s', connection.remotePeer)
|
407
|
-
const stream = await connection.newStream(
|
351
|
+
const stream = await connection.newStream(RELAY_V2_STOP_CODEC, {
|
408
352
|
maxOutboundStreams: this.maxOutboundStopStreams,
|
409
353
|
runOnLimitedConnection: true,
|
410
354
|
...options
|
@@ -439,9 +383,3 @@ class CircuitRelayServer extends TypedEventEmitter<RelayServerEvents> implements
|
|
439
383
|
return this.reservationStore.reservations
|
440
384
|
}
|
441
385
|
}
|
442
|
-
|
443
|
-
export function circuitRelayServer (init: CircuitRelayServerInit = {}): (components: CircuitRelayServerComponents) => CircuitRelayService {
|
444
|
-
return (components) => {
|
445
|
-
return new CircuitRelayServer(components, init)
|
446
|
-
}
|
447
|
-
}
|
@@ -2,7 +2,7 @@ import { trackedPeerMap } from '@libp2p/peer-collections'
|
|
2
2
|
import { retimeableSignal } from 'retimeable-signal'
|
3
3
|
import { DEFAULT_DATA_LIMIT, DEFAULT_DURATION_LIMIT, DEFAULT_MAX_RESERVATION_STORE_SIZE, DEFAULT_MAX_RESERVATION_TTL } from '../constants.js'
|
4
4
|
import { Status } from '../pb/index.js'
|
5
|
-
import type { RelayReservation } from '../index.js'
|
5
|
+
import type { RelayReservation, ServerReservationStoreInit } from '../index.js'
|
6
6
|
import type { Limit } from '../pb/index.js'
|
7
7
|
import type { ComponentLogger, Logger, Metrics, PeerId } from '@libp2p/interface'
|
8
8
|
import type { PeerMap } from '@libp2p/peer-collections'
|
@@ -15,46 +15,6 @@ export interface ReservationStoreComponents {
|
|
15
15
|
metrics?: Metrics
|
16
16
|
}
|
17
17
|
|
18
|
-
export interface ReservationStoreInit {
|
19
|
-
/**
|
20
|
-
* maximum number of reservations allowed
|
21
|
-
*
|
22
|
-
* @default 15
|
23
|
-
*/
|
24
|
-
maxReservations?: number
|
25
|
-
|
26
|
-
/**
|
27
|
-
* interval after which stale reservations are cleared
|
28
|
-
*
|
29
|
-
* @default 300000
|
30
|
-
*/
|
31
|
-
reservationClearInterval?: number
|
32
|
-
|
33
|
-
/**
|
34
|
-
* apply default relay limits to a new reservation
|
35
|
-
*
|
36
|
-
* @default true
|
37
|
-
*/
|
38
|
-
applyDefaultLimit?: boolean
|
39
|
-
|
40
|
-
/**
|
41
|
-
* reservation ttl
|
42
|
-
*
|
43
|
-
* @default 7200000
|
44
|
-
*/
|
45
|
-
reservationTtl?: number
|
46
|
-
|
47
|
-
/**
|
48
|
-
* The maximum time a relayed connection can be open for
|
49
|
-
*/
|
50
|
-
defaultDurationLimit?: number
|
51
|
-
|
52
|
-
/**
|
53
|
-
* The maximum amount of data allowed to be transferred over a relayed connection
|
54
|
-
*/
|
55
|
-
defaultDataLimit?: bigint
|
56
|
-
}
|
57
|
-
|
58
18
|
export class ReservationStore {
|
59
19
|
public readonly reservations: PeerMap<RelayReservation>
|
60
20
|
private readonly maxReservations: number
|
@@ -64,7 +24,7 @@ export class ReservationStore {
|
|
64
24
|
private readonly defaultDataLimit: bigint
|
65
25
|
private readonly log: Logger
|
66
26
|
|
67
|
-
constructor (components: ReservationStoreComponents, init:
|
27
|
+
constructor (components: ReservationStoreComponents, init: ServerReservationStoreInit = {}) {
|
68
28
|
this.log = components.logger.forComponent('libp2p:circuit-relay:server:reservation-store')
|
69
29
|
this.maxReservations = init.maxReservations ?? DEFAULT_MAX_RESERVATION_STORE_SIZE
|
70
30
|
this.applyDefaultLimit = init.applyDefaultLimit !== false
|
@@ -1,30 +1,12 @@
|
|
1
|
-
import { PeerQueue } from '@libp2p/utils
|
1
|
+
import { PeerQueue } from '@libp2p/utils'
|
2
2
|
import { anySignal } from 'any-signal'
|
3
3
|
import { TypedEventEmitter, setMaxListeners } from 'main-event'
|
4
4
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
5
5
|
import {
|
6
6
|
RELAY_V2_HOP_CODEC
|
7
7
|
} from '../constants.js'
|
8
|
-
import type {
|
9
|
-
import type {
|
10
|
-
|
11
|
-
export interface RelayDiscoveryEvents {
|
12
|
-
'relay:discover': CustomEvent<PeerId>
|
13
|
-
}
|
14
|
-
|
15
|
-
export interface RelayDiscoveryComponents {
|
16
|
-
peerStore: PeerStore
|
17
|
-
connectionManager: ConnectionManager
|
18
|
-
transportManager: TransportManager
|
19
|
-
registrar: Registrar
|
20
|
-
logger: ComponentLogger
|
21
|
-
randomWalk: RandomWalk
|
22
|
-
events: TypedEventTarget<Libp2pEvents>
|
23
|
-
}
|
24
|
-
|
25
|
-
export interface RelayDiscoveryInit {
|
26
|
-
filter?: TopologyFilter
|
27
|
-
}
|
8
|
+
import type { RelayDiscoveryComponents, RelayDiscoveryEvents, RelayDiscoveryInit } from '../index.ts'
|
9
|
+
import type { Logger, Peer, PeerId, PeerInfo, Startable, TopologyFilter } from '@libp2p/interface'
|
28
10
|
|
29
11
|
/**
|
30
12
|
* ReservationManager automatically makes a circuit v2 reservation on any connected
|
@@ -221,7 +203,7 @@ export class RelayDiscovery extends TypedEventEmitter<RelayDiscoveryEvents> impl
|
|
221
203
|
}
|
222
204
|
|
223
205
|
onPeer (evt: CustomEvent<PeerInfo>): void {
|
224
|
-
this.log.trace('maybe dialing discovered peer %p
|
206
|
+
this.log.trace('maybe dialing discovered peer %p', evt.detail.id)
|
225
207
|
|
226
208
|
this.maybeDialPeer(evt)
|
227
209
|
.catch(err => {
|