libp2p 1.1.0 → 1.1.1
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 +22 -22
- package/dist/src/connection-manager/auto-dial.d.ts +2 -1
- package/dist/src/connection-manager/auto-dial.d.ts.map +1 -1
- package/dist/src/connection-manager/auto-dial.js +8 -6
- package/dist/src/connection-manager/auto-dial.js.map +1 -1
- package/dist/src/connection-manager/dial-queue.d.ts +11 -16
- package/dist/src/connection-manager/dial-queue.d.ts.map +1 -1
- package/dist/src/connection-manager/dial-queue.js +142 -257
- package/dist/src/connection-manager/dial-queue.js.map +1 -1
- package/dist/src/connection-manager/index.d.ts +5 -3
- package/dist/src/connection-manager/index.d.ts.map +1 -1
- package/dist/src/connection-manager/index.js +15 -9
- package/dist/src/connection-manager/index.js.map +1 -1
- package/dist/src/connection-manager/utils.d.ts +0 -2
- package/dist/src/connection-manager/utils.d.ts.map +1 -1
- package/dist/src/connection-manager/utils.js +0 -15
- package/dist/src/connection-manager/utils.js.map +1 -1
- package/dist/src/content-routing.d.ts +0 -1
- package/dist/src/content-routing.d.ts.map +1 -1
- package/dist/src/content-routing.js +6 -31
- package/dist/src/content-routing.js.map +1 -1
- package/dist/src/peer-routing.d.ts +1 -1
- package/dist/src/peer-routing.d.ts.map +1 -1
- package/dist/src/peer-routing.js +12 -14
- package/dist/src/peer-routing.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +30 -31
- package/src/connection-manager/auto-dial.ts +11 -8
- package/src/connection-manager/dial-queue.ts +161 -290
- package/src/connection-manager/index.ts +22 -12
- package/src/connection-manager/utils.ts +0 -20
- package/src/content-routing.ts +8 -37
- package/src/peer-routing.ts +12 -15
- package/src/version.ts +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
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 {
|
|
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 {
|
|
19
|
-
import type { AddressSorter, AbortOptions,
|
|
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
|
|
33
|
-
|
|
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
|
|
66
|
-
|
|
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.
|
|
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
|
|
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.
|
|
125
|
-
this.log.error('error in dial queue',
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
return
|
|
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.
|
|
209
|
-
|
|
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
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
249
|
-
|
|
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.
|
|
255
|
-
|
|
256
|
-
|
|
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('
|
|
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
|
-
|
|
265
|
-
|
|
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
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
85
|
-
*
|
|
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
|
-
|
|
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
|
}
|