libp2p 0.43.4 → 0.44.0-17eb162a

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.
Files changed (162) hide show
  1. package/dist/index.min.js +29 -29
  2. package/dist/src/address-manager/index.d.ts +4 -4
  3. package/dist/src/address-manager/index.d.ts.map +1 -1
  4. package/dist/src/address-manager/index.js +9 -6
  5. package/dist/src/address-manager/index.js.map +1 -1
  6. package/dist/src/autonat/index.d.ts +1 -2
  7. package/dist/src/autonat/index.d.ts.map +1 -1
  8. package/dist/src/autonat/index.js +4 -6
  9. package/dist/src/autonat/index.js.map +1 -1
  10. package/dist/src/circuit-relay/server/advert-service.d.ts.map +1 -1
  11. package/dist/src/circuit-relay/server/advert-service.js +2 -2
  12. package/dist/src/circuit-relay/server/advert-service.js.map +1 -1
  13. package/dist/src/circuit-relay/server/index.d.ts.map +1 -1
  14. package/dist/src/circuit-relay/server/index.js +7 -3
  15. package/dist/src/circuit-relay/server/index.js.map +1 -1
  16. package/dist/src/circuit-relay/transport/discovery.d.ts.map +1 -1
  17. package/dist/src/circuit-relay/transport/discovery.js +3 -1
  18. package/dist/src/circuit-relay/transport/discovery.js.map +1 -1
  19. package/dist/src/circuit-relay/transport/index.d.ts +3 -0
  20. package/dist/src/circuit-relay/transport/index.d.ts.map +1 -1
  21. package/dist/src/circuit-relay/transport/index.js +6 -2
  22. package/dist/src/circuit-relay/transport/index.js.map +1 -1
  23. package/dist/src/circuit-relay/transport/listener.d.ts +3 -0
  24. package/dist/src/circuit-relay/transport/listener.d.ts.map +1 -1
  25. package/dist/src/circuit-relay/transport/listener.js +5 -4
  26. package/dist/src/circuit-relay/transport/listener.js.map +1 -1
  27. package/dist/src/circuit-relay/transport/reservation-store.d.ts +4 -1
  28. package/dist/src/circuit-relay/transport/reservation-store.d.ts.map +1 -1
  29. package/dist/src/circuit-relay/transport/reservation-store.js +11 -6
  30. package/dist/src/circuit-relay/transport/reservation-store.js.map +1 -1
  31. package/dist/src/circuit-relay/utils.d.ts.map +1 -1
  32. package/dist/src/circuit-relay/utils.js +3 -1
  33. package/dist/src/circuit-relay/utils.js.map +1 -1
  34. package/dist/src/components.d.ts +8 -6
  35. package/dist/src/components.d.ts.map +1 -1
  36. package/dist/src/components.js +10 -10
  37. package/dist/src/components.js.map +1 -1
  38. package/dist/src/config.d.ts.map +1 -1
  39. package/dist/src/config.js +0 -9
  40. package/dist/src/config.js.map +1 -1
  41. package/dist/src/connection-manager/auto-dial.d.ts +39 -0
  42. package/dist/src/connection-manager/auto-dial.d.ts.map +1 -0
  43. package/dist/src/connection-manager/auto-dial.js +131 -0
  44. package/dist/src/connection-manager/auto-dial.js.map +1 -0
  45. package/dist/src/connection-manager/connection-pruner.d.ts +32 -0
  46. package/dist/src/connection-manager/connection-pruner.d.ts.map +1 -0
  47. package/dist/src/connection-manager/connection-pruner.js +111 -0
  48. package/dist/src/connection-manager/connection-pruner.js.map +1 -0
  49. package/dist/src/connection-manager/constants.d.ts +49 -0
  50. package/dist/src/connection-manager/constants.d.ts.map +1 -0
  51. package/dist/src/connection-manager/constants.js +49 -0
  52. package/dist/src/connection-manager/constants.js.map +1 -0
  53. package/dist/src/connection-manager/dial-queue.d.ts +78 -0
  54. package/dist/src/connection-manager/dial-queue.d.ts.map +1 -0
  55. package/dist/src/connection-manager/dial-queue.js +374 -0
  56. package/dist/src/connection-manager/dial-queue.js.map +1 -0
  57. package/dist/src/connection-manager/index.d.ts +70 -73
  58. package/dist/src/connection-manager/index.d.ts.map +1 -1
  59. package/dist/src/connection-manager/index.js +116 -248
  60. package/dist/src/connection-manager/index.js.map +1 -1
  61. package/dist/src/connection-manager/utils.d.ts +9 -0
  62. package/dist/src/connection-manager/utils.d.ts.map +1 -0
  63. package/dist/src/connection-manager/utils.js +66 -0
  64. package/dist/src/connection-manager/utils.js.map +1 -0
  65. package/dist/src/content-routing/index.js +1 -1
  66. package/dist/src/content-routing/index.js.map +1 -1
  67. package/dist/src/content-routing/utils.d.ts.map +1 -1
  68. package/dist/src/content-routing/utils.js +3 -1
  69. package/dist/src/content-routing/utils.js.map +1 -1
  70. package/dist/src/fetch/index.js +3 -3
  71. package/dist/src/fetch/index.js.map +1 -1
  72. package/dist/src/identify/index.d.ts +4 -0
  73. package/dist/src/identify/index.d.ts.map +1 -1
  74. package/dist/src/identify/index.js +104 -128
  75. package/dist/src/identify/index.js.map +1 -1
  76. package/dist/src/index.d.ts +2 -19
  77. package/dist/src/index.d.ts.map +1 -1
  78. package/dist/src/index.js.map +1 -1
  79. package/dist/src/insecure/index.d.ts.map +1 -1
  80. package/dist/src/insecure/index.js +3 -1
  81. package/dist/src/insecure/index.js.map +1 -1
  82. package/dist/src/libp2p.d.ts +5 -3
  83. package/dist/src/libp2p.d.ts.map +1 -1
  84. package/dist/src/libp2p.js +47 -50
  85. package/dist/src/libp2p.js.map +1 -1
  86. package/dist/src/registrar.d.ts +5 -2
  87. package/dist/src/registrar.d.ts.map +1 -1
  88. package/dist/src/registrar.js +25 -23
  89. package/dist/src/registrar.js.map +1 -1
  90. package/dist/src/transport-manager.d.ts +9 -3
  91. package/dist/src/transport-manager.d.ts.map +1 -1
  92. package/dist/src/transport-manager.js +23 -9
  93. package/dist/src/transport-manager.js.map +1 -1
  94. package/dist/src/upgrader.d.ts +14 -11
  95. package/dist/src/upgrader.d.ts.map +1 -1
  96. package/dist/src/upgrader.js +14 -10
  97. package/dist/src/upgrader.js.map +1 -1
  98. package/package.json +46 -45
  99. package/src/address-manager/index.ts +12 -8
  100. package/src/autonat/index.ts +6 -9
  101. package/src/circuit-relay/server/advert-service.ts +2 -2
  102. package/src/circuit-relay/server/index.ts +7 -3
  103. package/src/circuit-relay/transport/discovery.ts +3 -1
  104. package/src/circuit-relay/transport/index.ts +10 -2
  105. package/src/circuit-relay/transport/listener.ts +8 -4
  106. package/src/circuit-relay/transport/reservation-store.ts +15 -7
  107. package/src/circuit-relay/utils.ts +3 -1
  108. package/src/components.ts +19 -17
  109. package/src/config.ts +0 -9
  110. package/src/connection-manager/auto-dial.ts +183 -0
  111. package/src/connection-manager/connection-pruner.ts +157 -0
  112. package/src/connection-manager/constants.ts +60 -0
  113. package/src/connection-manager/dial-queue.ts +512 -0
  114. package/src/connection-manager/index.ts +191 -340
  115. package/src/connection-manager/utils.ts +76 -0
  116. package/src/content-routing/index.ts +1 -1
  117. package/src/content-routing/utils.ts +3 -1
  118. package/src/fetch/index.ts +4 -4
  119. package/src/identify/index.ts +100 -140
  120. package/src/index.ts +2 -20
  121. package/src/insecure/index.ts +9 -5
  122. package/src/libp2p.ts +54 -60
  123. package/src/registrar.ts +29 -25
  124. package/src/transport-manager.ts +31 -11
  125. package/src/upgrader.ts +31 -22
  126. package/src/version.ts +1 -1
  127. package/dist/src/connection-manager/auto-dialler.d.ts +0 -42
  128. package/dist/src/connection-manager/auto-dialler.d.ts.map +0 -1
  129. package/dist/src/connection-manager/auto-dialler.js +0 -105
  130. package/dist/src/connection-manager/auto-dialler.js.map +0 -1
  131. package/dist/src/connection-manager/dialer/dial-request.d.ts +0 -27
  132. package/dist/src/connection-manager/dialer/dial-request.d.ts.map +0 -1
  133. package/dist/src/connection-manager/dialer/dial-request.js +0 -114
  134. package/dist/src/connection-manager/dialer/dial-request.js.map +0 -1
  135. package/dist/src/connection-manager/dialer/index.d.ts +0 -121
  136. package/dist/src/connection-manager/dialer/index.d.ts.map +0 -1
  137. package/dist/src/connection-manager/dialer/index.js +0 -324
  138. package/dist/src/connection-manager/dialer/index.js.map +0 -1
  139. package/dist/src/connection-manager/latency-monitor.d.ts +0 -120
  140. package/dist/src/connection-manager/latency-monitor.d.ts.map +0 -1
  141. package/dist/src/connection-manager/latency-monitor.js +0 -220
  142. package/dist/src/connection-manager/latency-monitor.js.map +0 -1
  143. package/dist/src/connection-manager/visibility-change-emitter.d.ts +0 -66
  144. package/dist/src/connection-manager/visibility-change-emitter.d.ts.map +0 -1
  145. package/dist/src/connection-manager/visibility-change-emitter.js +0 -118
  146. package/dist/src/connection-manager/visibility-change-emitter.js.map +0 -1
  147. package/dist/src/constants.d.ts +0 -27
  148. package/dist/src/constants.d.ts.map +0 -1
  149. package/dist/src/constants.js +0 -31
  150. package/dist/src/constants.js.map +0 -1
  151. package/dist/src/peer-record-updater.d.ts +0 -24
  152. package/dist/src/peer-record-updater.d.ts.map +0 -1
  153. package/dist/src/peer-record-updater.js +0 -44
  154. package/dist/src/peer-record-updater.js.map +0 -1
  155. package/dist/typedoc-urls.json +0 -41
  156. package/src/connection-manager/auto-dialler.ts +0 -158
  157. package/src/connection-manager/dialer/dial-request.ts +0 -144
  158. package/src/connection-manager/dialer/index.ts +0 -461
  159. package/src/connection-manager/latency-monitor.ts +0 -319
  160. package/src/connection-manager/visibility-change-emitter.ts +0 -135
  161. package/src/constants.ts +0 -36
  162. package/src/peer-record-updater.ts +0 -65
@@ -0,0 +1,512 @@
1
+ import { logger } from '@libp2p/logger'
2
+ import { AbortError, CodeError } from '@libp2p/interfaces/errors'
3
+ import { Multiaddr, Resolver, resolvers } from '@multiformats/multiaddr'
4
+ import { TimeoutController } from 'timeout-abort-controller'
5
+ import { publicAddressesFirst } from '@libp2p/utils/address-sort'
6
+ import { codes } from '../errors.js'
7
+ import {
8
+ DIAL_TIMEOUT,
9
+ MAX_PARALLEL_DIALS_PER_PEER,
10
+ MAX_PARALLEL_DIALS,
11
+ MAX_PEER_ADDRS_TO_DIAL
12
+ } from './constants.js'
13
+ import type { Connection } from '@libp2p/interface-connection'
14
+ import type { AbortOptions } from '@libp2p/interfaces'
15
+ import type { PeerId } from '@libp2p/interface-peer-id'
16
+ import { getPeerAddress } from '../get-peer.js'
17
+ import type { Address, PeerStore } from '@libp2p/interface-peer-store'
18
+ import type { Metric, Metrics } from '@libp2p/interface-metrics'
19
+ import type { TransportManager } from '@libp2p/interface-transport'
20
+ import type { ConnectionGater } from '@libp2p/interface-connection-gater'
21
+ import PQueue from 'p-queue'
22
+ import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
23
+ import { combineSignals, resolveMultiaddrs } from './utils.js'
24
+ import pDefer from 'p-defer'
25
+ import type { ClearableSignal } from 'any-signal'
26
+ import type { AddressSorter } from '@libp2p/interface-libp2p'
27
+
28
+ const log = logger('libp2p:connection-manager:dial-queue')
29
+
30
+ export type PendingDialStatus = 'queued' | 'active' | 'error' | 'success'
31
+
32
+ export interface PendingDial {
33
+ id: string
34
+ status: PendingDialStatus
35
+ peerId?: PeerId
36
+ multiaddrs: Multiaddr[]
37
+ promise: Promise<Connection>
38
+ }
39
+
40
+ export interface PendingDialTarget {
41
+ resolve: (value: any) => void
42
+ reject: (err: Error) => void
43
+ }
44
+
45
+ export interface DialOptions extends AbortOptions {
46
+ priority?: number
47
+ }
48
+
49
+ interface DialerInit {
50
+ addressSorter?: AddressSorter
51
+ maxParallelDials?: number
52
+ maxPeerAddrsToDial?: number
53
+ maxParallelDialsPerPeer?: number
54
+ dialTimeout?: number
55
+ resolvers?: Record<string, Resolver>
56
+ }
57
+
58
+ const defaultOptions = {
59
+ addressSorter: publicAddressesFirst,
60
+ maxParallelDials: MAX_PARALLEL_DIALS,
61
+ maxPeerAddrsToDial: MAX_PEER_ADDRS_TO_DIAL,
62
+ maxParallelDialsPerPeer: MAX_PARALLEL_DIALS_PER_PEER,
63
+ dialTimeout: DIAL_TIMEOUT,
64
+ resolvers: {
65
+ dnsaddr: dnsaddrResolver
66
+ }
67
+ }
68
+
69
+ interface DialQueueComponents {
70
+ peerId: PeerId
71
+ metrics?: Metrics
72
+ peerStore: PeerStore
73
+ transportManager: TransportManager
74
+ connectionGater: ConnectionGater
75
+ }
76
+
77
+ export class DialQueue {
78
+ public pendingDials: PendingDial[]
79
+ public queue: PQueue
80
+ private readonly peerId: PeerId
81
+ private readonly peerStore: PeerStore
82
+ private readonly connectionGater: ConnectionGater
83
+ private readonly transportManager: TransportManager
84
+ private readonly addressSorter: AddressSorter
85
+ private readonly maxPeerAddrsToDial: number
86
+ private readonly maxParallelDialsPerPeer: number
87
+ private readonly dialTimeout: number
88
+ private readonly inProgressDialCount?: Metric
89
+ private readonly pendingDialCount?: Metric
90
+ private readonly shutDownController: AbortController
91
+
92
+ constructor (components: DialQueueComponents, init: DialerInit = {}) {
93
+ this.addressSorter = init.addressSorter ?? defaultOptions.addressSorter
94
+ this.maxPeerAddrsToDial = init.maxPeerAddrsToDial ?? defaultOptions.maxPeerAddrsToDial
95
+ this.maxParallelDialsPerPeer = init.maxParallelDialsPerPeer ?? defaultOptions.maxParallelDialsPerPeer
96
+ this.dialTimeout = init.dialTimeout ?? defaultOptions.dialTimeout
97
+
98
+ this.peerId = components.peerId
99
+ this.peerStore = components.peerStore
100
+ this.connectionGater = components.connectionGater
101
+ this.transportManager = components.transportManager
102
+ this.shutDownController = new AbortController()
103
+
104
+ this.pendingDialCount = components.metrics?.registerMetric('libp2p_dialler_pending_dials')
105
+ this.inProgressDialCount = components.metrics?.registerMetric('libp2p_dialler_in_progress_dials')
106
+ this.pendingDials = []
107
+
108
+ for (const [key, value] of Object.entries(init.resolvers ?? {})) {
109
+ resolvers.set(key, value)
110
+ }
111
+
112
+ // controls dial concurrency
113
+ this.queue = new PQueue({
114
+ concurrency: init.maxParallelDials ?? defaultOptions.maxParallelDials
115
+ })
116
+
117
+ // a job was added to the queue
118
+ this.queue.on('add', () => {
119
+ this.pendingDialCount?.update(this.queue.size)
120
+ this.inProgressDialCount?.update(this.queue.pending)
121
+ })
122
+ // a queued job started
123
+ this.queue.on('active', () => {
124
+ this.pendingDialCount?.update(this.queue.size)
125
+ this.inProgressDialCount?.update(this.queue.pending)
126
+ })
127
+ // a started job completed without error
128
+ this.queue.on('completed', () => {
129
+ this.pendingDialCount?.update(this.queue.size)
130
+ this.inProgressDialCount?.update(this.queue.pending)
131
+ })
132
+ // a started job errored
133
+ this.queue.on('error', (err) => {
134
+ log.error('error in dial queue', err)
135
+ this.pendingDialCount?.update(this.queue.size)
136
+ this.inProgressDialCount?.update(this.queue.pending)
137
+ })
138
+ // all queued jobs have been started
139
+ this.queue.on('empty', () => {
140
+ this.pendingDialCount?.update(this.queue.size)
141
+ this.inProgressDialCount?.update(this.queue.pending)
142
+ })
143
+ // add started jobs have run and the queue is empty
144
+ this.queue.on('idle', () => {
145
+ this.pendingDialCount?.update(this.queue.size)
146
+ this.inProgressDialCount?.update(this.queue.pending)
147
+ })
148
+ }
149
+
150
+ /**
151
+ * Clears any pending dials
152
+ */
153
+ stop (): void {
154
+ this.shutDownController.abort()
155
+ }
156
+
157
+ /**
158
+ * Connects to a given peer, multiaddr or list of multiaddrs.
159
+ *
160
+ * If a peer is passed, all known multiaddrs will be tried. If a multiaddr or
161
+ * multiaddrs are passed only those will be dialled.
162
+ *
163
+ * Where a list of multiaddrs is passed, if any contain a peer id then all
164
+ * multiaddrs in the list must contain the same peer id.
165
+ *
166
+ * The dial to the first address that is successfully able to upgrade a connection
167
+ * will be used, all other dials will be aborted when that happens.
168
+ */
169
+ async dial (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], options: DialOptions = {}): Promise<Connection> {
170
+ const { peerId, multiaddrs } = getPeerAddress(peerIdOrMultiaddr)
171
+
172
+ const addrs: Address[] = multiaddrs.map(multiaddr => ({
173
+ multiaddr,
174
+ isCertified: false
175
+ }))
176
+
177
+ // create abort conditions - need to do this before `calculateMultiaddrs` as we may be about to
178
+ // resolve a dns addr which can time out
179
+ const { timeoutController, signal } = this.createDialAbortControllers(options.signal)
180
+ let addrsToDial: Address[]
181
+
182
+ try {
183
+ // load addresses from address book, resolve and dnsaddrs, filter undiallables, add peer IDs, etc
184
+ addrsToDial = await this.calculateMultiaddrs(peerId, addrs, {
185
+ ...options,
186
+ signal
187
+ })
188
+ } catch (err) {
189
+ signal.clear()
190
+ timeoutController.clear()
191
+ throw err
192
+ }
193
+
194
+ // ready to dial, all async work finished - make sure we don't have any
195
+ // pending dials in progress for this peer or set of multiaddrs
196
+ const existingDial = this.pendingDials.find(dial => {
197
+ // is the dial for the same peer id?
198
+ if (dial.peerId != null && peerId != null && dial.peerId.equals(peerId)) {
199
+ return true
200
+ }
201
+
202
+ // is the dial for the same set of multiaddrs?
203
+ if (addrsToDial.map(({ multiaddr }) => multiaddr.toString()).join() === dial.multiaddrs.map(multiaddr => multiaddr.toString()).join()) {
204
+ return true
205
+ }
206
+
207
+ return false
208
+ })
209
+
210
+ if (existingDial != null) {
211
+ log('joining existing dial target for %p', peerId)
212
+ signal.clear()
213
+ timeoutController.clear()
214
+ return await existingDial.promise
215
+ }
216
+
217
+ log('creating dial target for', addrsToDial.map(({ multiaddr }) => multiaddr.toString()))
218
+ // @ts-expect-error .promise property is set below
219
+ const pendingDial: PendingDial = {
220
+ id: randomId(),
221
+ status: 'queued',
222
+ peerId,
223
+ multiaddrs: addrsToDial.map(({ multiaddr }) => multiaddr)
224
+ }
225
+
226
+ pendingDial.promise = this.performDial(pendingDial, {
227
+ ...options,
228
+ signal
229
+ })
230
+ .finally(() => {
231
+ // remove our pending dial entry
232
+ this.pendingDials = this.pendingDials.filter(p => p.id !== pendingDial.id)
233
+
234
+ // clean up abort signals/controllers
235
+ signal.clear()
236
+ timeoutController.clear()
237
+ })
238
+ .catch(err => {
239
+ log.error('dial failed to %s', addrsToDial.map(({ multiaddr }) => multiaddr.toString()).join(', '), err)
240
+
241
+ // Error is a timeout
242
+ if (timeoutController.signal.aborted) {
243
+ const error = new CodeError(err.message, codes.ERR_TIMEOUT)
244
+ throw error
245
+ }
246
+
247
+ throw err
248
+ })
249
+
250
+ // let other dials join this one
251
+ this.pendingDials.push(pendingDial)
252
+
253
+ return await pendingDial.promise
254
+ }
255
+
256
+ private createDialAbortControllers (userSignal?: AbortSignal): { timeoutController: TimeoutController, signal: ClearableSignal } {
257
+ // ensure we throw if the dial takes longer than the dial timeout
258
+ const timeoutController = new TimeoutController(this.dialTimeout)
259
+
260
+ // let any signal abort the dial
261
+ const signal = combineSignals(
262
+ timeoutController.signal,
263
+ this.shutDownController.signal,
264
+ userSignal
265
+ )
266
+
267
+ return { timeoutController, signal }
268
+ }
269
+
270
+ private async calculateMultiaddrs (peerId?: PeerId, addrs: Address[] = [], options: DialOptions = {}): Promise<Address[]> {
271
+ // if a peer id or multiaddr(s) with a peer id, make sure it isn't our peer id and that we are allowed to dial it
272
+ if (peerId != null) {
273
+ if (this.peerId.equals(peerId)) {
274
+ throw new CodeError('Tried to dial self', codes.ERR_DIALED_SELF)
275
+ }
276
+
277
+ if ((await this.connectionGater.denyDialPeer?.(peerId)) === true) {
278
+ throw new CodeError('The dial request is blocked by gater.allowDialPeer', codes.ERR_PEER_DIAL_INTERCEPTED)
279
+ }
280
+
281
+ // if just a peer id was passed, load available multiaddrs for this peer from the address book
282
+ if (addrs.length === 0) {
283
+ log('loading multiaddrs for %p', peerId)
284
+ try {
285
+ const peer = await this.peerStore.get(peerId)
286
+ addrs.push(...peer.addresses)
287
+ log('loaded multiaddrs for %p', peerId, addrs.map(({ multiaddr }) => multiaddr.toString()))
288
+ } catch (err: any) {
289
+ if (err.code !== codes.ERR_NOT_FOUND) {
290
+ throw err
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ // resolve addresses - this can result in a one-to-many translation when dnsaddrs are resolved
297
+ const resolvedAddresses = (await Promise.all(
298
+ addrs.map(async addr => {
299
+ const result = await resolveMultiaddrs(addr.multiaddr, options)
300
+
301
+ if (result.length === 1 && result[0].equals(addr.multiaddr)) {
302
+ return addr
303
+ }
304
+
305
+ return result.map(multiaddr => ({
306
+ multiaddr,
307
+ isCertified: false
308
+ }))
309
+ })
310
+ ))
311
+ .flat()
312
+
313
+ // filter out any multiaddrs that we do not have transports for
314
+ const filteredAddrs = resolvedAddresses.filter(addr => Boolean(this.transportManager.transportForMultiaddr(addr.multiaddr)))
315
+
316
+ // deduplicate addresses
317
+ const dedupedAddrs: Map<string, Address> = new Map()
318
+
319
+ for (const addr of filteredAddrs) {
320
+ const maStr = addr.multiaddr.toString()
321
+ const existing = dedupedAddrs.get(maStr)
322
+
323
+ if (existing != null) {
324
+ existing.isCertified = existing.isCertified || addr.isCertified || false
325
+ continue
326
+ }
327
+
328
+ dedupedAddrs.set(maStr, addr)
329
+ }
330
+
331
+ let dedupedMultiaddrs = [...dedupedAddrs.values()]
332
+
333
+ if (dedupedMultiaddrs.length === 0 || dedupedMultiaddrs.length > this.maxPeerAddrsToDial) {
334
+ log('addresses for %p before filtering', peerId ?? 'unknown peer', resolvedAddresses.map(({ multiaddr }) => multiaddr.toString()))
335
+ log('addresses for %p after filtering', peerId ?? 'unknown peer', dedupedMultiaddrs.map(({ multiaddr }) => multiaddr.toString()))
336
+ }
337
+
338
+ // make sure we actually have some addresses to dial
339
+ if (dedupedMultiaddrs.length === 0) {
340
+ throw new CodeError('The dial request has no valid addresses', codes.ERR_NO_VALID_ADDRESSES)
341
+ }
342
+
343
+ // make sure we don't have too many addresses to dial
344
+ if (dedupedMultiaddrs.length > this.maxPeerAddrsToDial) {
345
+ throw new CodeError('dial with more addresses than allowed', codes.ERR_TOO_MANY_ADDRESSES)
346
+ }
347
+
348
+ // ensure the peer id is appended to the multiaddr
349
+ if (peerId != null) {
350
+ const peerIdMultiaddr = `/p2p/${peerId.toString()}`
351
+ dedupedMultiaddrs = dedupedMultiaddrs.map(addr => {
352
+ const addressPeerId = addr.multiaddr.getPeerId()
353
+ const lastProto = addr.multiaddr.protos().pop()
354
+
355
+ // do not append peer id to path multiaddrs
356
+ if (lastProto?.path === true) {
357
+ return addr
358
+ }
359
+
360
+ // append peer id to multiaddr if it is not already present
361
+ if (addressPeerId !== peerId.toString()) {
362
+ return {
363
+ multiaddr: addr.multiaddr.encapsulate(peerIdMultiaddr),
364
+ isCertified: addr.isCertified
365
+ }
366
+ }
367
+
368
+ return addr
369
+ })
370
+ }
371
+
372
+ const gatedAdrs: Address[] = []
373
+
374
+ for (const addr of dedupedMultiaddrs) {
375
+ if (this.connectionGater.denyDialMultiaddr != null && await this.connectionGater.denyDialMultiaddr(addr.multiaddr)) {
376
+ continue
377
+ }
378
+
379
+ gatedAdrs.push(addr)
380
+ }
381
+
382
+ return gatedAdrs.sort(this.addressSorter)
383
+ }
384
+
385
+ private async performDial (pendingDial: PendingDial, options: DialOptions = {}): Promise<Connection> {
386
+ const dialAbortControllers: Array<(AbortController | undefined)> = pendingDial.multiaddrs.map(() => new AbortController())
387
+
388
+ try {
389
+ // internal peer dial queue to ensure we only dial the configured number of addresses
390
+ // per peer at the same time to prevent one peer with a lot of addresses swamping
391
+ // the dial queue
392
+ const peerDialQueue = new PQueue({
393
+ concurrency: this.maxParallelDialsPerPeer
394
+ })
395
+ peerDialQueue.on('error', (err) => {
396
+ log.error('error dialling', err)
397
+ })
398
+
399
+ const conn = await Promise.any(pendingDial.multiaddrs.map(async (addr, i) => {
400
+ const controller = dialAbortControllers[i]
401
+
402
+ if (controller == null) {
403
+ throw new CodeError('dialAction did not come with an AbortController', codes.ERR_INVALID_PARAMETERS)
404
+ }
405
+
406
+ // let any signal abort the dial
407
+ const signal = combineSignals(controller.signal, options.signal)
408
+ signal.addEventListener('abort', () => {
409
+ log('dial to %s aborted', addr)
410
+ })
411
+ const deferred = pDefer<Connection>()
412
+
413
+ await peerDialQueue.add(async () => {
414
+ if (signal.aborted) {
415
+ log('dial to %s was aborted before reaching the head of the peer dial queue', addr)
416
+ deferred.reject(new AbortError())
417
+ return
418
+ }
419
+
420
+ // add the individual dial to the dial queue so we don't breach maxConcurrentDials
421
+ await this.queue.add(async () => {
422
+ try {
423
+ if (signal.aborted) {
424
+ log('dial to %s was aborted before reaching the head of the dial queue', addr)
425
+ deferred.reject(new AbortError())
426
+ return
427
+ }
428
+
429
+ // update dial status
430
+ pendingDial.status = 'active'
431
+
432
+ const conn = await this.transportManager.dial(addr, {
433
+ ...options,
434
+ signal
435
+ })
436
+
437
+ if (controller.signal.aborted) {
438
+ // another dial succeeded faster than this one
439
+ log('multiple dials succeeded, closing superfluous connection')
440
+
441
+ conn.close().catch(err => {
442
+ log.error('error closing superfluous connection', err)
443
+ })
444
+
445
+ deferred.reject(new AbortError())
446
+ return
447
+ }
448
+
449
+ // remove the successful AbortController so it is not aborted
450
+ dialAbortControllers[i] = undefined
451
+
452
+ // immediately abort any other dials
453
+ dialAbortControllers.forEach(c => {
454
+ if (c !== undefined) {
455
+ c.abort()
456
+ }
457
+ })
458
+
459
+ log('dial to %s succeeded', addr)
460
+
461
+ // resolve the connection promise
462
+ deferred.resolve(conn)
463
+ } catch (err: any) {
464
+ // something only went wrong if our signal was not aborted
465
+ log.error('error during dial of %s', addr, err)
466
+ deferred.reject(err)
467
+ }
468
+ }, {
469
+ ...options,
470
+ signal
471
+ }).catch(err => {
472
+ deferred.reject(err)
473
+ })
474
+ }, {
475
+ signal
476
+ }).catch(err => {
477
+ deferred.reject(err)
478
+ }).finally(() => {
479
+ signal.clear()
480
+ })
481
+
482
+ return await deferred.promise
483
+ }))
484
+
485
+ // dial succeeded or failed
486
+ if (conn == null) {
487
+ throw new CodeError('successful dial led to empty object returned from peer dial queue', codes.ERR_TRANSPORT_DIAL_FAILED)
488
+ }
489
+
490
+ pendingDial.status = 'success'
491
+
492
+ return conn
493
+ } catch (err: any) {
494
+ pendingDial.status = 'error'
495
+
496
+ // if we only dialled one address, unwrap the AggregateError to provide more
497
+ // useful feedback to the user
498
+ if (pendingDial.multiaddrs.length === 1 && err.name === 'AggregateError') {
499
+ throw err.errors[0]
500
+ }
501
+
502
+ throw err
503
+ }
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Returns a random string
509
+ */
510
+ function randomId (): string {
511
+ return `${(parseInt(String(Math.random() * 1e9), 10)).toString()}${Date.now()}`
512
+ }