libp2p 2.10.0 → 3.0.0-049bfa0fa

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 (121) hide show
  1. package/README.md +39 -37
  2. package/dist/index.min.js +14 -17
  3. package/dist/index.min.js.map +4 -4
  4. package/dist/src/address-manager/dns-mappings.d.ts +1 -2
  5. package/dist/src/address-manager/dns-mappings.d.ts.map +1 -1
  6. package/dist/src/address-manager/dns-mappings.js +39 -43
  7. package/dist/src/address-manager/dns-mappings.js.map +1 -1
  8. package/dist/src/address-manager/index.d.ts.map +1 -1
  9. package/dist/src/address-manager/index.js +31 -14
  10. package/dist/src/address-manager/index.js.map +1 -1
  11. package/dist/src/address-manager/ip-mappings.d.ts +1 -0
  12. package/dist/src/address-manager/ip-mappings.d.ts.map +1 -1
  13. package/dist/src/address-manager/ip-mappings.js +51 -40
  14. package/dist/src/address-manager/ip-mappings.js.map +1 -1
  15. package/dist/src/address-manager/observed-addresses.d.ts.map +1 -1
  16. package/dist/src/address-manager/observed-addresses.js +1 -3
  17. package/dist/src/address-manager/observed-addresses.js.map +1 -1
  18. package/dist/src/address-manager/transport-addresses.d.ts.map +1 -1
  19. package/dist/src/address-manager/transport-addresses.js +6 -8
  20. package/dist/src/address-manager/transport-addresses.js.map +1 -1
  21. package/dist/src/config/connection-gater.browser.d.ts.map +1 -1
  22. package/dist/src/config/connection-gater.browser.js +8 -22
  23. package/dist/src/config/connection-gater.browser.js.map +1 -1
  24. package/dist/src/config/connection-gater.d.ts.map +1 -1
  25. package/dist/src/config/connection-gater.js +1 -12
  26. package/dist/src/config/connection-gater.js.map +1 -1
  27. package/dist/src/config.d.ts.map +1 -1
  28. package/dist/src/config.js +3 -22
  29. package/dist/src/config.js.map +1 -1
  30. package/dist/src/connection-manager/address-sorter.d.ts.map +1 -1
  31. package/dist/src/connection-manager/address-sorter.js +1 -2
  32. package/dist/src/connection-manager/address-sorter.js.map +1 -1
  33. package/dist/src/connection-manager/connection-pruner.d.ts.map +1 -1
  34. package/dist/src/connection-manager/connection-pruner.js +7 -3
  35. package/dist/src/connection-manager/connection-pruner.js.map +1 -1
  36. package/dist/src/connection-manager/constants.defaults.d.ts +4 -0
  37. package/dist/src/connection-manager/constants.defaults.d.ts.map +1 -1
  38. package/dist/src/connection-manager/constants.defaults.js +4 -0
  39. package/dist/src/connection-manager/constants.defaults.js.map +1 -1
  40. package/dist/src/connection-manager/dial-queue.d.ts +2 -2
  41. package/dist/src/connection-manager/dial-queue.d.ts.map +1 -1
  42. package/dist/src/connection-manager/dial-queue.js +11 -23
  43. package/dist/src/connection-manager/dial-queue.js.map +1 -1
  44. package/dist/src/connection-manager/index.d.ts +10 -18
  45. package/dist/src/connection-manager/index.d.ts.map +1 -1
  46. package/dist/src/connection-manager/index.js +44 -26
  47. package/dist/src/connection-manager/index.js.map +1 -1
  48. package/dist/src/connection-manager/reconnect-queue.js +1 -1
  49. package/dist/src/connection-manager/reconnect-queue.js.map +1 -1
  50. package/dist/src/connection-manager/utils.d.ts +31 -3
  51. package/dist/src/connection-manager/utils.d.ts.map +1 -1
  52. package/dist/src/connection-manager/utils.js +99 -18
  53. package/dist/src/connection-manager/utils.js.map +1 -1
  54. package/dist/src/connection-monitor.d.ts +1 -1
  55. package/dist/src/connection-monitor.d.ts.map +1 -1
  56. package/dist/src/connection-monitor.js +2 -3
  57. package/dist/src/connection-monitor.js.map +1 -1
  58. package/dist/src/connection.d.ts +14 -9
  59. package/dist/src/connection.d.ts.map +1 -1
  60. package/dist/src/connection.js +118 -139
  61. package/dist/src/connection.js.map +1 -1
  62. package/dist/src/get-peer.js +3 -3
  63. package/dist/src/get-peer.js.map +1 -1
  64. package/dist/src/index.d.ts +2 -2
  65. package/dist/src/index.js +2 -2
  66. package/dist/src/libp2p.d.ts +3 -1
  67. package/dist/src/libp2p.d.ts.map +1 -1
  68. package/dist/src/libp2p.js +13 -7
  69. package/dist/src/libp2p.js.map +1 -1
  70. package/dist/src/peer-routing.js +1 -1
  71. package/dist/src/peer-routing.js.map +1 -1
  72. package/dist/src/random-walk.d.ts.map +1 -1
  73. package/dist/src/random-walk.js +13 -3
  74. package/dist/src/random-walk.js.map +1 -1
  75. package/dist/src/registrar.d.ts +8 -4
  76. package/dist/src/registrar.d.ts.map +1 -1
  77. package/dist/src/registrar.js +66 -46
  78. package/dist/src/registrar.js.map +1 -1
  79. package/dist/src/transport-manager.js +15 -2
  80. package/dist/src/transport-manager.js.map +1 -1
  81. package/dist/src/upgrader.d.ts +26 -16
  82. package/dist/src/upgrader.d.ts.map +1 -1
  83. package/dist/src/upgrader.js +88 -122
  84. package/dist/src/upgrader.js.map +1 -1
  85. package/dist/src/utils.d.ts +3 -0
  86. package/dist/src/utils.d.ts.map +1 -0
  87. package/dist/src/utils.js +25 -0
  88. package/dist/src/utils.js.map +1 -0
  89. package/dist/src/version.d.ts +1 -1
  90. package/dist/src/version.d.ts.map +1 -1
  91. package/dist/src/version.js +1 -1
  92. package/dist/src/version.js.map +1 -1
  93. package/package.json +27 -30
  94. package/src/address-manager/dns-mappings.ts +50 -50
  95. package/src/address-manager/index.ts +37 -17
  96. package/src/address-manager/ip-mappings.ts +64 -44
  97. package/src/address-manager/observed-addresses.ts +1 -3
  98. package/src/address-manager/transport-addresses.ts +7 -9
  99. package/src/config/connection-gater.browser.ts +8 -24
  100. package/src/config/connection-gater.ts +1 -12
  101. package/src/config.ts +3 -25
  102. package/src/connection-manager/address-sorter.ts +1 -2
  103. package/src/connection-manager/connection-pruner.ts +8 -3
  104. package/src/connection-manager/constants.defaults.ts +5 -0
  105. package/src/connection-manager/dial-queue.ts +12 -27
  106. package/src/connection-manager/index.ts +60 -45
  107. package/src/connection-manager/reconnect-queue.ts +1 -1
  108. package/src/connection-manager/utils.ts +129 -21
  109. package/src/connection-monitor.ts +3 -4
  110. package/src/connection.ts +142 -179
  111. package/src/get-peer.ts +3 -3
  112. package/src/index.ts +2 -2
  113. package/src/libp2p.ts +16 -9
  114. package/src/peer-routing.ts +1 -1
  115. package/src/random-walk.ts +13 -3
  116. package/src/registrar.ts +87 -61
  117. package/src/transport-manager.ts +18 -2
  118. package/src/upgrader.ts +132 -149
  119. package/src/utils.ts +31 -0
  120. package/src/version.ts +1 -1
  121. package/dist/typedoc-urls.json +0 -24
@@ -4,16 +4,5 @@ import type { ConnectionGater } from '@libp2p/interface'
4
4
  * Returns a default connection gater implementation that allows everything
5
5
  */
6
6
  export function connectionGater (gater: ConnectionGater = {}): ConnectionGater {
7
- return {
8
- denyDialPeer: async () => false,
9
- denyDialMultiaddr: async () => false,
10
- denyInboundConnection: async () => false,
11
- denyOutboundConnection: async () => false,
12
- denyInboundEncryptedConnection: async () => false,
13
- denyOutboundEncryptedConnection: async () => false,
14
- denyInboundUpgradedConnection: async () => false,
15
- denyOutboundUpgradedConnection: async () => false,
16
- filterMultiaddrForPeer: async () => true,
17
- ...gater
18
- }
7
+ return gater
19
8
  }
package/src/config.ts CHANGED
@@ -1,33 +1,11 @@
1
- import { FaultTolerance, InvalidParametersError } from '@libp2p/interface'
2
- import { mergeOptions } from '@libp2p/utils/merge-options'
3
- import { dnsaddrResolver } from './connection-manager/resolvers/dnsaddr.ts'
1
+ import { InvalidParametersError } from '@libp2p/interface'
4
2
  import type { Libp2pInit } from './index.js'
5
3
  import type { ServiceMap } from '@libp2p/interface'
6
- import type { Multiaddr } from '@multiformats/multiaddr'
7
-
8
- const DefaultConfig: Libp2pInit = {
9
- addresses: {
10
- listen: [],
11
- announce: [],
12
- noAnnounce: [],
13
- announceFilter: (multiaddrs: Multiaddr[]) => multiaddrs
14
- },
15
- connectionManager: {
16
- resolvers: {
17
- dnsaddr: dnsaddrResolver
18
- }
19
- },
20
- transportManager: {
21
- faultTolerance: FaultTolerance.FATAL_ALL
22
- }
23
- }
24
4
 
25
5
  export async function validateConfig <T extends ServiceMap = Record<string, unknown>> (opts: Libp2pInit<T>): Promise<Libp2pInit<T>> {
26
- const resultingOptions: Libp2pInit<T> = mergeOptions(DefaultConfig, opts)
27
-
28
- if (resultingOptions.connectionProtector === null && globalThis.process?.env?.LIBP2P_FORCE_PNET != null) {
6
+ if (opts.connectionProtector === null && globalThis.process?.env?.LIBP2P_FORCE_PNET != null) {
29
7
  throw new InvalidParametersError('Private network is enforced, but no protector was provided')
30
8
  }
31
9
 
32
- return resultingOptions
10
+ return opts
33
11
  }
@@ -1,5 +1,4 @@
1
- import { isLoopback } from '@libp2p/utils/multiaddr/is-loopback'
2
- import { isPrivate } from '@libp2p/utils/multiaddr/is-private'
1
+ import { isLoopback, isPrivate } from '@libp2p/utils'
3
2
  import { Circuit, WebSockets, WebSocketsSecure, WebRTC, WebRTCDirect, WebTransport, TCP } from '@multiformats/multiaddr-matcher'
4
3
  import type { Address } from '@libp2p/interface'
5
4
 
@@ -1,6 +1,6 @@
1
1
  import { PeerMap } from '@libp2p/peer-collections'
2
- import { safelyCloseConnectionIfUnused } from '@libp2p/utils/close'
3
- import { multiaddrToIpNet } from './utils.js'
2
+ import { getNetConfig, isNetworkAddress } from '@libp2p/utils'
3
+ import { multiaddrToIpNet, safelyCloseConnectionIfUnused } from './utils.js'
4
4
  import type { IpNet } from '@chainsafe/netmask'
5
5
  import type { Libp2pEvents, Logger, ComponentLogger, PeerStore, Connection } from '@libp2p/interface'
6
6
  import type { ConnectionManager } from '@libp2p/interface-internal'
@@ -103,7 +103,12 @@ export class ConnectionPruner {
103
103
  this.log('too many connections open - closing a connection to %p', connection.remotePeer)
104
104
  // check allow list
105
105
  const connectionInAllowList = this.allow.some((ipNet) => {
106
- return ipNet.contains(connection.remoteAddr.nodeAddress().address)
106
+ if (isNetworkAddress(connection.remoteAddr)) {
107
+ const config = getNetConfig(connection.remoteAddr)
108
+ return ipNet.contains(config.host)
109
+ }
110
+
111
+ return true
107
112
  })
108
113
 
109
114
  // Connections in the allow list should be excluded from pruning
@@ -3,6 +3,11 @@
3
3
  */
4
4
  export const DIAL_TIMEOUT = 10_000
5
5
 
6
+ /**
7
+ * @see https://libp2p.github.io/js-libp2p/interfaces/index._internal_.ConnectionManagerConfig.html#connectionCloseTimeout
8
+ */
9
+ export const CONNECTION_CLOSE_TIMEOUT = 1_000
10
+
6
11
  /**
7
12
  * @see https://libp2p.github.io/js-libp2p/interfaces/index._internal_.ConnectionManagerConfig.html#inboundUpgradeTimeout
8
13
  */
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable max-depth */
2
2
  import { TimeoutError, DialError, AbortError } from '@libp2p/interface'
3
3
  import { PeerMap } from '@libp2p/peer-collections'
4
- import { PriorityQueue } from '@libp2p/utils/priority-queue'
5
- import { multiaddr } from '@multiformats/multiaddr'
4
+ import { PriorityQueue } from '@libp2p/utils'
5
+ import { CODE_P2P, multiaddr } from '@multiformats/multiaddr'
6
6
  import { Circuit } from '@multiformats/multiaddr-matcher'
7
7
  import { anySignal } from 'any-signal'
8
8
  import { setMaxListeners } from 'main-event'
@@ -20,10 +20,11 @@ import {
20
20
  LAST_DIAL_SUCCESS_KEY
21
21
  } from './constants.js'
22
22
  import { resolveMultiaddr, dnsaddrResolver } from './resolvers/index.js'
23
+ import { findExistingConnection } from './utils.ts'
23
24
  import { DEFAULT_DIAL_PRIORITY } from './index.js'
24
25
  import type { AddressSorter, ComponentLogger, Logger, Connection, ConnectionGater, Metrics, PeerId, Address, PeerStore, PeerRouting, IsDialableOptions, OpenConnectionProgressEvents, MultiaddrResolver } from '@libp2p/interface'
25
26
  import type { OpenConnectionOptions, TransportManager } from '@libp2p/interface-internal'
26
- import type { PriorityQueueJobOptions } from '@libp2p/utils/priority-queue'
27
+ import type { PriorityQueueJobOptions } from '@libp2p/utils'
27
28
  import type { DNS } from '@multiformats/dns'
28
29
  import type { Multiaddr } from '@multiformats/multiaddr'
29
30
  import type { ProgressOptions } from 'progress-events'
@@ -103,7 +104,7 @@ export class DialQueue {
103
104
  // a started job errored
104
105
  this.queue.addEventListener('failure', (event) => {
105
106
  if (event.detail?.error.name !== AbortError.name) {
106
- this.log.error('error in dial queue - %e', event.detail)
107
+ this.log.error('error in dial queue - %e', event.detail.error)
107
108
  }
108
109
  })
109
110
  }
@@ -136,30 +137,14 @@ export class DialQueue {
136
137
  async dial (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], options: OpenConnectionOptions = {}): Promise<Connection> {
137
138
  const { peerId, multiaddrs } = getPeerAddress(peerIdOrMultiaddr)
138
139
 
139
- // make sure we don't have an existing non-limited connection to any of the
140
- // addresses we are about to dial
141
- const existingConnection = Array.from(this.connections.values()).flat().find(conn => {
142
- if (options.force === true) {
143
- return false
144
- }
145
-
146
- if (conn.limits != null) {
147
- return false
148
- }
140
+ if (peerId != null && options.force !== true) {
141
+ const existingConnection = findExistingConnection(peerId, this.connections.get(peerId), multiaddrs)
149
142
 
150
- if (conn.remotePeer.equals(peerId)) {
151
- return true
143
+ if (existingConnection != null) {
144
+ this.log('already connected to %a', existingConnection.remoteAddr)
145
+ options.onProgress?.(new CustomProgressEvent('dial-queue:already-connected'))
146
+ return existingConnection
152
147
  }
153
-
154
- return multiaddrs.find(addr => {
155
- return addr.equals(conn.remoteAddr)
156
- })
157
- })
158
-
159
- if (existingConnection?.status === 'open') {
160
- this.log('already connected to %a', existingConnection.remoteAddr)
161
- options.onProgress?.(new CustomProgressEvent('dial-queue:already-connected'))
162
- return existingConnection
163
148
  }
164
149
 
165
150
  // ready to dial, all async work finished - make sure we don't have any
@@ -461,7 +446,7 @@ export class DialQueue {
461
446
  // if the resolved multiaddr has a PeerID but it's the wrong one, ignore it
462
447
  // - this can happen with addresses like bootstrap.libp2p.io that resolve
463
448
  // to multiple different peers
464
- const addrPeerId = addr.multiaddr.getPeerId()
449
+ const addrPeerId = addr.multiaddr.getComponents().findLast(c => c.code === CODE_P2P)?.value
465
450
  if (peerId != null && addrPeerId != null) {
466
451
  return peerId.equals(addrPeerId)
467
452
  }
@@ -1,7 +1,8 @@
1
1
  import { ConnectionClosedError, InvalidMultiaddrError, InvalidParametersError, InvalidPeerIdError, NotStartedError, start, stop } from '@libp2p/interface'
2
2
  import { PeerMap } from '@libp2p/peer-collections'
3
- import { RateLimiter } from '@libp2p/utils/rate-limiter'
3
+ import { getNetConfig, isNetworkAddress, RateLimiter } from '@libp2p/utils'
4
4
  import { multiaddr } from '@multiformats/multiaddr'
5
+ import { pEvent } from 'p-event'
5
6
  import { CustomProgressEvent } from 'progress-events'
6
7
  import { getPeerAddress } from '../get-peer.js'
7
8
  import { ConnectionPruner } from './connection-pruner.js'
@@ -9,11 +10,11 @@ import { DIAL_TIMEOUT, INBOUND_CONNECTION_THRESHOLD, MAX_CONNECTIONS, MAX_DIAL_Q
9
10
  import { DialQueue } from './dial-queue.js'
10
11
  import { ReconnectQueue } from './reconnect-queue.js'
11
12
  import { dnsaddrResolver } from './resolvers/index.ts'
12
- import { multiaddrToIpNet } from './utils.js'
13
+ import { findExistingConnection, multiaddrToIpNet } from './utils.js'
13
14
  import type { IpNet } from '@chainsafe/netmask'
14
- import type { PendingDial, AddressSorter, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, MultiaddrConnection, ConnectionGater, Metrics, PeerId, PeerStore, Startable, PendingDialStatus, PeerRouting, IsDialableOptions, MultiaddrResolver } from '@libp2p/interface'
15
+ import type { PendingDial, AddressSorter, Libp2pEvents, AbortOptions, ComponentLogger, Logger, Connection, MultiaddrConnection, ConnectionGater, Metrics, PeerId, PeerStore, Startable, PendingDialStatus, PeerRouting, IsDialableOptions, MultiaddrResolver, Stream, NewStreamOptions } from '@libp2p/interface'
15
16
  import type { ConnectionManager, OpenConnectionOptions, TransportManager } from '@libp2p/interface-internal'
16
- import type { JobStatus } from '@libp2p/utils/queue'
17
+ import type { JobStatus } from '@libp2p/utils'
17
18
  import type { Multiaddr } from '@multiformats/multiaddr'
18
19
  import type { TypedEventTarget } from 'main-event'
19
20
 
@@ -66,30 +67,20 @@ export interface ConnectionManagerInit {
66
67
  dialTimeout?: number
67
68
 
68
69
  /**
69
- * When a new incoming connection is opened, the upgrade process (e.g.
70
- * protect, encrypt, multiplex etc) must complete within this number of ms.
70
+ * How many ms to wait when closing a connection if an abort signal is not
71
+ * passed
71
72
  *
72
- * @default 10_000
73
+ * @default 1_000
73
74
  */
74
- inboundUpgradeTimeout?: number
75
+ connectionCloseTimeout?: number
75
76
 
76
77
  /**
77
- * When a new outbound connection is opened, the upgrade process (e.g.
78
+ * When a new incoming connection is opened, the upgrade process (e.g.
78
79
  * protect, encrypt, multiplex etc) must complete within this number of ms.
79
80
  *
80
- * Does not apply if an abort signal is passed to the `.dial` method.
81
- *
82
- * @deprecated This is handled by `dialTimeout`
83
- */
84
- outboundUpgradeTimeout?: number
85
-
86
- /**
87
- * Protocol negotiation must complete within this number of ms
88
- *
89
- * @default 2000
90
- * @deprecated use outboundStreamProtocolNegotiationTimeout or inboundStreamProtocolNegotiationTimeout instead
81
+ * @default 10_000
91
82
  */
92
- protocolNegotiationTimeout?: number
83
+ inboundUpgradeTimeout?: number
93
84
 
94
85
  /**
95
86
  * Outbound protocol negotiation must complete within this number of ms.
@@ -241,8 +232,8 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
241
232
  this.onDisconnect = this.onDisconnect.bind(this)
242
233
 
243
234
  // allow/deny lists
244
- this.allow = (init.allow ?? []).map(str => multiaddrToIpNet(str))
245
- this.deny = (init.deny ?? []).map(str => multiaddrToIpNet(str))
235
+ this.allow = (init.allow ?? []).map(str => multiaddrToIpNet(multiaddr(str)))
236
+ this.deny = (init.deny ?? []).map(str => multiaddrToIpNet(multiaddr(str)))
246
237
 
247
238
  this.incomingPendingConnections = 0
248
239
  this.maxIncomingPendingConnections = init.maxIncomingPendingConnections ?? defaultOptions.maxIncomingPendingConnections
@@ -399,16 +390,22 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
399
390
  )
400
391
 
401
392
  // Close all connections we're tracking
402
- const tasks: Array<Promise<void>> = []
393
+ const tasks: Array<Promise<any>> = []
403
394
  for (const connectionList of this.connections.values()) {
404
395
  for (const connection of connectionList) {
405
- tasks.push((async () => {
406
- try {
407
- await connection.close()
408
- } catch (err) {
409
- this.log.error(err)
410
- }
411
- })())
396
+ tasks.push(
397
+ Promise.all([
398
+ pEvent(connection, 'close', {
399
+ signal: AbortSignal.timeout(500)
400
+ }),
401
+ connection.close({
402
+ signal: AbortSignal.timeout(500)
403
+ })
404
+ ])
405
+ .catch(err => {
406
+ connection.abort(err)
407
+ })
408
+ )
412
409
  }
413
410
  }
414
411
 
@@ -504,7 +501,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
504
501
  this.connections.delete(peerId)
505
502
 
506
503
  // broadcast disconnect event
507
- this.events.safeDispatchEvent('peer:disconnect', { detail: connection.remotePeer })
504
+ this.events.safeDispatchEvent('peer:disconnect', { detail: peerId })
508
505
  }
509
506
  }
510
507
 
@@ -536,7 +533,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
536
533
  try {
537
534
  options.signal?.throwIfAborted()
538
535
 
539
- const { peerId } = getPeerAddress(peerIdOrMultiaddr)
536
+ const { peerId, multiaddrs } = getPeerAddress(peerIdOrMultiaddr)
540
537
 
541
538
  if (this.peerId.equals(peerId)) {
542
539
  throw new InvalidPeerIdError('Can not dial self')
@@ -544,11 +541,10 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
544
541
 
545
542
  if (peerId != null && options.force !== true) {
546
543
  this.log('dial %p', peerId)
547
- const existingConnection = this.getConnections(peerId)
548
- .find(conn => conn.limits == null)
544
+ const existingConnection = findExistingConnection(peerId, this.getConnections(peerId), multiaddrs)
549
545
 
550
546
  if (existingConnection != null) {
551
- this.log('had an existing non-limited connection to %p as %a', peerId, existingConnection.remoteAddr)
547
+ this.log('had an existing connection to %p as %a', peerId, existingConnection.remoteAddr)
552
548
 
553
549
  options.onProgress?.(new CustomProgressEvent('dial-queue:already-connected'))
554
550
  return existingConnection
@@ -600,13 +596,22 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
600
596
  }
601
597
  }
602
598
 
599
+ async openStream (peerIdOrMultiaddr: PeerId | Multiaddr | Multiaddr[], protocol: string | string[], options: OpenConnectionOptions & NewStreamOptions = {}): Promise<Stream> {
600
+ const connection = await this.openConnection(peerIdOrMultiaddr, options)
601
+
602
+ return connection.newStream(protocol, options)
603
+ }
604
+
603
605
  async closeConnections (peerId: PeerId, options: AbortOptions = {}): Promise<void> {
604
606
  const connections = this.connections.get(peerId) ?? []
605
607
 
606
608
  await Promise.all(
607
609
  connections.map(async connection => {
608
610
  try {
609
- await connection.close(options)
611
+ await Promise.all([
612
+ pEvent(connection, 'close', options),
613
+ connection.close(options)
614
+ ])
610
615
  } catch (err: any) {
611
616
  connection.abort(err)
612
617
  }
@@ -614,10 +619,15 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
614
619
  )
615
620
  }
616
621
 
617
- async acceptIncomingConnection (maConn: MultiaddrConnection): Promise<boolean> {
622
+ acceptIncomingConnection (maConn: MultiaddrConnection): boolean {
618
623
  // check deny list
619
- const denyConnection = this.deny.some(ma => {
620
- return ma.contains(maConn.remoteAddr.nodeAddress().address)
624
+ const denyConnection = this.deny.some(ipNet => {
625
+ if (isNetworkAddress(maConn.remoteAddr)) {
626
+ const config = getNetConfig(maConn.remoteAddr)
627
+ return ipNet.contains(config.host)
628
+ }
629
+
630
+ return false
621
631
  })
622
632
 
623
633
  if (denyConnection) {
@@ -627,7 +637,12 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
627
637
 
628
638
  // check allow list
629
639
  const allowConnection = this.allow.some(ipNet => {
630
- return ipNet.contains(maConn.remoteAddr.nodeAddress().address)
640
+ if (isNetworkAddress(maConn.remoteAddr)) {
641
+ const config = getNetConfig(maConn.remoteAddr)
642
+ return ipNet.contains(config.host)
643
+ }
644
+
645
+ return true
631
646
  })
632
647
 
633
648
  if (allowConnection) {
@@ -642,13 +657,13 @@ export class DefaultConnectionManager implements ConnectionManager, Startable {
642
657
  return false
643
658
  }
644
659
 
645
- if (maConn.remoteAddr.isThinWaistAddress()) {
646
- const host = maConn.remoteAddr.nodeAddress().address
660
+ if (isNetworkAddress(maConn.remoteAddr)) {
661
+ const config = getNetConfig(maConn.remoteAddr)
647
662
 
648
663
  try {
649
- await this.inboundConnectionRateLimiter.consume(host, 1)
664
+ this.inboundConnectionRateLimiter.consume(config.host, 1)
650
665
  } catch {
651
- this.log('connection from %a refused - inboundConnectionThreshold exceeded by host %s', maConn.remoteAddr, host)
666
+ this.log('connection from %a refused - inboundConnectionThreshold exceeded by host %s', maConn.remoteAddr, config.host)
652
667
  return false
653
668
  }
654
669
  }
@@ -1,5 +1,5 @@
1
1
  import { KEEP_ALIVE } from '@libp2p/interface'
2
- import { PeerQueue } from '@libp2p/utils/peer-queue'
2
+ import { PeerQueue } from '@libp2p/utils'
3
3
  import pRetry from 'p-retry'
4
4
  import { MAX_PARALLEL_RECONNECTS } from './constants.js'
5
5
  import type { ComponentLogger, Libp2pEvents, Logger, Metrics, Peer, PeerId, PeerStore, Startable } from '@libp2p/interface'
@@ -1,38 +1,146 @@
1
- import { multiaddr } from '@multiformats/multiaddr'
2
- import { convertToIpNet } from '@multiformats/multiaddr/convert'
3
- import type { IpNet } from '@chainsafe/netmask'
1
+ import { IpNet } from '@chainsafe/netmask'
2
+ import { InvalidParametersError } from '@libp2p/interface'
3
+ import { getNetConfig } from '@libp2p/utils'
4
+ import { Circuit } from '@multiformats/multiaddr-matcher'
5
+ import type { Connection, AbortOptions, PeerId } from '@libp2p/interface'
4
6
  import type { Multiaddr } from '@multiformats/multiaddr'
5
7
 
8
+ /**
9
+ * These are speculative protocols that are run automatically on connection open
10
+ * so are usually not the reason the connection was opened.
11
+ *
12
+ * Consequently when requested it should be safe to close connections that only
13
+ * have these protocol streams open.
14
+ */
15
+ const DEFAULT_CLOSABLE_PROTOCOLS = [
16
+ // identify
17
+ '/ipfs/id/1.0.0',
18
+
19
+ // identify-push
20
+ '/ipfs/id/push/1.0.0',
21
+
22
+ // autonat
23
+ '/libp2p/autonat/1.0.0',
24
+
25
+ // dcutr
26
+ '/libp2p/dcutr'
27
+ ]
28
+
29
+ export interface SafelyCloseConnectionOptions extends AbortOptions {
30
+ /**
31
+ * Only close the stream if it either has no protocol streams open or only
32
+ * ones in this list.
33
+ *
34
+ * @default ['/ipfs/id/1.0.0']
35
+ */
36
+ closableProtocols?: string[]
37
+ }
38
+
39
+ /**
40
+ * Close the passed connection if it has no streams, or only closable protocol
41
+ * streams, falling back to aborting the connection if closing it cleanly fails.
42
+ */
43
+ export async function safelyCloseConnectionIfUnused (connection?: Connection, options?: SafelyCloseConnectionOptions): Promise<void> {
44
+ const streamProtocols = connection?.streams?.map(stream => stream.protocol) ?? []
45
+ const closableProtocols = options?.closableProtocols ?? DEFAULT_CLOSABLE_PROTOCOLS
46
+
47
+ // if the connection has protocols not in the closable protocols list, do not
48
+ // close the connection
49
+ if (streamProtocols.filter(proto => proto != null && !closableProtocols.includes(proto)).length > 0) {
50
+ return
51
+ }
52
+
53
+ try {
54
+ await connection?.close(options)
55
+ } catch (err: any) {
56
+ connection?.abort(err)
57
+ }
58
+ }
59
+
6
60
  /**
7
61
  * Converts a multiaddr string or object to an IpNet object.
8
62
  * If the multiaddr doesn't include /ipcidr, it will encapsulate with the appropriate CIDR:
9
63
  * - /ipcidr/32 for IPv4
10
64
  * - /ipcidr/128 for IPv6
11
65
  *
12
- * @param {string | Multiaddr} ma - The multiaddr string or object to convert.
66
+ * @param {string | Multiaddr} ma - The multiaddr object to convert.
13
67
  * @returns {IpNet} The converted IpNet object.
14
68
  * @throws {Error} Throws an error if the multiaddr is not valid.
15
69
  */
16
- export function multiaddrToIpNet (ma: string | Multiaddr): IpNet {
17
- try {
18
- let parsedMa: Multiaddr
19
- if (typeof ma === 'string') {
20
- parsedMa = multiaddr(ma)
21
- } else {
22
- parsedMa = ma
23
- }
70
+ export function multiaddrToIpNet (ma: Multiaddr): IpNet {
71
+ const config = getNetConfig(ma)
72
+ let mask = config.cidr
24
73
 
25
- const protoNames = new Set([...parsedMa.getComponents().map(component => component.name)])
74
+ if (config.type !== 'ip4' && config.type !== 'ip6') {
75
+ throw new InvalidParametersError(`Multiaddr ${ma} was not an IPv4 or IPv6 address`)
76
+ }
26
77
 
27
- // Check if /ipcidr is already present
28
- if (!protoNames.has('ipcidr')) {
29
- const isIPv6 = protoNames.has('ip6')
30
- const cidr = isIPv6 ? '/ipcidr/128' : '/ipcidr/32'
31
- parsedMa = parsedMa.encapsulate(cidr)
78
+ // Check if /ipcidr is already present
79
+ if (mask == null) {
80
+ switch (config.type) {
81
+ case 'ip4': {
82
+ mask = 32
83
+ break
84
+ }
85
+ case 'ip6': {
86
+ mask = 128
87
+ break
88
+ }
89
+ default: {
90
+ throw new InvalidParametersError(`Multiaddr ${ma} was not an IPv4 or IPv6 address`)
91
+ }
32
92
  }
93
+ }
94
+
95
+ return new IpNet(config.host, mask)
96
+ }
97
+
98
+ /**
99
+ * Returns true if the passed multiaddr would result in a direct connection to
100
+ * the peer.
101
+ *
102
+ * Currently only circuit relay addresses are supported as indirect connections.
103
+ */
104
+ export function isDirect (ma: Multiaddr): boolean {
105
+ return !Circuit.exactMatch(ma)
106
+ }
107
+
108
+ /**
109
+ * If there is an existing non-limited connection to the remote peer return it,
110
+ * unless it is indirect and at least one of the passed dial addresses would
111
+ * result in a direct connection
112
+ */
113
+ export function findExistingConnection (peerId?: PeerId, connections?: Connection[], dialAddresses?: Multiaddr[]): Connection | undefined {
114
+ if (peerId == null || connections == null) {
115
+ return
116
+ }
117
+
118
+ const existingConnection = connections
119
+ .sort((a, b) => {
120
+ if (a.direct) {
121
+ return -1
122
+ }
123
+
124
+ if (b.direct) {
125
+ return 1
126
+ }
33
127
 
34
- return convertToIpNet(parsedMa)
35
- } catch (error) {
36
- throw new Error(`Can't convert to IpNet, Invalid multiaddr format: ${ma}`)
128
+ return 0
129
+ })
130
+ .find(con => con.limits == null)
131
+
132
+ if (existingConnection == null || existingConnection.direct || dialAddresses == null) {
133
+ return existingConnection
37
134
  }
135
+
136
+ // we have an indirect, but unlimited connection - test the dial addresses to
137
+ // see if any of them would result in a direct connection, in which case allow
138
+ // the attempt to upgrade to a direct connection
139
+ const wouldUpgradeToDirect = dialAddresses.some(ma => isDirect(ma))
140
+
141
+ if (wouldUpgradeToDirect) {
142
+ return
143
+ }
144
+
145
+ return existingConnection
38
146
  }
@@ -1,11 +1,10 @@
1
1
  import { randomBytes } from '@libp2p/crypto'
2
2
  import { serviceCapabilities } from '@libp2p/interface'
3
- import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout'
4
- import { byteStream } from 'it-byte-stream'
3
+ import { AdaptiveTimeout, byteStream } from '@libp2p/utils'
5
4
  import { setMaxListeners } from 'main-event'
6
5
  import type { ComponentLogger, Logger, Metrics, Startable } from '@libp2p/interface'
7
6
  import type { ConnectionManager } from '@libp2p/interface-internal'
8
- import type { AdaptiveTimeoutInit } from '@libp2p/utils/adaptive-timeout'
7
+ import type { AdaptiveTimeoutInit } from '@libp2p/utils'
9
8
 
10
9
  const DEFAULT_PING_INTERVAL_MS = 10000
11
10
  const PROTOCOL_VERSION = '1.0.0'
@@ -120,7 +119,7 @@ export class ConnectionMonitor implements Startable {
120
119
 
121
120
  conn.rtt = Date.now() - start
122
121
 
123
- await bs.unwrap().close({
122
+ await stream.close({
124
123
  signal
125
124
  })
126
125
  } catch (err: any) {