@libp2p/kad-dht 15.0.2-32627c876 → 15.0.2-3df5bf2da

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 (70) hide show
  1. package/dist/index.min.js +1 -1
  2. package/dist/index.min.js.map +4 -4
  3. package/dist/src/constants.d.ts +1 -1
  4. package/dist/src/constants.d.ts.map +1 -1
  5. package/dist/src/constants.js +1 -1
  6. package/dist/src/constants.js.map +1 -1
  7. package/dist/src/content-fetching/index.d.ts.map +1 -1
  8. package/dist/src/content-fetching/index.js +26 -6
  9. package/dist/src/content-fetching/index.js.map +1 -1
  10. package/dist/src/content-routing/index.d.ts.map +1 -1
  11. package/dist/src/content-routing/index.js +84 -38
  12. package/dist/src/content-routing/index.js.map +1 -1
  13. package/dist/src/index.d.ts +55 -10
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1 -0
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/kad-dht.d.ts +3 -1
  18. package/dist/src/kad-dht.d.ts.map +1 -1
  19. package/dist/src/kad-dht.js +12 -8
  20. package/dist/src/kad-dht.js.map +1 -1
  21. package/dist/src/network.d.ts +3 -5
  22. package/dist/src/network.d.ts.map +1 -1
  23. package/dist/src/network.js +10 -8
  24. package/dist/src/network.js.map +1 -1
  25. package/dist/src/peer-distance-list.d.ts +4 -3
  26. package/dist/src/peer-distance-list.d.ts.map +1 -1
  27. package/dist/src/peer-distance-list.js +9 -2
  28. package/dist/src/peer-distance-list.js.map +1 -1
  29. package/dist/src/peer-routing/index.d.ts +7 -5
  30. package/dist/src/peer-routing/index.d.ts.map +1 -1
  31. package/dist/src/peer-routing/index.js +57 -45
  32. package/dist/src/peer-routing/index.js.map +1 -1
  33. package/dist/src/providers.js +4 -4
  34. package/dist/src/providers.js.map +1 -1
  35. package/dist/src/query/events.d.ts +17 -6
  36. package/dist/src/query/events.d.ts.map +1 -1
  37. package/dist/src/query/events.js +18 -0
  38. package/dist/src/query/events.js.map +1 -1
  39. package/dist/src/query/manager.d.ts +0 -4
  40. package/dist/src/query/manager.d.ts.map +1 -1
  41. package/dist/src/query/manager.js +20 -9
  42. package/dist/src/query/manager.js.map +1 -1
  43. package/dist/src/query/query-path.d.ts +7 -11
  44. package/dist/src/query/query-path.d.ts.map +1 -1
  45. package/dist/src/query/query-path.js +67 -45
  46. package/dist/src/query/query-path.js.map +1 -1
  47. package/dist/src/query/types.d.ts +6 -5
  48. package/dist/src/query/types.d.ts.map +1 -1
  49. package/dist/src/rpc/handlers/get-providers.js +1 -1
  50. package/dist/src/rpc/handlers/get-providers.js.map +1 -1
  51. package/dist/src/utils.d.ts +1 -0
  52. package/dist/src/utils.d.ts.map +1 -1
  53. package/dist/src/utils.js +4 -0
  54. package/dist/src/utils.js.map +1 -1
  55. package/package.json +13 -12
  56. package/src/constants.ts +1 -1
  57. package/src/content-fetching/index.ts +26 -6
  58. package/src/content-routing/index.ts +91 -47
  59. package/src/index.ts +62 -10
  60. package/src/kad-dht.ts +12 -8
  61. package/src/network.ts +18 -12
  62. package/src/peer-distance-list.ts +13 -3
  63. package/src/peer-routing/index.ts +61 -48
  64. package/src/providers.ts +4 -4
  65. package/src/query/events.ts +41 -6
  66. package/src/query/manager.ts +23 -14
  67. package/src/query/query-path.ts +83 -65
  68. package/src/query/types.ts +7 -5
  69. package/src/rpc/handlers/get-providers.ts +1 -1
  70. package/src/utils.ts +9 -0
package/src/network.ts CHANGED
@@ -7,9 +7,10 @@ import { fromPbPeerInfo } from './message/utils.js'
7
7
  import {
8
8
  sendQueryEvent,
9
9
  peerResponseEvent,
10
- queryErrorEvent
10
+ queryErrorEvent,
11
+ dialPeerEvent
11
12
  } from './query/events.js'
12
- import type { KadDHTComponents, QueryEvent } from './index.js'
13
+ import type { DisjointPath, KadDHTComponents, QueryEvent } from './index.js'
13
14
  import type { AbortOptions, Logger, Stream, PeerId, PeerInfo, Startable, RoutingOptions, CounterGroup } from '@libp2p/interface'
14
15
 
15
16
  export interface NetworkInit {
@@ -29,7 +30,7 @@ export interface SendMessageOptions extends RoutingOptions {
29
30
  * this option is which index within `k` this message is for, and it
30
31
  * allows observers to collate events together on a per-path basis
31
32
  */
32
- path: number
33
+ path: DisjointPath
33
34
  }
34
35
 
35
36
  /**
@@ -161,9 +162,6 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
161
162
  throw new InvalidParametersError('Message type was missing')
162
163
  }
163
164
 
164
- this.log('sending %s to %p', msg.type, to)
165
- yield sendQueryEvent({ to, type, path: options.path }, options)
166
-
167
165
  let stream: Stream | undefined
168
166
  const signal = this.timeout.getTimeoutSignal(options)
169
167
 
@@ -175,8 +173,15 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
175
173
  try {
176
174
  this.metrics.operations?.increment({ [type]: true })
177
175
 
176
+ this.log('dialling %p', to)
177
+ yield dialPeerEvent({ peer: to, path: options.path }, options)
178
+
178
179
  const connection = await this.components.connectionManager.openConnection(to, options)
179
180
  stream = await connection.newStream(this.protocol, options)
181
+
182
+ this.log('sending %s to %p', msg.type, to)
183
+ yield sendQueryEvent({ to, type, path: options.path }, options)
184
+
180
185
  const response = await this._writeReadMessage(stream, msg, options)
181
186
 
182
187
  stream.close(options)
@@ -224,9 +229,6 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
224
229
  throw new InvalidParametersError('Message type was missing')
225
230
  }
226
231
 
227
- this.log('sending %s to %p', msg.type, to)
228
- yield sendQueryEvent({ to, type, path: options.path }, options)
229
-
230
232
  let stream: Stream | undefined
231
233
  const signal = this.timeout.getTimeoutSignal(options)
232
234
 
@@ -238,9 +240,15 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
238
240
  try {
239
241
  this.metrics.operations?.increment({ [type]: true })
240
242
 
243
+ this.log('dialling %p', to)
244
+ yield dialPeerEvent({ peer: to, path: options.path }, options)
245
+
241
246
  const connection = await this.components.connectionManager.openConnection(to, options)
242
247
  stream = await connection.newStream(this.protocol, options)
243
248
 
249
+ this.log('sending %s to %p', msg.type, to)
250
+ yield sendQueryEvent({ to, type, path: options.path }, options)
251
+
244
252
  await this._writeMessage(stream, msg, options)
245
253
 
246
254
  stream.close(options)
@@ -269,9 +277,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
269
277
  }
270
278
 
271
279
  /**
272
- * Write a message and read its response.
273
- * If no response is received after the specified timeout
274
- * this will error out.
280
+ * Write a message and read a response
275
281
  */
276
282
  async _writeReadMessage (stream: Stream, msg: Partial<Message>, options: AbortOptions): Promise<Message> {
277
283
  const pb = pbStream(stream)
@@ -1,12 +1,13 @@
1
1
  import { xor as uint8ArrayXor } from 'uint8arrays/xor'
2
2
  import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare'
3
3
  import { convertPeerId } from './utils.js'
4
+ import type { DisjointPath } from './index.js'
4
5
  import type { PeerId, PeerInfo } from '@libp2p/interface'
5
6
 
6
7
  interface PeerDistance {
7
8
  peer: PeerInfo
8
9
  distance: Uint8Array
9
- path: number
10
+ path: DisjointPath
10
11
  }
11
12
 
12
13
  /**
@@ -48,7 +49,7 @@ export class PeerDistanceList {
48
49
  /**
49
50
  * Add a peerId to the list.
50
51
  */
51
- async add (peer: PeerInfo, path: number = -1): Promise<void> {
52
+ async add (peer: PeerInfo, path: DisjointPath = { index: -1, queued: 0, running: 0, total: 0 }): Promise<void> {
52
53
  const dhtKey = await convertPeerId(peer.id)
53
54
 
54
55
  this.addWithKadId(peer, dhtKey, path)
@@ -57,7 +58,7 @@ export class PeerDistanceList {
57
58
  /**
58
59
  * Add a peerId to the list.
59
60
  */
60
- addWithKadId (peer: PeerInfo, kadId: Uint8Array, path: number = -1): void {
61
+ addWithKadId (peer: PeerInfo, kadId: Uint8Array, path: DisjointPath = { index: -1, queued: 0, running: 0, total: 0 }): void {
61
62
  if (this.peerDistances.find(pd => pd.peer.id.equals(peer.id)) != null) {
62
63
  return
63
64
  }
@@ -68,6 +69,15 @@ export class PeerDistanceList {
68
69
  path
69
70
  }
70
71
 
72
+ if (this.peerDistances.length === this.capacity) {
73
+ const lastPeer = this.peerDistances[this.peerDistances.length - 1]
74
+
75
+ // only add if it's closer than our furthest peer
76
+ if (lastPeer != null && uint8ArrayXorCompare(el.distance, lastPeer.distance) !== -1) {
77
+ return
78
+ }
79
+ }
80
+
71
81
  let added = false
72
82
 
73
83
  for (let j = 0; j < this.peerDistances.length; j++) {
@@ -3,7 +3,6 @@ import { InvalidPublicKeyError, NotFoundError } from '@libp2p/interface'
3
3
  import { peerIdFromPublicKey, peerIdFromMultihash } from '@libp2p/peer-id'
4
4
  import { Libp2pRecord } from '@libp2p/record'
5
5
  import * as Digest from 'multiformats/hashes/digest'
6
- import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
7
6
  import { xor as uint8ArrayXor } from 'uint8arrays/xor'
8
7
  import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare'
9
8
  import { QueryError, InvalidRecordError } from '../errors.js'
@@ -23,12 +22,14 @@ import type { QueryManager, QueryOptions } from '../query/manager.js'
23
22
  import type { QueryFunc } from '../query/types.js'
24
23
  import type { RoutingTable } from '../routing-table/index.js'
25
24
  import type { ComponentLogger, Logger, Metrics, PeerId, PeerInfo, PeerStore, RoutingOptions } from '@libp2p/interface'
25
+ import type { ConnectionManager } from '@libp2p/interface-internal'
26
26
 
27
27
  export interface PeerRoutingComponents {
28
28
  peerId: PeerId
29
29
  peerStore: PeerStore
30
30
  logger: ComponentLogger
31
31
  metrics?: Metrics
32
+ connectionManager: ConnectionManager
32
33
  }
33
34
 
34
35
  export interface PeerRoutingInit {
@@ -45,16 +46,14 @@ export class PeerRouting {
45
46
  private readonly network: Network
46
47
  private readonly validators: Validators
47
48
  private readonly queryManager: QueryManager
48
- private readonly peerStore: PeerStore
49
- private readonly peerId: PeerId
49
+ private readonly components: PeerRoutingComponents
50
50
 
51
51
  constructor (components: PeerRoutingComponents, init: PeerRoutingInit) {
52
52
  this.routingTable = init.routingTable
53
53
  this.network = init.network
54
54
  this.validators = init.validators
55
55
  this.queryManager = init.queryManager
56
- this.peerStore = components.peerStore
57
- this.peerId = components.peerId
56
+ this.components = components
58
57
  this.log = components.logger.forComponent(`${init.logPrefix}:peer-routing`)
59
58
 
60
59
  this.findPeer = components.metrics?.traceFunction('libp2p.kadDHT.findPeer', this.findPeer.bind(this), {
@@ -77,7 +76,7 @@ export class PeerRouting {
77
76
  this.log('findPeerLocal found %p in routing table', peer)
78
77
 
79
78
  try {
80
- peerData = await this.peerStore.get(p)
79
+ peerData = await this.components.peerStore.get(p)
81
80
  } catch (err: any) {
82
81
  if (err.name !== 'NotFoundError') {
83
82
  throw err
@@ -87,7 +86,7 @@ export class PeerRouting {
87
86
 
88
87
  if (peerData == null) {
89
88
  try {
90
- peerData = await this.peerStore.get(peer)
89
+ peerData = await this.components.peerStore.get(peer)
91
90
  } catch (err: any) {
92
91
  if (err.name !== 'NotFoundError') {
93
92
  throw err
@@ -124,10 +123,16 @@ export class PeerRouting {
124
123
  */
125
124
  async * getPublicKeyFromNode (peer: PeerId, options: RoutingOptions = {}): AsyncGenerator<QueryEvent> {
126
125
  const pkKey = keyForPublicKey(peer)
126
+ const path = {
127
+ index: -1,
128
+ queued: 0,
129
+ running: 0,
130
+ total: 0
131
+ }
127
132
 
128
133
  for await (const event of this._getValueSingle(peer, pkKey, {
129
134
  ...options,
130
- path: -1
135
+ path
131
136
  })) {
132
137
  yield event
133
138
 
@@ -146,7 +151,8 @@ export class PeerRouting {
146
151
 
147
152
  yield valueEvent({
148
153
  from: peer,
149
- value: event.record.value
154
+ value: event.record.value,
155
+ path
150
156
  }, options)
151
157
  }
152
158
  }
@@ -168,9 +174,14 @@ export class PeerRouting {
168
174
  if (pi != null) {
169
175
  this.log('found local')
170
176
  yield finalPeerEvent({
171
- from: this.peerId,
177
+ from: this.components.peerId,
172
178
  peer: pi,
173
- path: -1
179
+ path: {
180
+ index: -1,
181
+ queued: 0,
182
+ running: 0,
183
+ total: 0
184
+ }
174
185
  }, options)
175
186
  return
176
187
  }
@@ -187,7 +198,7 @@ export class PeerRouting {
187
198
  key: id.toMultihash().bytes
188
199
  }
189
200
 
190
- for await (const event of self.network.sendRequest(peer, request, {
201
+ for await (const event of self.network.sendRequest(peer.id, request, {
191
202
  ...options,
192
203
  signal,
193
204
  path
@@ -219,58 +230,64 @@ export class PeerRouting {
219
230
  }
220
231
 
221
232
  if (!foundPeer) {
222
- yield queryErrorEvent({
223
- from: this.peerId,
224
- error: new NotFoundError('Not found')
225
- }, options)
233
+ throw new NotFoundError('Not found')
226
234
  }
227
235
  }
228
236
 
229
237
  /**
230
- * Kademlia 'FIND_NODE' operation on a key, which could be the bytes from
231
- * a multihash or a peer ID
238
+ * Kademlia 'FIND_NODE' operation on a key, which could be the bytes from a
239
+ * multihash or a peer ID
232
240
  */
233
241
  async * getClosestPeers (key: Uint8Array, options: QueryOptions = {}): AsyncGenerator<QueryEvent> {
234
242
  this.log('getClosestPeers to %b', key)
235
243
  const kadId = await convertBuffer(key)
236
- const tablePeers = this.routingTable.closestPeers(kadId)
237
- const self = this // eslint-disable-line @typescript-eslint/no-this-alias
238
-
239
244
  const peers = new PeerDistanceList(kadId, this.routingTable.kBucketSize)
240
- await Promise.all(tablePeers.map(async peer => { await peers.add({ id: peer, multiaddrs: [] }, -1) }))
245
+ const self = this // eslint-disable-line @typescript-eslint/no-this-alias
241
246
 
242
- const getCloserPeersQuery: QueryFunc = async function * ({ peer, signal, path }) {
243
- self.log('closerPeersSingle %s from %p', uint8ArrayToString(key, 'base32'), peer)
247
+ const getCloserPeersQuery: QueryFunc = async function * ({ peer, path, peerKadId, signal }) {
248
+ self.log('getClosestPeers asking %p', peer)
244
249
  const request: Partial<Message> = {
245
250
  type: MessageType.FIND_NODE,
246
251
  key
247
252
  }
248
253
 
249
- yield * self.network.sendRequest(peer, request, {
254
+ yield * self.network.sendRequest(peer.id, request, {
250
255
  ...options,
251
256
  signal,
252
257
  path
253
258
  })
254
- }
255
-
256
- for await (const event of this.queryManager.run(key, getCloserPeersQuery, options)) {
257
- if (event.name === 'PEER_RESPONSE') {
258
- await Promise.all(event.closer.map(async peerData => {
259
- await peers.add(peerData, event.path)
260
- }))
261
- }
262
259
 
263
- yield event
260
+ // add the peer to the list if we've managed to contact it successfully
261
+ peers.addWithKadId(peer, peerKadId, path)
264
262
  }
265
263
 
264
+ yield * this.queryManager.run(key, getCloserPeersQuery, options)
265
+
266
266
  this.log('found %d peers close to %b', peers.length, key)
267
267
 
268
- for (const { peer, path } of peers.peers) {
269
- yield finalPeerEvent({
270
- from: this.peerId,
271
- peer,
272
- path
273
- }, options)
268
+ for (let { peer, path } of peers.peers) {
269
+ try {
270
+ if (peer.multiaddrs.length === 0) {
271
+ peer = await self.components.peerStore.getInfo(peer.id)
272
+ }
273
+
274
+ if (peer.multiaddrs.length === 0) {
275
+ continue
276
+ }
277
+
278
+ yield finalPeerEvent({
279
+ from: this.components.peerId,
280
+ peer: await self.components.peerStore.getInfo(peer.id),
281
+ path: {
282
+ index: path.index,
283
+ queued: 0,
284
+ running: 0,
285
+ total: 0
286
+ }
287
+ }, options)
288
+ } catch {
289
+ continue
290
+ }
274
291
  }
275
292
  }
276
293
 
@@ -318,7 +335,8 @@ export class PeerRouting {
318
335
  }
319
336
 
320
337
  /**
321
- * Get the nearest peers to the given query, but if closer than self
338
+ * Get the peers in our routing table that are closer than the passed PeerId
339
+ * to the passed key
322
340
  */
323
341
  async getCloserPeersOffline (key: Uint8Array, closerThan: PeerId): Promise<PeerInfo[]> {
324
342
  const output: PeerInfo[] = []
@@ -328,7 +346,7 @@ export class PeerRouting {
328
346
  const multihash = Digest.decode(key)
329
347
  const targetPeerId = peerIdFromMultihash(multihash)
330
348
 
331
- const peer = await this.peerStore.get(targetPeerId)
349
+ const peer = await this.components.peerStore.get(targetPeerId)
332
350
 
333
351
  output.push({
334
352
  id: peer.id,
@@ -351,12 +369,7 @@ export class PeerRouting {
351
369
  }
352
370
 
353
371
  try {
354
- const peer = await this.peerStore.get(peerId)
355
-
356
- output.push({
357
- id: peerId,
358
- multiaddrs: peer.addresses.map(({ multiaddr }) => multiaddr)
359
- })
372
+ output.push(await this.components.peerStore.getInfo(peerId))
360
373
  } catch (err: any) {
361
374
  if (err.name !== 'NotFoundError') {
362
375
  throw err
package/src/providers.ts CHANGED
@@ -41,7 +41,7 @@ export class Providers {
41
41
  const release = await this.lock.readLock()
42
42
 
43
43
  try {
44
- this.log('%p provides %s', provider, cid)
44
+ this.log.trace('%p provides %s', provider, cid)
45
45
  await this.writeProviderEntry(cid, provider)
46
46
  } finally {
47
47
  release()
@@ -56,7 +56,7 @@ export class Providers {
56
56
 
57
57
  try {
58
58
  const key = toProviderKey(this.datastorePrefix, cid, provider)
59
- this.log('%p no longer provides %s', provider, cid)
59
+ this.log.trace('%p no longer provides %s', provider, cid)
60
60
  await this.datastore.delete(key)
61
61
  } finally {
62
62
  release()
@@ -70,9 +70,9 @@ export class Providers {
70
70
  const release = await this.lock.readLock()
71
71
 
72
72
  try {
73
- this.log('get providers for %c', cid)
73
+ this.log.trace('get providers for %c', cid)
74
74
  const provs = await this.loadProviders(cid, options)
75
- this.log('got %d providers for %c', provs.size, cid)
75
+ this.log.trace('got %d providers for %c', provs.size, cid)
76
76
 
77
77
  return [...provs.keys()]
78
78
  } finally {
@@ -1,4 +1,4 @@
1
- import type { MessageType, SendQueryEvent, PeerResponseEvent, AddPeerEvent, ValueEvent, ProviderEvent, QueryErrorEvent, FinalPeerEvent } from '../index.js'
1
+ import type { MessageType, SendQueryEvent, PeerResponseEvent, AddPeerEvent, ValueEvent, ProviderEvent, QueryErrorEvent, FinalPeerEvent, DisjointPath, PathEndedEvent, DialPeerEvent } from '../index.js'
2
2
  import type { PeerId, PeerInfo } from '@libp2p/interface'
3
3
  import type { Libp2pRecord } from '@libp2p/record'
4
4
  import type { ProgressOptions } from 'progress-events'
@@ -6,7 +6,7 @@ import type { ProgressOptions } from 'progress-events'
6
6
  export interface QueryEventFields {
7
7
  to: PeerId
8
8
  type: MessageType
9
- path: number
9
+ path: DisjointPath
10
10
  }
11
11
 
12
12
  export function sendQueryEvent (fields: QueryEventFields, options: ProgressOptions = {}): SendQueryEvent {
@@ -26,7 +26,7 @@ export function sendQueryEvent (fields: QueryEventFields, options: ProgressOptio
26
26
  export interface PeerResponseEventFields {
27
27
  from: PeerId
28
28
  messageType: MessageType
29
- path: number
29
+ path: DisjointPath
30
30
  closer?: PeerInfo[]
31
31
  providers?: PeerInfo[]
32
32
  record?: Libp2pRecord
@@ -50,7 +50,7 @@ export function peerResponseEvent (fields: PeerResponseEventFields, options: Pro
50
50
  export interface FinalPeerEventFields {
51
51
  from: PeerId
52
52
  peer: PeerInfo
53
- path: number
53
+ path: DisjointPath
54
54
  }
55
55
 
56
56
  export function finalPeerEvent (fields: FinalPeerEventFields, options: ProgressOptions = {}): FinalPeerEvent {
@@ -68,7 +68,7 @@ export function finalPeerEvent (fields: FinalPeerEventFields, options: ProgressO
68
68
  export interface ErrorEventFields {
69
69
  from: PeerId
70
70
  error: Error
71
- path?: number
71
+ path: DisjointPath
72
72
  }
73
73
 
74
74
  export function queryErrorEvent (fields: ErrorEventFields, options: ProgressOptions = {}): QueryErrorEvent {
@@ -86,6 +86,7 @@ export function queryErrorEvent (fields: ErrorEventFields, options: ProgressOpti
86
86
  export interface ProviderEventFields {
87
87
  from: PeerId
88
88
  providers: PeerInfo[]
89
+ path: DisjointPath
89
90
  }
90
91
 
91
92
  export function providerEvent (fields: ProviderEventFields, options: ProgressOptions = {}): ProviderEvent {
@@ -103,6 +104,7 @@ export function providerEvent (fields: ProviderEventFields, options: ProgressOpt
103
104
  export interface ValueEventFields {
104
105
  from: PeerId
105
106
  value: Uint8Array
107
+ path: DisjointPath
106
108
  }
107
109
 
108
110
  export function valueEvent (fields: ValueEventFields, options: ProgressOptions = {}): ValueEvent {
@@ -119,7 +121,7 @@ export function valueEvent (fields: ValueEventFields, options: ProgressOptions =
119
121
 
120
122
  export interface AddPeerEventFields {
121
123
  peer: PeerId
122
- path: number
124
+ path: DisjointPath
123
125
  }
124
126
 
125
127
  export function addPeerEvent (fields: AddPeerEventFields, options: ProgressOptions = {}): AddPeerEvent {
@@ -133,3 +135,36 @@ export function addPeerEvent (fields: AddPeerEventFields, options: ProgressOptio
133
135
 
134
136
  return event
135
137
  }
138
+
139
+ export interface DialPeerEventFields {
140
+ peer: PeerId
141
+ path: DisjointPath
142
+ }
143
+
144
+ export function dialPeerEvent (fields: DialPeerEventFields, options: ProgressOptions = {}): DialPeerEvent {
145
+ const event: DialPeerEvent = {
146
+ ...fields,
147
+ name: 'DIAL_PEER',
148
+ type: 7
149
+ }
150
+
151
+ options.onProgress?.(new CustomEvent('kad-dht:query:dial-peer', { detail: event }))
152
+
153
+ return event
154
+ }
155
+
156
+ export interface PathEndedEventFields {
157
+ path: DisjointPath
158
+ }
159
+
160
+ export function pathEndedEvent (fields: PathEndedEventFields, options: ProgressOptions = {}): PathEndedEvent {
161
+ const event: PathEndedEvent = {
162
+ ...fields,
163
+ name: 'PATH_ENDED',
164
+ type: 8
165
+ }
166
+
167
+ options.onProgress?.(new CustomEvent('kad-dht:query:path-ended', { detail: event }))
168
+
169
+ return event
170
+ }
@@ -1,5 +1,6 @@
1
+ /* eslint-disable complexity */
1
2
  import { setMaxListeners } from '@libp2p/interface'
2
- import { PeerSet } from '@libp2p/peer-collections'
3
+ import { createScalableCuckooFilter } from '@libp2p/utils/filters'
3
4
  import { anySignal } from 'any-signal'
4
5
  import merge from 'it-merge'
5
6
  import { pEvent } from 'p-event'
@@ -39,11 +40,6 @@ export interface QueryManagerComponents {
39
40
  }
40
41
 
41
42
  export interface QueryOptions extends RoutingOptions {
42
- /**
43
- * A timeout for subqueries executed as part of the main query
44
- */
45
- queryFuncTimeout?: number
46
-
47
43
  isSelfQuery?: boolean
48
44
  }
49
45
 
@@ -161,7 +157,7 @@ export class QueryManager implements Startable {
161
157
  }
162
158
 
163
159
  if (options.isSelfQuery !== true && this.initialQuerySelfHasRun != null) {
164
- log('waiting for initial query-self query before continuing')
160
+ log('waiting for initial self query before continuing')
165
161
 
166
162
  await raceSignal(this.initialQuerySelfHasRun.promise, signal)
167
163
 
@@ -171,8 +167,22 @@ export class QueryManager implements Startable {
171
167
  log('query:start')
172
168
 
173
169
  const id = await convertBuffer(key)
174
- const peers = this.routingTable.closestPeers(id)
175
- const peersToQuery = peers.slice(0, Math.min(this.disjointPaths, peers.length))
170
+ const peers = this.routingTable.closestPeers(id, this.routingTable.kBucketSize)
171
+
172
+ // split peers into d buckets evenly(ish)
173
+ const peersToQuery = peers.sort(() => {
174
+ if (Math.random() > 0.5) {
175
+ return 1
176
+ }
177
+
178
+ return -1
179
+ })
180
+ .reduce((acc: PeerId[][], curr, index) => {
181
+ acc[index % this.disjointPaths].push(curr)
182
+
183
+ return acc
184
+ }, new Array(this.disjointPaths).fill(0).map(() => []))
185
+ .filter(peers => peers.length > 0)
176
186
 
177
187
  if (peers.length === 0) {
178
188
  log.error('running query with no peers')
@@ -180,21 +190,20 @@ export class QueryManager implements Startable {
180
190
  }
181
191
 
182
192
  // make sure we don't get trapped in a loop
183
- const peersSeen = new PeerSet()
193
+ const peersSeen = createScalableCuckooFilter(1024)
184
194
 
185
195
  // Create query paths from the starting peers
186
196
  const paths = peersToQuery.map((peer, index) => {
187
197
  return queryPath({
188
198
  ...options,
189
199
  key,
190
- startingPeer: peer,
200
+ startingPeers: peer,
191
201
  ourPeerId: this.peerId,
192
202
  signal,
193
203
  query: queryFunc,
194
204
  path: index,
195
205
  numPaths: peersToQuery.length,
196
206
  alpha: this.alpha,
197
- queryFuncTimeout: options.queryFuncTimeout,
198
207
  log,
199
208
  peersSeen,
200
209
  onProgress: options.onProgress,
@@ -223,8 +232,8 @@ export class QueryManager implements Startable {
223
232
  }
224
233
  }
225
234
 
226
- yield event
227
235
  signal.throwIfAborted()
236
+ yield event
228
237
  }
229
238
 
230
239
  queryFinished = true
@@ -241,7 +250,7 @@ export class QueryManager implements Startable {
241
250
 
242
251
  signal.clear()
243
252
 
244
- log('query:done')
253
+ log('query finished')
245
254
  }
246
255
  }
247
256
  }