libp2p 1.0.12 → 1.1.0-444d83751

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 (38) hide show
  1. package/dist/index.min.js +34 -34
  2. package/dist/src/connection-manager/auto-dial.d.ts +2 -1
  3. package/dist/src/connection-manager/auto-dial.d.ts.map +1 -1
  4. package/dist/src/connection-manager/auto-dial.js +8 -6
  5. package/dist/src/connection-manager/auto-dial.js.map +1 -1
  6. package/dist/src/connection-manager/dial-queue.d.ts +11 -16
  7. package/dist/src/connection-manager/dial-queue.d.ts.map +1 -1
  8. package/dist/src/connection-manager/dial-queue.js +142 -257
  9. package/dist/src/connection-manager/dial-queue.js.map +1 -1
  10. package/dist/src/connection-manager/index.d.ts +5 -3
  11. package/dist/src/connection-manager/index.d.ts.map +1 -1
  12. package/dist/src/connection-manager/index.js +15 -9
  13. package/dist/src/connection-manager/index.js.map +1 -1
  14. package/dist/src/connection-manager/utils.d.ts +0 -2
  15. package/dist/src/connection-manager/utils.d.ts.map +1 -1
  16. package/dist/src/connection-manager/utils.js +0 -15
  17. package/dist/src/connection-manager/utils.js.map +1 -1
  18. package/dist/src/content-routing.d.ts +0 -1
  19. package/dist/src/content-routing.d.ts.map +1 -1
  20. package/dist/src/content-routing.js +6 -31
  21. package/dist/src/content-routing.js.map +1 -1
  22. package/dist/src/peer-routing.d.ts +1 -1
  23. package/dist/src/peer-routing.d.ts.map +1 -1
  24. package/dist/src/peer-routing.js +12 -14
  25. package/dist/src/peer-routing.js.map +1 -1
  26. package/dist/src/version.d.ts +1 -1
  27. package/dist/src/version.d.ts.map +1 -1
  28. package/dist/src/version.js +1 -1
  29. package/dist/src/version.js.map +1 -1
  30. package/package.json +31 -32
  31. package/src/connection-manager/auto-dial.ts +11 -8
  32. package/src/connection-manager/dial-queue.ts +161 -290
  33. package/src/connection-manager/index.ts +22 -12
  34. package/src/connection-manager/utils.ts +0 -20
  35. package/src/content-routing.ts +8 -37
  36. package/src/peer-routing.ts +12 -15
  37. package/src/version.ts +1 -1
  38. package/dist/typedoc-urls.json +0 -10
@@ -1,11 +1,11 @@
1
- import { AbortError, CodeError, ERR_TIMEOUT, setMaxListeners } from '@libp2p/interface'
1
+ /* eslint-disable max-depth */
2
+ import { CodeError, AggregateCodeError, ERR_TIMEOUT, setMaxListeners } from '@libp2p/interface'
2
3
  import { PeerMap } from '@libp2p/peer-collections'
3
4
  import { defaultAddressSort } from '@libp2p/utils/address-sort'
4
- import { type Multiaddr, type Resolver, resolvers } from '@multiformats/multiaddr'
5
+ import { Queue, type QueueAddOptions } from '@libp2p/utils/queue'
6
+ import { type Multiaddr, type Resolver, resolvers, multiaddr } from '@multiformats/multiaddr'
5
7
  import { dnsaddrResolver } from '@multiformats/multiaddr/resolvers'
6
8
  import { type ClearableSignal, anySignal } from 'any-signal'
7
- import pDefer from 'p-defer'
8
- import PQueue from 'p-queue'
9
9
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
10
10
  import { codes } from '../errors.js'
11
11
  import { getPeerAddress } from '../get-peer.js'
@@ -15,8 +15,8 @@ import {
15
15
  MAX_PEER_ADDRS_TO_DIAL,
16
16
  LAST_DIAL_FAILURE_KEY
17
17
  } from './constants.js'
18
- import { combineSignals, resolveMultiaddrs } from './utils.js'
19
- import type { AddressSorter, AbortOptions, PendingDial, ComponentLogger, Logger, Connection, ConnectionGater, Metric, Metrics, PeerId, Address, PeerStore } from '@libp2p/interface'
18
+ import { resolveMultiaddrs } from './utils.js'
19
+ import type { AddressSorter, AbortOptions, ComponentLogger, Logger, Connection, ConnectionGater, Metrics, PeerId, Address, PeerStore, PeerRouting } from '@libp2p/interface'
20
20
  import type { TransportManager } from '@libp2p/interface-internal'
21
21
 
22
22
  export interface PendingDialTarget {
@@ -29,8 +29,9 @@ export interface DialOptions extends AbortOptions {
29
29
  force?: boolean
30
30
  }
31
31
 
32
- interface PendingDialInternal extends PendingDial {
33
- promise: Promise<Connection>
32
+ interface DialQueueJobOptions extends QueueAddOptions {
33
+ peerId?: PeerId
34
+ multiaddrs: Set<string>
34
35
  }
35
36
 
36
37
  interface DialerInit {
@@ -56,23 +57,18 @@ interface DialQueueComponents {
56
57
  peerId: PeerId
57
58
  metrics?: Metrics
58
59
  peerStore: PeerStore
60
+ peerRouting: PeerRouting
59
61
  transportManager: TransportManager
60
62
  connectionGater: ConnectionGater
61
63
  logger: ComponentLogger
62
64
  }
63
65
 
64
66
  export class DialQueue {
65
- public pendingDials: PendingDialInternal[]
66
- public queue: PQueue
67
- private readonly peerId: PeerId
68
- private readonly peerStore: PeerStore
69
- private readonly connectionGater: ConnectionGater
70
- private readonly transportManager: TransportManager
67
+ public queue: Queue<Connection, DialQueueJobOptions>
68
+ private readonly components: DialQueueComponents
71
69
  private readonly addressSorter: AddressSorter
72
70
  private readonly maxPeerAddrsToDial: number
73
71
  private readonly dialTimeout: number
74
- private readonly inProgressDialCount?: Metric
75
- private readonly pendingDialCount?: Metric
76
72
  private shutDownController: AbortController
77
73
  private readonly connections: PeerMap<Connection[]>
78
74
  private readonly log: Logger
@@ -84,57 +80,24 @@ export class DialQueue {
84
80
  this.connections = init.connections ?? new PeerMap()
85
81
  this.log = components.logger.forComponent('libp2p:connection-manager:dial-queue')
86
82
 
87
- this.peerId = components.peerId
88
- this.peerStore = components.peerStore
89
- this.connectionGater = components.connectionGater
90
- this.transportManager = components.transportManager
83
+ this.components = components
91
84
  this.shutDownController = new AbortController()
92
85
 
93
86
  setMaxListeners(Infinity, this.shutDownController.signal)
94
87
 
95
- this.pendingDialCount = components.metrics?.registerMetric('libp2p_dial_queue_pending_dials')
96
- this.inProgressDialCount = components.metrics?.registerMetric('libp2p_dial_queue_in_progress_dials')
97
- this.pendingDials = []
98
-
99
88
  for (const [key, value] of Object.entries(init.resolvers ?? {})) {
100
89
  resolvers.set(key, value)
101
90
  }
102
91
 
103
92
  // controls dial concurrency
104
- this.queue = new PQueue({
105
- concurrency: init.maxParallelDials ?? defaultOptions.maxParallelDials
106
- })
107
-
108
- // a job was added to the queue
109
- this.queue.on('add', () => {
110
- this.pendingDialCount?.update(this.queue.size)
111
- this.inProgressDialCount?.update(this.queue.pending)
112
- })
113
- // a queued job started
114
- this.queue.on('active', () => {
115
- this.pendingDialCount?.update(this.queue.size)
116
- this.inProgressDialCount?.update(this.queue.pending)
117
- })
118
- // a started job completed without error
119
- this.queue.on('completed', () => {
120
- this.pendingDialCount?.update(this.queue.size)
121
- this.inProgressDialCount?.update(this.queue.pending)
93
+ this.queue = new Queue({
94
+ concurrency: init.maxParallelDials ?? defaultOptions.maxParallelDials,
95
+ metricName: 'libp2p_dial_queue',
96
+ metrics: components.metrics
122
97
  })
123
98
  // a started job errored
124
- this.queue.on('error', (err) => {
125
- this.log.error('error in dial queue', err)
126
- this.pendingDialCount?.update(this.queue.size)
127
- this.inProgressDialCount?.update(this.queue.pending)
128
- })
129
- // all queued jobs have been started
130
- this.queue.on('empty', () => {
131
- this.pendingDialCount?.update(this.queue.size)
132
- this.inProgressDialCount?.update(this.queue.pending)
133
- })
134
- // add started jobs have run and the queue is empty
135
- this.queue.on('idle', () => {
136
- this.pendingDialCount?.update(this.queue.size)
137
- this.inProgressDialCount?.update(this.queue.pending)
99
+ this.queue.addEventListener('error', (event) => {
100
+ this.log.error('error in dial queue', event.detail)
138
101
  })
139
102
  }
140
103
 
@@ -147,7 +110,7 @@ export class DialQueue {
147
110
  */
148
111
  stop (): void {
149
112
  this.shutDownController.abort()
150
- this.queue.clear()
113
+ this.queue.abort()
151
114
  }
152
115
 
153
116
  /**
@@ -159,42 +122,25 @@ export class DialQueue {
159
122
  * Where a list of multiaddrs is passed, if any contain a peer id then all
160
123
  * multiaddrs in the list must contain the same peer id.
161
124
  *
162
- * The dial to the first address that is successfully able to upgrade a connection
163
- * will be used, all other dials will be aborted when that happens.
125
+ * The dial to the first address that is successfully able to upgrade a
126
+ * connection will be used, all other dials will be aborted when that happens.
164
127
  */
165
128
  async dial (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], options: DialOptions = {}): Promise<Connection> {
166
129
  const { peerId, multiaddrs } = getPeerAddress(peerIdOrMultiaddr)
167
130
 
168
- const addrs: Address[] = multiaddrs.map(multiaddr => ({
169
- multiaddr,
170
- isCertified: false
171
- }))
172
-
173
- // create abort conditions - need to do this before `calculateMultiaddrs` as we may be about to
174
- // resolve a dns addr which can time out
175
- const signal = this.createDialAbortControllers(options.signal)
176
- let addrsToDial: Address[]
177
-
178
- try {
179
- // load addresses from address book, resolve and dnsaddrs, filter undiallables, add peer IDs, etc
180
- addrsToDial = await this.calculateMultiaddrs(peerId, addrs, {
181
- ...options,
182
- signal
183
- })
184
- } catch (err) {
185
- signal.clear()
186
- throw err
187
- }
188
-
189
131
  // make sure we don't have an existing connection to any of the addresses we
190
132
  // are about to dial
191
- let existingConnection = Array.from(this.connections.values()).flat().find(conn => {
133
+ const existingConnection = Array.from(this.connections.values()).flat().find(conn => {
192
134
  if (options.force === true) {
193
135
  return false
194
136
  }
195
137
 
196
- return addrsToDial.find(addr => {
197
- return addr.multiaddr.equals(conn.remoteAddr)
138
+ if (conn.remotePeer.equals(peerId)) {
139
+ return true
140
+ }
141
+
142
+ return multiaddrs.find(addr => {
143
+ return addr.equals(conn.remoteAddr)
198
144
  })
199
145
  })
200
146
 
@@ -205,15 +151,22 @@ export class DialQueue {
205
151
 
206
152
  // ready to dial, all async work finished - make sure we don't have any
207
153
  // pending dials in progress for this peer or set of multiaddrs
208
- const existingDial = this.pendingDials.find(dial => {
209
- // is the dial for the same peer id?
210
- if (dial.peerId != null && peerId != null && dial.peerId.equals(peerId)) {
154
+ const existingDial = this.queue.queue.find(job => {
155
+ if (peerId?.equals(job.options.peerId) === true) {
211
156
  return true
212
157
  }
213
158
 
214
- // is the dial for the same set of multiaddrs?
215
- if (addrsToDial.map(({ multiaddr }) => multiaddr.toString()).join() === dial.multiaddrs.map(multiaddr => multiaddr.toString()).join()) {
216
- return true
159
+ // does the dial contain any of the target multiaddrs?
160
+ const addresses = job.options.multiaddrs
161
+
162
+ if (addresses == null) {
163
+ return false
164
+ }
165
+
166
+ for (const multiaddr of multiaddrs) {
167
+ if (addresses.has(multiaddr.toString())) {
168
+ return true
169
+ }
217
170
  }
218
171
 
219
172
  return false
@@ -221,83 +174,104 @@ export class DialQueue {
221
174
 
222
175
  if (existingDial != null) {
223
176
  this.log('joining existing dial target for %p', peerId)
224
- signal.clear()
225
- return existingDial.promise
226
- }
227
177
 
228
- this.log('creating dial target for', addrsToDial.map(({ multiaddr }) => multiaddr.toString()))
229
- // @ts-expect-error .promise property is set below
230
- const pendingDial: PendingDialInternal = {
231
- id: randomId(),
232
- status: 'queued',
233
- peerId,
234
- multiaddrs: addrsToDial.map(({ multiaddr }) => multiaddr)
178
+ // add all multiaddrs to the dial target
179
+ for (const multiaddr of multiaddrs) {
180
+ existingDial.options.multiaddrs.add(multiaddr.toString())
181
+ }
182
+
183
+ return existingDial.join(options)
235
184
  }
236
185
 
237
- pendingDial.promise = this.performDial(pendingDial, {
238
- ...options,
239
- signal
240
- })
241
- .finally(() => {
242
- // remove our pending dial entry
243
- this.pendingDials = this.pendingDials.filter(p => p.id !== pendingDial.id)
186
+ this.log('creating dial target for %p', peerId, multiaddrs.map(ma => ma.toString()))
244
187
 
245
- // clean up abort signals/controllers
188
+ return this.queue.add(async (options) => {
189
+ // create abort conditions - need to do this before `calculateMultiaddrs` as
190
+ // we may be about to resolve a dns addr which can time out
191
+ const signal = this.createDialAbortController(options?.signal)
192
+ let addrsToDial: Address[]
193
+
194
+ try {
195
+ // load addresses from address book, resolve and dnsaddrs, filter
196
+ // undiallables, add peer IDs, etc
197
+ addrsToDial = await this.calculateMultiaddrs(peerId, options?.multiaddrs, {
198
+ ...options,
199
+ signal
200
+ })
201
+
202
+ addrsToDial.map(({ multiaddr }) => multiaddr.toString()).forEach(addr => {
203
+ options?.multiaddrs.add(addr)
204
+ })
205
+ } catch (err) {
246
206
  signal.clear()
247
- })
248
- .catch(async err => {
249
- this.log.error('dial failed to %s', pendingDial.multiaddrs.map(ma => ma.toString()).join(', '), err)
207
+ throw err
208
+ }
209
+
210
+ try {
211
+ let dialed = 0
212
+ const errors: Error[] = []
213
+
214
+ for (const address of addrsToDial) {
215
+ if (dialed === this.maxPeerAddrsToDial) {
216
+ this.log('dialed maxPeerAddrsToDial (%d) addresses for %p, not trying any others', dialed, peerId)
217
+
218
+ throw new CodeError('Peer had more than maxPeerAddrsToDial', codes.ERR_TOO_MANY_ADDRESSES)
219
+ }
220
+
221
+ dialed++
250
222
 
251
- if (peerId != null) {
252
- // record the last failed dial
253
223
  try {
254
- await this.peerStore.patch(peerId, {
255
- metadata: {
256
- [LAST_DIAL_FAILURE_KEY]: uint8ArrayFromString(Date.now().toString())
257
- }
224
+ const conn = await this.components.transportManager.dial(address.multiaddr, {
225
+ ...options,
226
+ signal
258
227
  })
228
+
229
+ this.log('dial to %a succeeded', address.multiaddr)
230
+
231
+ return conn
259
232
  } catch (err: any) {
260
- this.log.error('could not update last dial failure key for %p', peerId, err)
233
+ this.log.error('dial failed to %a', address.multiaddr, err)
234
+
235
+ if (peerId != null) {
236
+ // record the failed dial
237
+ try {
238
+ await this.components.peerStore.patch(peerId, {
239
+ metadata: {
240
+ [LAST_DIAL_FAILURE_KEY]: uint8ArrayFromString(Date.now().toString())
241
+ }
242
+ })
243
+ } catch (err: any) {
244
+ this.log.error('could not update last dial failure key for %p', peerId, err)
245
+ }
246
+ }
247
+
248
+ // the user/dial timeout/shutdown controller signal aborted
249
+ if (signal.aborted) {
250
+ throw new CodeError(err.message, ERR_TIMEOUT)
251
+ }
252
+
253
+ errors.push(err)
261
254
  }
262
255
  }
263
256
 
264
- // Error is a timeout
265
- if (signal.aborted) {
266
- const error = new CodeError(err.message, ERR_TIMEOUT)
267
- throw error
257
+ if (errors.length === 1) {
258
+ throw errors[0]
268
259
  }
269
260
 
270
- throw err
271
- })
272
-
273
- // let other dials join this one
274
- this.pendingDials.push(pendingDial)
275
-
276
- const connection = await pendingDial.promise
277
-
278
- // we may have been dialing a multiaddr without a peer id attached but by
279
- // this point we have upgraded the connection so the remote peer information
280
- // should be available - check again that we don't already have a connection
281
- // to the remote multiaddr
282
- existingConnection = Array.from(this.connections.values()).flat().find(conn => {
283
- if (options.force === true) {
284
- return false
261
+ throw new AggregateCodeError(errors, 'All multiaddr dials failed', codes.ERR_TRANSPORT_DIAL_FAILED)
262
+ } finally {
263
+ // clean up abort signals/controllers
264
+ signal.clear()
285
265
  }
286
-
287
- return conn.id !== connection.id && conn.remoteAddr.equals(connection.remoteAddr)
266
+ }, {
267
+ peerId,
268
+ priority: options.priority,
269
+ multiaddrs: new Set(multiaddrs.map(ma => ma.toString())),
270
+ signal: options.signal
288
271
  })
289
-
290
- if (existingConnection != null) {
291
- this.log('already connected to %a', existingConnection.remoteAddr)
292
- await connection.close()
293
- return existingConnection
294
- }
295
-
296
- this.log('connection opened to %a', connection.remoteAddr)
297
- return connection
298
272
  }
299
273
 
300
- private createDialAbortControllers (userSignal?: AbortSignal): ClearableSignal {
274
+ private createDialAbortController (userSignal?: AbortSignal): ClearableSignal {
301
275
  // let any signal abort the dial
302
276
  const signal = anySignal([
303
277
  AbortSignal.timeout(this.dialTimeout),
@@ -312,22 +286,28 @@ export class DialQueue {
312
286
  }
313
287
 
314
288
  // eslint-disable-next-line complexity
315
- private async calculateMultiaddrs (peerId?: PeerId, addrs: Address[] = [], options: DialOptions = {}): Promise<Address[]> {
289
+ private async calculateMultiaddrs (peerId?: PeerId, multiaddrs: Set<string> = new Set<string>(), options: DialOptions = {}): Promise<Address[]> {
290
+ const addrs: Address[] = [...multiaddrs].map(ma => ({
291
+ multiaddr: multiaddr(ma),
292
+ isCertified: false
293
+ }))
294
+
316
295
  // 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
317
296
  if (peerId != null) {
318
- if (this.peerId.equals(peerId)) {
297
+ if (this.components.peerId.equals(peerId)) {
319
298
  throw new CodeError('Tried to dial self', codes.ERR_DIALED_SELF)
320
299
  }
321
300
 
322
- if ((await this.connectionGater.denyDialPeer?.(peerId)) === true) {
301
+ if ((await this.components.connectionGater.denyDialPeer?.(peerId)) === true) {
323
302
  throw new CodeError('The dial request is blocked by gater.allowDialPeer', codes.ERR_PEER_DIAL_INTERCEPTED)
324
303
  }
325
304
 
326
- // if just a peer id was passed, load available multiaddrs for this peer from the address book
305
+ // if just a peer id was passed, load available multiaddrs for this peer
306
+ // from the peer store
327
307
  if (addrs.length === 0) {
328
308
  this.log('loading multiaddrs for %p', peerId)
329
309
  try {
330
- const peer = await this.peerStore.get(peerId)
310
+ const peer = await this.components.peerStore.get(peerId)
331
311
  addrs.push(...peer.addresses)
332
312
  this.log('loaded multiaddrs for %p', peerId, addrs.map(({ multiaddr }) => multiaddr.toString()))
333
313
  } catch (err: any) {
@@ -336,9 +316,31 @@ export class DialQueue {
336
316
  }
337
317
  }
338
318
  }
319
+
320
+ // if we still don't have any addresses for this peer, try a lookup
321
+ // using the peer routing
322
+ if (addrs.length === 0) {
323
+ this.log('looking up multiaddrs for %p in the peer routing', peerId)
324
+
325
+ try {
326
+ const peerInfo = await this.components.peerRouting.findPeer(peerId)
327
+
328
+ this.log('found multiaddrs for %p in the peer routing', peerId, addrs.map(({ multiaddr }) => multiaddr.toString()))
329
+
330
+ addrs.push(...peerInfo.multiaddrs.map(multiaddr => ({
331
+ multiaddr,
332
+ isCertified: false
333
+ })))
334
+ } catch (err: any) {
335
+ if (err.code !== codes.ERR_NO_ROUTERS_AVAILABLE) {
336
+ this.log.error('looking up multiaddrs for %p in the peer routing failed', peerId, err)
337
+ }
338
+ }
339
+ }
339
340
  }
340
341
 
341
- // resolve addresses - this can result in a one-to-many translation when dnsaddrs are resolved
342
+ // resolve addresses - this can result in a one-to-many translation when
343
+ // dnsaddrs are resolved
342
344
  let resolvedAddresses = (await Promise.all(
343
345
  addrs.map(async addr => {
344
346
  const result = await resolveMultiaddrs(addr.multiaddr, {
@@ -383,7 +385,7 @@ export class DialQueue {
383
385
 
384
386
  const filteredAddrs = resolvedAddresses.filter(addr => {
385
387
  // filter out any multiaddrs that we do not have transports for
386
- if (this.transportManager.transportForMultiaddr(addr.multiaddr) == null) {
388
+ if (this.components.transportManager.transportForMultiaddr(addr.multiaddr) == null) {
387
389
  return false
388
390
  }
389
391
 
@@ -415,25 +417,15 @@ export class DialQueue {
415
417
 
416
418
  const dedupedMultiaddrs = [...dedupedAddrs.values()]
417
419
 
418
- if (dedupedMultiaddrs.length === 0 || dedupedMultiaddrs.length > this.maxPeerAddrsToDial) {
419
- this.log('addresses for %p before filtering', peerId ?? 'unknown peer', resolvedAddresses.map(({ multiaddr }) => multiaddr.toString()))
420
- this.log('addresses for %p after filtering', peerId ?? 'unknown peer', dedupedMultiaddrs.map(({ multiaddr }) => multiaddr.toString()))
421
- }
422
-
423
420
  // make sure we actually have some addresses to dial
424
421
  if (dedupedMultiaddrs.length === 0) {
425
422
  throw new CodeError('The dial request has no valid addresses', codes.ERR_NO_VALID_ADDRESSES)
426
423
  }
427
424
 
428
- // make sure we don't have too many addresses to dial
429
- if (dedupedMultiaddrs.length > this.maxPeerAddrsToDial) {
430
- throw new CodeError('dial with more addresses than allowed', codes.ERR_TOO_MANY_ADDRESSES)
431
- }
432
-
433
425
  const gatedAdrs: Address[] = []
434
426
 
435
427
  for (const addr of dedupedMultiaddrs) {
436
- if (this.connectionGater.denyDialMultiaddr != null && await this.connectionGater.denyDialMultiaddr(addr.multiaddr)) {
428
+ if (this.components.connectionGater.denyDialMultiaddr != null && await this.components.connectionGater.denyDialMultiaddr(addr.multiaddr)) {
437
429
  continue
438
430
  }
439
431
 
@@ -447,130 +439,9 @@ export class DialQueue {
447
439
  throw new CodeError('The connection gater denied all addresses in the dial request', codes.ERR_NO_VALID_ADDRESSES)
448
440
  }
449
441
 
450
- return sortedGatedAddrs
451
- }
452
-
453
- private async performDial (pendingDial: PendingDialInternal, options: DialOptions = {}): Promise<Connection> {
454
- const dialAbortControllers: Array<(AbortController | undefined)> = pendingDial.multiaddrs.map(() => new AbortController())
455
-
456
- try {
457
- // internal peer dial queue - only one dial per peer at a time
458
- const peerDialQueue = new PQueue({ concurrency: 1 })
459
- peerDialQueue.on('error', (err) => {
460
- this.log.error('error dialing %s %o', pendingDial.multiaddrs, err)
461
- })
462
-
463
- const conn = await Promise.any(pendingDial.multiaddrs.map(async (addr, i) => {
464
- const controller = dialAbortControllers[i]
465
-
466
- if (controller == null) {
467
- throw new CodeError('dialAction did not come with an AbortController', codes.ERR_INVALID_PARAMETERS)
468
- }
469
-
470
- // let any signal abort the dial
471
- const signal = combineSignals(controller.signal, options.signal)
472
- signal.addEventListener('abort', () => {
473
- this.log('dial to %a aborted', addr)
474
- })
475
- const deferred = pDefer<Connection>()
476
-
477
- await peerDialQueue.add(async () => {
478
- if (signal.aborted) {
479
- this.log('dial to %a was aborted before reaching the head of the peer dial queue', addr)
480
- deferred.reject(new AbortError())
481
- return
482
- }
483
-
484
- // add the individual dial to the dial queue so we don't breach maxConcurrentDials
485
- await this.queue.add(async () => {
486
- try {
487
- if (signal.aborted) {
488
- this.log('dial to %a was aborted before reaching the head of the dial queue', addr)
489
- deferred.reject(new AbortError())
490
- return
491
- }
492
-
493
- // update dial status
494
- pendingDial.status = 'active'
495
-
496
- const conn = await this.transportManager.dial(addr, {
497
- ...options,
498
- signal
499
- })
500
-
501
- if (controller.signal.aborted) {
502
- // another dial succeeded faster than this one
503
- this.log('multiple dials succeeded, closing superfluous connection')
504
-
505
- conn.close().catch(err => {
506
- this.log.error('error closing superfluous connection', err)
507
- })
508
-
509
- deferred.reject(new AbortError())
510
- return
511
- }
512
-
513
- // remove the successful AbortController so it is not aborted
514
- dialAbortControllers[i] = undefined
515
-
516
- // immediately abort any other dials
517
- dialAbortControllers.forEach(c => {
518
- if (c !== undefined) {
519
- c.abort()
520
- }
521
- })
522
-
523
- this.log('dial to %a succeeded', addr)
442
+ this.log.trace('addresses for %p before filtering', peerId ?? 'unknown peer', resolvedAddresses.map(({ multiaddr }) => multiaddr.toString()))
443
+ this.log.trace('addresses for %p after filtering', peerId ?? 'unknown peer', sortedGatedAddrs.map(({ multiaddr }) => multiaddr.toString()))
524
444
 
525
- // resolve the connection promise
526
- deferred.resolve(conn)
527
- } catch (err: any) {
528
- // something only went wrong if our signal was not aborted
529
- this.log.error('error during dial of %a', addr, err)
530
- deferred.reject(err)
531
- }
532
- }, {
533
- ...options,
534
- signal
535
- }).catch(err => {
536
- deferred.reject(err)
537
- })
538
- }, {
539
- signal
540
- }).catch(err => {
541
- deferred.reject(err)
542
- }).finally(() => {
543
- signal.clear()
544
- })
545
-
546
- return deferred.promise
547
- }))
548
-
549
- // dial succeeded or failed
550
- if (conn == null) {
551
- throw new CodeError('successful dial led to empty object returned from peer dial queue', codes.ERR_TRANSPORT_DIAL_FAILED)
552
- }
553
-
554
- pendingDial.status = 'success'
555
-
556
- return conn
557
- } catch (err: any) {
558
- pendingDial.status = 'error'
559
-
560
- // if we only dialled one address, unwrap the AggregateError to provide more
561
- // useful feedback to the user
562
- if (pendingDial.multiaddrs.length === 1 && err.name === 'AggregateError') {
563
- throw err.errors[0]
564
- }
565
-
566
- throw err
567
- }
445
+ return sortedGatedAddrs
568
446
  }
569
447
  }
570
-
571
- /**
572
- * Returns a random string
573
- */
574
- function randomId (): string {
575
- return `${(parseInt(String(Math.random() * 1e9), 10)).toString()}${Date.now()}`
576
- }
@@ -10,8 +10,9 @@ import { AutoDial } from './auto-dial.js'
10
10
  import { ConnectionPruner } from './connection-pruner.js'
11
11
  import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_MAX_QUEUE_LENGTH, AUTO_DIAL_PRIORITY, DIAL_TIMEOUT, INBOUND_CONNECTION_THRESHOLD, MAX_CONNECTIONS, MAX_INCOMING_PENDING_CONNECTIONS, MAX_PARALLEL_DIALS, MAX_PEER_ADDRS_TO_DIAL, MIN_CONNECTIONS } from './constants.js'
12
12
  import { DialQueue } from './dial-queue.js'
13
- import type { PendingDial, AddressSorter, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, MultiaddrConnection, ConnectionGater, TypedEventTarget, Metrics, PeerId, Peer, PeerStore, Startable } from '@libp2p/interface'
13
+ import type { PendingDial, AddressSorter, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, MultiaddrConnection, ConnectionGater, TypedEventTarget, Metrics, PeerId, Peer, PeerStore, Startable, PendingDialStatus, PeerRouting } from '@libp2p/interface'
14
14
  import type { ConnectionManager, OpenConnectionOptions, TransportManager } from '@libp2p/interface-internal'
15
+ import type { JobStatus } from '@libp2p/utils/queue'
15
16
 
16
17
  const DEFAULT_DIAL_PRIORITY = 50
17
18
 
@@ -81,8 +82,9 @@ export interface ConnectionManagerInit {
81
82
  maxParallelDials?: number
82
83
 
83
84
  /**
84
- * Maximum number of addresses allowed for a given peer - if a peer has more
85
- * addresses than this then the dial will fail. (default: 25)
85
+ * Maximum number of addresses allowed for a given peer before giving up
86
+ *
87
+ * @default 25
86
88
  */
87
89
  maxPeerAddrsToDial?: number
88
90
 
@@ -143,6 +145,7 @@ export interface DefaultConnectionManagerComponents {
143
145
  peerId: PeerId
144
146
  metrics?: Metrics
145
147
  peerStore: PeerStore
148
+ peerRouting: PeerRouting
146
149
  transportManager: TransportManager
147
150
  connectionGater: ConnectionGater
148
151
  events: TypedEventTarget<Libp2pEvents>
@@ -232,14 +235,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
232
235
  allow: this.allow
233
236
  })
234
237
 
235
- this.dialQueue = new DialQueue({
236
- peerId: components.peerId,
237
- metrics: components.metrics,
238
- peerStore: components.peerStore,
239
- transportManager: components.transportManager,
240
- connectionGater: components.connectionGater,
241
- logger: components.logger
242
- }, {
238
+ this.dialQueue = new DialQueue(components, {
243
239
  addressSorter: init.addressSorter ?? defaultAddressSort,
244
240
  maxParallelDials: init.maxParallelDials ?? MAX_PARALLEL_DIALS,
245
241
  maxPeerAddrsToDial: init.maxPeerAddrsToDial ?? MAX_PEER_ADDRS_TO_DIAL,
@@ -599,6 +595,20 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
599
595
  }
600
596
 
601
597
  getDialQueue (): PendingDial[] {
602
- return this.dialQueue.pendingDials
598
+ const statusMap: Record<JobStatus, PendingDialStatus> = {
599
+ queued: 'queued',
600
+ running: 'active',
601
+ errored: 'error',
602
+ complete: 'success'
603
+ }
604
+
605
+ return this.dialQueue.queue.queue.map(job => {
606
+ return {
607
+ id: job.id,
608
+ status: statusMap[job.status],
609
+ peerId: job.options.peerId,
610
+ multiaddrs: [...job.options.multiaddrs].map(ma => multiaddr(ma))
611
+ }
612
+ })
603
613
  }
604
614
  }