@libp2p/kad-dht 15.0.2 → 15.1.0

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 (85) hide show
  1. package/dist/index.min.js +2 -15
  2. package/dist/index.min.js.map +7 -0
  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 +4 -4
  8. package/dist/src/content-fetching/index.d.ts.map +1 -1
  9. package/dist/src/content-fetching/index.js +39 -12
  10. package/dist/src/content-fetching/index.js.map +1 -1
  11. package/dist/src/content-routing/index.d.ts.map +1 -1
  12. package/dist/src/content-routing/index.js +87 -37
  13. package/dist/src/content-routing/index.js.map +1 -1
  14. package/dist/src/index.d.ts +57 -5
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +1 -0
  17. package/dist/src/index.js.map +1 -1
  18. package/dist/src/kad-dht.d.ts +3 -1
  19. package/dist/src/kad-dht.d.ts.map +1 -1
  20. package/dist/src/kad-dht.js +14 -10
  21. package/dist/src/kad-dht.js.map +1 -1
  22. package/dist/src/network.d.ts +12 -6
  23. package/dist/src/network.d.ts.map +1 -1
  24. package/dist/src/network.js +17 -16
  25. package/dist/src/network.js.map +1 -1
  26. package/dist/src/peer-distance-list.d.ts +10 -3
  27. package/dist/src/peer-distance-list.d.ts.map +1 -1
  28. package/dist/src/peer-distance-list.js +13 -5
  29. package/dist/src/peer-distance-list.js.map +1 -1
  30. package/dist/src/peer-routing/index.d.ts +10 -8
  31. package/dist/src/peer-routing/index.d.ts.map +1 -1
  32. package/dist/src/peer-routing/index.js +78 -48
  33. package/dist/src/peer-routing/index.js.map +1 -1
  34. package/dist/src/providers.d.ts +4 -4
  35. package/dist/src/providers.d.ts.map +1 -1
  36. package/dist/src/providers.js +12 -12
  37. package/dist/src/providers.js.map +1 -1
  38. package/dist/src/query/events.d.ts +15 -3
  39. package/dist/src/query/events.d.ts.map +1 -1
  40. package/dist/src/query/events.js +9 -0
  41. package/dist/src/query/events.js.map +1 -1
  42. package/dist/src/query/manager.d.ts +2 -4
  43. package/dist/src/query/manager.d.ts.map +1 -1
  44. package/dist/src/query/manager.js +42 -16
  45. package/dist/src/query/manager.js.map +1 -1
  46. package/dist/src/query/query-path.d.ts +10 -14
  47. package/dist/src/query/query-path.d.ts.map +1 -1
  48. package/dist/src/query/query-path.js +67 -53
  49. package/dist/src/query/query-path.js.map +1 -1
  50. package/dist/src/query/types.d.ts +6 -5
  51. package/dist/src/query/types.d.ts.map +1 -1
  52. package/dist/src/query-self.d.ts +2 -3
  53. package/dist/src/query-self.d.ts.map +1 -1
  54. package/dist/src/query-self.js +11 -14
  55. package/dist/src/query-self.js.map +1 -1
  56. package/dist/src/routing-table/closest-peers.js +1 -1
  57. package/dist/src/routing-table/closest-peers.js.map +1 -1
  58. package/dist/src/routing-table/k-bucket.js +1 -1
  59. package/dist/src/routing-table/k-bucket.js.map +1 -1
  60. package/dist/src/rpc/handlers/get-providers.js +1 -1
  61. package/dist/src/rpc/handlers/get-providers.js.map +1 -1
  62. package/dist/src/utils.d.ts +1 -0
  63. package/dist/src/utils.d.ts.map +1 -1
  64. package/dist/src/utils.js +4 -0
  65. package/dist/src/utils.js.map +1 -1
  66. package/dist/typedoc-urls.json +4 -0
  67. package/package.json +12 -11
  68. package/src/constants.ts +1 -1
  69. package/src/content-fetching/index.ts +40 -13
  70. package/src/content-routing/index.ts +92 -44
  71. package/src/index.ts +64 -5
  72. package/src/kad-dht.ts +14 -10
  73. package/src/network.ts +33 -20
  74. package/src/peer-distance-list.ts +19 -7
  75. package/src/peer-routing/index.ts +83 -52
  76. package/src/providers.ts +13 -13
  77. package/src/query/events.ts +27 -3
  78. package/src/query/manager.ts +48 -21
  79. package/src/query/query-path.ts +86 -76
  80. package/src/query/types.ts +7 -5
  81. package/src/query-self.ts +15 -17
  82. package/src/routing-table/closest-peers.ts +1 -1
  83. package/src/routing-table/k-bucket.ts +1 -1
  84. package/src/rpc/handlers/get-providers.ts +1 -1
  85. package/src/utils.ts +9 -0
@@ -1,16 +1,15 @@
1
- import { setMaxListeners } from '@libp2p/interface'
2
1
  import { Queue } from '@libp2p/utils/queue'
3
- import { anySignal } from 'any-signal'
2
+ import { pushable } from 'it-pushable'
4
3
  import { xor as uint8ArrayXor } from 'uint8arrays/xor'
5
4
  import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare'
6
5
  import { QueryAbortedError } from '../errors.js'
7
6
  import { convertPeerId, convertBuffer } from '../utils.js'
8
- import { queryErrorEvent } from './events.js'
7
+ import { pathEndedEvent, queryErrorEvent } from './events.js'
9
8
  import type { QueryEvent } from '../index.js'
10
9
  import type { QueryFunc } from '../query/types.js'
11
- import type { Logger, PeerId, RoutingOptions, AbortOptions } from '@libp2p/interface'
10
+ import type { Logger, PeerId, RoutingOptions, AbortOptions, PeerInfo } from '@libp2p/interface'
12
11
  import type { ConnectionManager } from '@libp2p/interface-internal'
13
- import type { PeerSet } from '@libp2p/peer-collections'
12
+ import type { Filter } from '@libp2p/utils/filters'
14
13
 
15
14
  export interface QueryPathOptions extends RoutingOptions {
16
15
  /**
@@ -21,18 +20,13 @@ export interface QueryPathOptions extends RoutingOptions {
21
20
  /**
22
21
  * Where we start our query
23
22
  */
24
- startingPeer: PeerId
23
+ startingPeers: PeerId[]
25
24
 
26
25
  /**
27
26
  * Who we are
28
27
  */
29
28
  ourPeerId: PeerId
30
29
 
31
- /**
32
- * When to stop querying
33
- */
34
- signal: AbortSignal
35
-
36
30
  /**
37
31
  * The query function to run with each peer
38
32
  */
@@ -44,20 +38,15 @@ export interface QueryPathOptions extends RoutingOptions {
44
38
  alpha: number
45
39
 
46
40
  /**
47
- * How many concurrent node/value lookups to run
41
+ * The index within `k` this path represents
48
42
  */
49
- pathIndex: number
43
+ path: number
50
44
 
51
45
  /**
52
- * How many concurrent node/value lookups to run
46
+ * How many disjoint paths are in this query
53
47
  */
54
48
  numPaths: number
55
49
 
56
- /**
57
- * A timeout for queryFunc in ms
58
- */
59
- queryFuncTimeout?: number
60
-
61
50
  /**
62
51
  * Query log
63
52
  */
@@ -66,12 +55,17 @@ export interface QueryPathOptions extends RoutingOptions {
66
55
  /**
67
56
  * Set of peers seen by this and other paths
68
57
  */
69
- peersSeen: PeerSet
58
+ peersSeen: Filter
70
59
 
71
60
  /**
72
61
  * The libp2p connection manager
73
62
  */
74
63
  connectionManager: ConnectionManager
64
+
65
+ /**
66
+ * The overall query abort signal
67
+ */
68
+ signal: AbortSignal
75
69
  }
76
70
 
77
71
  interface QueryQueueOptions extends AbortOptions {
@@ -83,60 +77,75 @@ interface QueryQueueOptions extends AbortOptions {
83
77
  * every peer encountered that we have not seen before
84
78
  */
85
79
  export async function * queryPath (options: QueryPathOptions): AsyncGenerator<QueryEvent, void, undefined> {
86
- const { key, startingPeer, ourPeerId, signal, query, alpha, pathIndex, numPaths, queryFuncTimeout, log, peersSeen, connectionManager } = options
80
+ const { key, startingPeers, ourPeerId, query, alpha, path, numPaths, log, peersSeen, connectionManager, signal } = options
81
+ const events = pushable<QueryEvent>({
82
+ objectMode: true
83
+ })
84
+
87
85
  // Only ALPHA node/value lookups are allowed at any given time for each process
88
86
  // https://github.com/libp2p/specs/tree/master/kad-dht#alpha-concurrency-parameter-%CE%B1
89
- const queue = new Queue<QueryEvent | undefined, QueryQueueOptions>({
87
+ const queue = new Queue<undefined, QueryQueueOptions>({
90
88
  concurrency: alpha,
91
89
  sort: (a, b) => uint8ArrayXorCompare(a.options.distance, b.options.distance)
92
90
  })
91
+ queue.addEventListener('idle', () => {
92
+ events.push(pathEndedEvent({
93
+ path: {
94
+ index: path,
95
+ queued: queue.queued,
96
+ running: queue.running,
97
+ total: queue.size
98
+ }
99
+ }, options))
100
+
101
+ events.end()
102
+ })
103
+ queue.addEventListener('error', (evt) => {
104
+ log.error('error during query - %e', evt.detail)
105
+ })
106
+
107
+ signal.addEventListener('abort', () => {
108
+ queue.abort()
109
+ events.end(new QueryAbortedError())
110
+ })
93
111
 
94
112
  // perform lookups on kadId, not the actual value
95
113
  const kadId = await convertBuffer(key)
96
114
 
97
115
  /**
98
- * Adds the passed peer to the query queue if it's not us and no
99
- * other path has passed through this peer
116
+ * Adds the passed peer to the query queue if it's not us and no other path
117
+ * has passed through this peer
100
118
  */
101
- function queryPeer (peer: PeerId, peerKadId: Uint8Array): void {
119
+ function queryPeer (peer: PeerInfo, peerKadId: Uint8Array): void {
102
120
  if (peer == null) {
103
121
  return
104
122
  }
105
123
 
106
- peersSeen.add(peer)
124
+ peersSeen.add(peer.id.toMultihash().bytes)
107
125
 
108
126
  const peerXor = uint8ArrayXor(peerKadId, kadId)
109
127
 
110
128
  queue.add(async () => {
111
- const signals = [signal]
112
-
113
- if (queryFuncTimeout != null) {
114
- signals.push(AbortSignal.timeout(queryFuncTimeout))
115
- }
116
-
117
- const compoundSignal = anySignal(signals)
118
-
119
- // this signal can get listened to a lot
120
- setMaxListeners(Infinity, compoundSignal)
121
-
122
129
  try {
123
130
  for await (const event of query({
124
131
  ...options,
125
132
  key,
126
133
  peer,
127
- signal: compoundSignal,
128
- pathIndex,
129
- numPaths
134
+ path: {
135
+ index: path,
136
+ queued: queue.queued,
137
+ running: queue.running,
138
+ total: queue.size
139
+ },
140
+ numPaths,
141
+ peerKadId,
142
+ signal
130
143
  })) {
131
- if (compoundSignal.aborted) {
132
- return
133
- }
134
-
135
144
  // if there are closer peers and the query has not completed, continue the query
136
145
  if (event.name === 'PEER_RESPONSE') {
137
146
  for (const closerPeer of event.closer) {
138
- if (peersSeen.has(closerPeer.id)) { // eslint-disable-line max-depth
139
- log.trace('already seen %p in query', closerPeer.id)
147
+ if (peersSeen.has(closerPeer.id.toMultihash().bytes)) { // eslint-disable-line max-depth
148
+ log('already seen %p in query', closerPeer.id)
140
149
  continue
141
150
  }
142
151
 
@@ -155,51 +164,52 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
155
164
 
156
165
  // only continue query if closer peer is actually closer
157
166
  if (uint8ArrayXorCompare(closerPeerXor, peerXor) !== -1) { // eslint-disable-line max-depth
158
- log.trace('skipping %p as they are not closer to %b than %p', closerPeer.id, key, peer)
167
+ log('skipping %p as they are not closer to %b than %p', closerPeer.id, key, peer)
159
168
  continue
160
169
  }
161
170
 
162
- log.trace('querying closer peer %p', closerPeer.id)
163
- queryPeer(closerPeer.id, closerPeerKadId)
171
+ log('querying closer peer %p', closerPeer.id)
172
+ queryPeer(closerPeer, closerPeerKadId)
164
173
  }
165
174
  }
166
175
 
167
- queue.safeDispatchEvent('completed', {
168
- detail: event
176
+ events.push({
177
+ ...event,
178
+ path: {
179
+ index: path,
180
+ queued: queue.queued,
181
+ running: queue.running,
182
+ total: queue.size
183
+ }
169
184
  })
170
185
  }
171
186
  } catch (err: any) {
172
- if (!signal.aborted) {
173
- return queryErrorEvent({
174
- from: peer,
175
- error: err
176
- }, options)
177
- }
178
- } finally {
179
- compoundSignal.clear()
187
+ // yield error event if query is continuing
188
+ events.push(queryErrorEvent({
189
+ from: peer.id,
190
+ error: err,
191
+ path: {
192
+ index: path,
193
+ queued: queue.queued,
194
+ running: queue.running - 1,
195
+ total: queue.size - 1
196
+ }
197
+ }, options))
180
198
  }
181
199
  }, {
182
200
  distance: peerXor
183
201
  }).catch(err => {
184
- log.error(err)
202
+ log.error('error during query - %e', err)
185
203
  })
186
204
  }
187
205
 
188
- // begin the query with the starting peer
189
- queryPeer(startingPeer, await convertPeerId(startingPeer))
190
-
191
- try {
192
- // yield results as they come in
193
- for await (const event of queue.toGenerator({ signal })) {
194
- if (event != null) {
195
- yield event
196
- }
197
- }
198
- } catch (err) {
199
- if (signal.aborted) {
200
- throw new QueryAbortedError('Query aborted')
201
- }
206
+ // begin the query with the starting peers
207
+ await Promise.all(
208
+ startingPeers.map(async startingPeer => {
209
+ queryPeer({ id: startingPeer, multiaddrs: [] }, await convertPeerId(startingPeer))
210
+ })
211
+ )
202
212
 
203
- throw err
204
- }
213
+ // yield results as they come in
214
+ yield * events
205
215
  }
@@ -1,15 +1,17 @@
1
- import type { QueryEvent } from '../index.js'
2
- import type { PeerId } from '@libp2p/interface'
1
+ import type { DisjointPath, QueryEvent } from '../index.js'
2
+ import type { PeerInfo } from '@libp2p/interface'
3
3
 
4
4
  export interface QueryContext {
5
5
  // the key we are looking up
6
6
  key: Uint8Array
7
7
  // the current peer being queried
8
- peer: PeerId
8
+ peer: PeerInfo
9
+ // the KAD ID of the peer being queried
10
+ peerKadId: Uint8Array
9
11
  // if this signal emits an 'abort' event, any long-lived processes or requests started as part of this query should be terminated
10
- signal: AbortSignal
12
+ signal?: AbortSignal
11
13
  // which disjoint path we are following
12
- pathIndex: number
14
+ path: DisjointPath
13
15
  // the total number of disjoint paths being executed
14
16
  numPaths: number
15
17
  }
package/src/query-self.ts CHANGED
@@ -4,18 +4,16 @@ import length from 'it-length'
4
4
  import { pipe } from 'it-pipe'
5
5
  import take from 'it-take'
6
6
  import pDefer from 'p-defer'
7
- import { pEvent } from 'p-event'
8
7
  import { QUERY_SELF_INTERVAL, QUERY_SELF_TIMEOUT, K, QUERY_SELF_INITIAL_INTERVAL } from './constants.js'
9
8
  import { timeOperationMethod } from './utils.js'
10
9
  import type { OperationMetrics } from './kad-dht.js'
11
10
  import type { PeerRouting } from './peer-routing/index.js'
12
- import type { RoutingTable } from './routing-table/index.js'
13
11
  import type { ComponentLogger, Logger, Metrics, PeerId, Startable } from '@libp2p/interface'
14
12
  import type { DeferredPromise } from 'p-defer'
13
+
15
14
  export interface QuerySelfInit {
16
15
  logPrefix: string
17
16
  peerRouting: PeerRouting
18
- routingTable: RoutingTable
19
17
  count?: number
20
18
  interval?: number
21
19
  initialInterval?: number
@@ -28,6 +26,7 @@ export interface QuerySelfComponents {
28
26
  peerId: PeerId
29
27
  logger: ComponentLogger
30
28
  metrics?: Metrics
29
+ events: EventTarget
31
30
  }
32
31
 
33
32
  /**
@@ -37,7 +36,7 @@ export class QuerySelf implements Startable {
37
36
  private readonly log: Logger
38
37
  private readonly peerId: PeerId
39
38
  private readonly peerRouting: PeerRouting
40
- private readonly routingTable: RoutingTable
39
+ private readonly events: EventTarget
41
40
  private readonly count: number
42
41
  private readonly interval: number
43
42
  private readonly initialInterval: number
@@ -51,9 +50,9 @@ export class QuerySelf implements Startable {
51
50
  constructor (components: QuerySelfComponents, init: QuerySelfInit) {
52
51
  this.peerId = components.peerId
53
52
  this.log = components.logger.forComponent(`${init.logPrefix}:query-self`)
53
+ this.events = components.events
54
54
  this.running = false
55
55
  this.peerRouting = init.peerRouting
56
- this.routingTable = init.routingTable
57
56
  this.count = init.count ?? K
58
57
  this.interval = init.interval ?? QUERY_SELF_INTERVAL
59
58
  this.initialInterval = init.initialInterval ?? QUERY_SELF_INITIAL_INTERVAL
@@ -122,20 +121,10 @@ export class QuerySelf implements Startable {
122
121
  setMaxListeners(Infinity, signal, this.controller.signal)
123
122
 
124
123
  try {
125
- if (this.routingTable.size === 0) {
126
- this.log('routing table was empty, waiting for some peers before running query')
127
- // wait to discover at least one DHT peer that isn't us
128
- await pEvent(this.routingTable, 'peer:add', {
129
- signal,
130
- filter: (event) => !this.peerId.equals(event.detail)
131
- })
132
- this.log('routing table has peers, continuing with query')
133
- }
134
-
135
124
  this.log('run self-query, look for %d peers timing out after %dms', this.count, this.queryTimeout)
136
125
  const start = Date.now()
137
126
 
138
- const found = await pipe(
127
+ const peers = await pipe(
139
128
  this.peerRouting.getClosestPeers(this.peerId.toMultihash().bytes, {
140
129
  signal,
141
130
  isSelfQuery: true
@@ -144,7 +133,16 @@ export class QuerySelf implements Startable {
144
133
  async (source) => length(source)
145
134
  )
146
135
 
147
- this.log('self-query found %d peers in %dms', found, Date.now() - start)
136
+ const duration = Date.now() - start
137
+
138
+ this.log('self-query found %d peers in %dms', peers, duration)
139
+
140
+ this.events.dispatchEvent(new CustomEvent('kad-dht:query:self', {
141
+ detail: {
142
+ peers,
143
+ duration
144
+ }
145
+ }))
148
146
  } catch (err: any) {
149
147
  this.log.error('self-query error', err)
150
148
  } finally {
@@ -91,7 +91,7 @@ export class ClosestPeers implements Startable {
91
91
  }
92
92
 
93
93
  async updatePeerTags (): Promise<void> {
94
- const newClosest = new PeerSet(this.newPeers?.peers.map(peer => peer.id))
94
+ const newClosest = new PeerSet(this.newPeers?.peers.map(({ peer }) => peer.id))
95
95
  const added = newClosest.difference(this.closestPeers)
96
96
  const removed = this.closestPeers.difference(newClosest)
97
97
  this.closestPeers = newClosest
@@ -300,7 +300,7 @@ export class KBucket {
300
300
  list.addWithKadId({ id: peer.peerId, multiaddrs: [] }, peer.kadId)
301
301
  }
302
302
 
303
- yield * map(list.peers, info => info.id)
303
+ yield * map(list.peers, ({ peer }) => peer.id)
304
304
  }
305
305
 
306
306
  /**
@@ -67,7 +67,7 @@ export class GetProvidersHandler implements DHTMessageHandler {
67
67
 
68
68
  return info
69
69
  })),
70
- this.peerRouting.getCloserPeersOffline(msg.key, this.peerId)
70
+ this.peerRouting.getCloserPeersOffline(msg.key, peerId)
71
71
  ])
72
72
 
73
73
  const response: Message = {
package/src/utils.ts CHANGED
@@ -140,6 +140,15 @@ export function fromPublicKeyKey (key: Uint8Array): PeerId {
140
140
  return peerIdFromMultihash(multihash)
141
141
  }
142
142
 
143
+ export function uint8ArrayToBigInt (buf: Uint8Array): bigint {
144
+ return BigInt(
145
+ `0x${
146
+ Array.from(buf)
147
+ .map(val => val.toString(16).padStart(2, '0')).join('')
148
+ }`
149
+ )
150
+ }
151
+
143
152
  /**
144
153
  * Create a new put record, encodes and signs it if enabled
145
154
  */