@libp2p/kad-dht 14.0.2 → 14.1.0-02f285fc8

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 (83) hide show
  1. package/README.md +36 -0
  2. package/dist/index.min.js +2 -2
  3. package/dist/src/constants.d.ts +4 -3
  4. package/dist/src/constants.d.ts.map +1 -1
  5. package/dist/src/constants.js +8 -3
  6. package/dist/src/constants.js.map +1 -1
  7. package/dist/src/content-fetching/index.d.ts +1 -0
  8. package/dist/src/content-fetching/index.d.ts.map +1 -1
  9. package/dist/src/content-fetching/index.js +5 -3
  10. package/dist/src/content-fetching/index.js.map +1 -1
  11. package/dist/src/index.d.ts +107 -2
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +36 -0
  14. package/dist/src/index.js.map +1 -1
  15. package/dist/src/kad-dht.d.ts +14 -1
  16. package/dist/src/kad-dht.d.ts.map +1 -1
  17. package/dist/src/kad-dht.js +76 -27
  18. package/dist/src/kad-dht.js.map +1 -1
  19. package/dist/src/network.d.ts +1 -0
  20. package/dist/src/network.d.ts.map +1 -1
  21. package/dist/src/network.js +4 -5
  22. package/dist/src/network.js.map +1 -1
  23. package/dist/src/peer-routing/index.d.ts.map +1 -1
  24. package/dist/src/peer-routing/index.js +9 -10
  25. package/dist/src/peer-routing/index.js.map +1 -1
  26. package/dist/src/providers.d.ts +21 -50
  27. package/dist/src/providers.d.ts.map +1 -1
  28. package/dist/src/providers.js +51 -192
  29. package/dist/src/providers.js.map +1 -1
  30. package/dist/src/query/manager.d.ts +1 -1
  31. package/dist/src/query/manager.d.ts.map +1 -1
  32. package/dist/src/query/manager.js +8 -19
  33. package/dist/src/query/manager.js.map +1 -1
  34. package/dist/src/query-self.d.ts +3 -1
  35. package/dist/src/query-self.d.ts.map +1 -1
  36. package/dist/src/query-self.js +17 -16
  37. package/dist/src/query-self.js.map +1 -1
  38. package/dist/src/reprovider.d.ts +63 -0
  39. package/dist/src/reprovider.d.ts.map +1 -0
  40. package/dist/src/reprovider.js +161 -0
  41. package/dist/src/reprovider.js.map +1 -0
  42. package/dist/src/routing-table/closest-peers.d.ts +1 -0
  43. package/dist/src/routing-table/closest-peers.d.ts.map +1 -1
  44. package/dist/src/routing-table/closest-peers.js +7 -0
  45. package/dist/src/routing-table/closest-peers.js.map +1 -1
  46. package/dist/src/routing-table/index.d.ts +1 -0
  47. package/dist/src/routing-table/index.d.ts.map +1 -1
  48. package/dist/src/routing-table/index.js +14 -11
  49. package/dist/src/routing-table/index.js.map +1 -1
  50. package/dist/src/rpc/handlers/get-value.d.ts +1 -0
  51. package/dist/src/rpc/handlers/get-value.d.ts.map +1 -1
  52. package/dist/src/rpc/handlers/get-value.js +3 -1
  53. package/dist/src/rpc/handlers/get-value.js.map +1 -1
  54. package/dist/src/rpc/handlers/put-value.d.ts +1 -0
  55. package/dist/src/rpc/handlers/put-value.d.ts.map +1 -1
  56. package/dist/src/rpc/handlers/put-value.js +3 -1
  57. package/dist/src/rpc/handlers/put-value.js.map +1 -1
  58. package/dist/src/rpc/index.d.ts +1 -0
  59. package/dist/src/rpc/index.d.ts.map +1 -1
  60. package/dist/src/rpc/index.js +9 -10
  61. package/dist/src/rpc/index.js.map +1 -1
  62. package/dist/src/utils.d.ts +20 -1
  63. package/dist/src/utils.d.ts.map +1 -1
  64. package/dist/src/utils.js +87 -4
  65. package/dist/src/utils.js.map +1 -1
  66. package/package.json +12 -14
  67. package/src/constants.ts +11 -5
  68. package/src/content-fetching/index.ts +5 -3
  69. package/src/index.ts +116 -2
  70. package/src/kad-dht.ts +94 -41
  71. package/src/network.ts +5 -5
  72. package/src/peer-routing/index.ts +9 -11
  73. package/src/providers.ts +57 -244
  74. package/src/query/manager.ts +12 -28
  75. package/src/query-self.ts +20 -17
  76. package/src/reprovider.ts +226 -0
  77. package/src/routing-table/closest-peers.ts +9 -0
  78. package/src/routing-table/index.ts +16 -11
  79. package/src/rpc/handlers/get-value.ts +3 -1
  80. package/src/rpc/handlers/put-value.ts +3 -1
  81. package/src/rpc/index.ts +10 -10
  82. package/src/utils.ts +102 -4
  83. package/dist/typedoc-urls.json +0 -56
package/src/kad-dht.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { NotFoundError, TypedEventEmitter, contentRoutingSymbol, peerDiscoverySymbol, peerRoutingSymbol, serviceCapabilities, serviceDependencies, start, stop } from '@libp2p/interface'
2
2
  import drain from 'it-drain'
3
+ import createMortice from 'mortice'
3
4
  import pDefer from 'p-defer'
4
5
  import { PROTOCOL } from './constants.js'
5
6
  import { ContentFetching } from './content-fetching/index.js'
@@ -11,16 +12,18 @@ import { QueryManager } from './query/manager.js'
11
12
  import { QuerySelf } from './query-self.js'
12
13
  import { selectors as recordSelectors } from './record/selectors.js'
13
14
  import { validators as recordValidators } from './record/validators.js'
15
+ import { Reprovider } from './reprovider.js'
14
16
  import { RoutingTable } from './routing-table/index.js'
15
17
  import { RoutingTableRefresh } from './routing-table/refresh.js'
16
18
  import { RPC } from './rpc/index.js'
17
19
  import { TopologyListener } from './topology-listener.js'
18
20
  import {
19
21
  multiaddrIsPublic,
20
- removePrivateAddressesMapper
22
+ removePrivateAddressesMapper,
23
+ timeOperationGenerator
21
24
  } from './utils.js'
22
25
  import type { KadDHTComponents, KadDHTInit, Validators, Selectors, KadDHT as KadDHTInterface, QueryEvent, PeerInfoMapper } from './index.js'
23
- import type { ContentRouting, Logger, PeerDiscovery, PeerDiscoveryEvents, PeerId, PeerInfo, PeerRouting, RoutingOptions, Startable } from '@libp2p/interface'
26
+ import type { ContentRouting, CounterGroup, Logger, MetricGroup, PeerDiscovery, PeerDiscoveryEvents, PeerId, PeerInfo, PeerRouting, RoutingOptions, Startable } from '@libp2p/interface'
24
27
  import type { CID } from 'multiformats/cid'
25
28
 
26
29
  /**
@@ -37,6 +40,10 @@ class DHTContentRouting implements ContentRouting {
37
40
  await drain(this.dht.provide(cid, options))
38
41
  }
39
42
 
43
+ async cancelReprovide (key: CID): Promise<void> {
44
+ await this.dht.cancelReprovide(key)
45
+ }
46
+
40
47
  async * findProviders (cid: CID, options: RoutingOptions = {}): AsyncGenerator<PeerInfo, void, undefined> {
41
48
  for await (const event of this.dht.findProviders(cid, options)) {
42
49
  if (event.name === 'PROVIDER') {
@@ -92,6 +99,15 @@ class DHTPeerRouting implements PeerRouting {
92
99
  export const DEFAULT_MAX_INBOUND_STREAMS = 32
93
100
  export const DEFAULT_MAX_OUTBOUND_STREAMS = 64
94
101
 
102
+ export type Operation = 'GET_VALUE' | 'FIND_PROVIDERS' | 'FIND_PEER' | 'GET_CLOSEST_PEERS' | 'PROVIDE' | 'PUT_VALUE' | 'SELF_QUERY'
103
+
104
+ export interface OperationMetrics {
105
+ queries?: MetricGroup<Operation>
106
+ errors?: CounterGroup<Operation>
107
+ queryTime?: MetricGroup<Operation>
108
+ errorTime?: MetricGroup<Operation>
109
+ }
110
+
95
111
  /**
96
112
  * A DHT implementation modelled after Kademlia with S/Kademlia modifications.
97
113
  * Original implementation in go: https://github.com/libp2p/go-libp2p-kad-dht.
@@ -123,6 +139,7 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
123
139
  private readonly dhtContentRouting: DHTContentRouting
124
140
  private readonly dhtPeerRouting: DHTPeerRouting
125
141
  private readonly peerInfoMapper: PeerInfoMapper
142
+ private readonly reprovider: Reprovider
126
143
 
127
144
  /**
128
145
  * Create a new KadDHT
@@ -130,48 +147,52 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
130
147
  constructor (components: KadDHTComponents, init: KadDHTInit = {}) {
131
148
  super()
132
149
 
133
- const {
134
- kBucketSize,
135
- clientMode,
136
- validators,
137
- selectors,
138
- querySelfInterval,
139
- protocol,
140
- logPrefix,
141
- maxInboundStreams,
142
- maxOutboundStreams,
143
- providers: providersInit
144
- } = init
150
+ const logPrefix = init.logPrefix ?? 'libp2p:kad-dht'
151
+ const datastorePrefix = init.datastorePrefix ?? '/dht'
152
+ const metricsPrefix = init.metricsPrefix ?? 'libp2p_kad_dht'
145
153
 
146
- const loggingPrefix = logPrefix ?? 'libp2p:kad-dht'
154
+ const operationMetrics: OperationMetrics = {
155
+ queries: components.metrics?.registerMetricGroup(`${metricsPrefix}_operations_total`, { label: 'operation' }),
156
+ errors: components.metrics?.registerCounterGroup(`${metricsPrefix}_operation_errors_total`, { label: 'operation' }),
157
+ queryTime: components.metrics?.registerMetricGroup(`${metricsPrefix}_operation_time_seconds`, { label: 'operation' }),
158
+ errorTime: components.metrics?.registerMetricGroup(`${metricsPrefix}_operation_error_time_seconds`, { label: 'operation' })
159
+ }
147
160
 
148
161
  this.running = false
149
162
  this.components = components
150
- this.log = components.logger.forComponent(loggingPrefix)
151
- this.protocol = protocol ?? PROTOCOL
152
- this.kBucketSize = kBucketSize ?? 20
153
- this.clientMode = clientMode ?? true
154
- this.maxInboundStreams = maxInboundStreams ?? DEFAULT_MAX_INBOUND_STREAMS
155
- this.maxOutboundStreams = maxOutboundStreams ?? DEFAULT_MAX_OUTBOUND_STREAMS
163
+ this.log = components.logger.forComponent(logPrefix)
164
+ this.protocol = init.protocol ?? PROTOCOL
165
+ this.kBucketSize = init.kBucketSize ?? 20
166
+ this.clientMode = init.clientMode ?? true
167
+ this.maxInboundStreams = init.maxInboundStreams ?? DEFAULT_MAX_INBOUND_STREAMS
168
+ this.maxOutboundStreams = init.maxOutboundStreams ?? DEFAULT_MAX_OUTBOUND_STREAMS
156
169
  this.peerInfoMapper = init.peerInfoMapper ?? removePrivateAddressesMapper
157
170
 
158
- this.providers = new Providers(components, providersInit ?? {})
171
+ const providerLock = createMortice()
172
+
173
+ this.providers = new Providers(components, {
174
+ ...init.providers,
175
+ logPrefix,
176
+ datastorePrefix,
177
+ lock: providerLock
178
+ })
159
179
 
160
180
  this.validators = {
161
181
  ...recordValidators,
162
- ...validators
182
+ ...init.validators
163
183
  }
164
184
  this.selectors = {
165
185
  ...recordSelectors,
166
- ...selectors
186
+ ...init.selectors
167
187
  }
168
188
  this.network = new Network(components, {
169
189
  protocol: this.protocol,
170
- logPrefix: loggingPrefix
190
+ logPrefix,
191
+ metricsPrefix
171
192
  })
172
193
 
173
194
  this.routingTable = new RoutingTable(components, {
174
- kBucketSize,
195
+ kBucketSize: init.kBucketSize,
175
196
  pingOldContactTimeout: init.pingOldContactTimeout,
176
197
  pingOldContactConcurrency: init.pingOldContactConcurrency,
177
198
  pingOldContactMaxQueueSize: init.pingOldContactMaxQueueSize,
@@ -179,7 +200,8 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
179
200
  pingNewContactConcurrency: init.pingNewContactConcurrency,
180
201
  pingNewContactMaxQueueSize: init.pingNewContactMaxQueueSize,
181
202
  protocol: this.protocol,
182
- logPrefix: loggingPrefix,
203
+ logPrefix,
204
+ metricsPrefix,
183
205
  prefixLength: init.prefixLength,
184
206
  splitThreshold: init.kBucketSplitThreshold,
185
207
  network: this.network
@@ -198,7 +220,8 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
198
220
  this.queryManager = new QueryManager(components, {
199
221
  // Number of disjoint query paths to use - This is set to `kBucketSize/2` per the S/Kademlia paper
200
222
  disjointPaths: Math.ceil(this.kBucketSize / 2),
201
- logPrefix: loggingPrefix,
223
+ logPrefix,
224
+ metricsPrefix,
202
225
  initialQuerySelfHasRun,
203
226
  routingTable: this.routingTable
204
227
  })
@@ -209,7 +232,7 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
209
232
  network: this.network,
210
233
  validators: this.validators,
211
234
  queryManager: this.queryManager,
212
- logPrefix: loggingPrefix
235
+ logPrefix
213
236
  })
214
237
  this.contentFetching = new ContentFetching(components, {
215
238
  validators: this.validators,
@@ -217,7 +240,7 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
217
240
  peerRouting: this.peerRouting,
218
241
  queryManager: this.queryManager,
219
242
  network: this.network,
220
- logPrefix: loggingPrefix
243
+ logPrefix
221
244
  })
222
245
  this.contentRouting = new KADDHTContentRouting(components, {
223
246
  network: this.network,
@@ -225,32 +248,43 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
225
248
  queryManager: this.queryManager,
226
249
  routingTable: this.routingTable,
227
250
  providers: this.providers,
228
- logPrefix: loggingPrefix
251
+ logPrefix
229
252
  })
230
253
  this.routingTableRefresh = new RoutingTableRefresh(components, {
231
254
  peerRouting: this.peerRouting,
232
255
  routingTable: this.routingTable,
233
- logPrefix: loggingPrefix
256
+ logPrefix
234
257
  })
235
258
  this.rpc = new RPC(components, {
236
259
  routingTable: this.routingTable,
237
260
  providers: this.providers,
238
261
  peerRouting: this.peerRouting,
239
262
  validators: this.validators,
240
- logPrefix: loggingPrefix,
263
+ logPrefix,
264
+ metricsPrefix,
241
265
  peerInfoMapper: this.peerInfoMapper
242
266
  })
243
267
  this.topologyListener = new TopologyListener(components, {
244
268
  protocol: this.protocol,
245
- logPrefix: loggingPrefix
269
+ logPrefix
246
270
  })
247
271
  this.querySelf = new QuerySelf(components, {
248
272
  peerRouting: this.peerRouting,
249
- interval: querySelfInterval,
273
+ interval: init.querySelfInterval,
250
274
  initialInterval: init.initialQuerySelfInterval,
251
- logPrefix: loggingPrefix,
275
+ logPrefix,
252
276
  initialQuerySelfHasRun,
253
- routingTable: this.routingTable
277
+ routingTable: this.routingTable,
278
+ operationMetrics
279
+ })
280
+ this.reprovider = new Reprovider(components, {
281
+ ...init.reprovide,
282
+ logPrefix,
283
+ metricsPrefix,
284
+ datastorePrefix,
285
+ contentRouting: this.contentRouting,
286
+ lock: providerLock,
287
+ operationMetrics
254
288
  })
255
289
 
256
290
  // handle peers being discovered during processing of DHT messages
@@ -312,6 +346,13 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
312
346
  })
313
347
  })
314
348
  }
349
+
350
+ this.get = timeOperationGenerator(this.get.bind(this), operationMetrics, 'GET_VALUE')
351
+ this.findProviders = timeOperationGenerator(this.findProviders.bind(this), operationMetrics, 'FIND_PROVIDERS')
352
+ this.findPeer = timeOperationGenerator(this.findPeer.bind(this), operationMetrics, 'FIND_PEER')
353
+ this.getClosestPeers = timeOperationGenerator(this.getClosestPeers.bind(this), operationMetrics, 'GET_CLOSEST_PEERS')
354
+ this.provide = timeOperationGenerator(this.provide.bind(this), operationMetrics, 'PROVIDE')
355
+ this.put = timeOperationGenerator(this.put.bind(this), operationMetrics, 'PUT_VALUE')
315
356
  }
316
357
 
317
358
  readonly [Symbol.toStringTag] = '@libp2p/kad-dht'
@@ -403,6 +444,10 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
403
444
  * Start listening to incoming connections.
404
445
  */
405
446
  async start (): Promise<void> {
447
+ if (this.running) {
448
+ return
449
+ }
450
+
406
451
  this.running = true
407
452
 
408
453
  // Only respond to queries when not in client mode
@@ -410,11 +455,11 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
410
455
 
411
456
  await start(
412
457
  this.routingTable,
413
- this.providers,
414
458
  this.queryManager,
415
459
  this.network,
416
460
  this.topologyListener,
417
- this.routingTableRefresh
461
+ this.routingTableRefresh,
462
+ this.reprovider
418
463
  )
419
464
 
420
465
  // Query self after other components are configured
@@ -432,12 +477,12 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
432
477
 
433
478
  await stop(
434
479
  this.querySelf,
435
- this.providers,
436
480
  this.queryManager,
437
481
  this.network,
438
482
  this.routingTable,
439
483
  this.routingTableRefresh,
440
- this.topologyListener
484
+ this.topologyListener,
485
+ this.reprovider
441
486
  )
442
487
  }
443
488
 
@@ -464,6 +509,14 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
464
509
  yield * this.contentRouting.provide(key, this.components.addressManager.getAddresses(), options)
465
510
  }
466
511
 
512
+ /**
513
+ * Provider records must be re-published every 24 hours - pass a previously
514
+ * provided CID here to not re-publish a record for it any more
515
+ */
516
+ async cancelReprovide (key: CID): Promise<void> {
517
+ await this.providers.removeProvider(key, this.components.peerId)
518
+ }
519
+
467
520
  /**
468
521
  * Search the dht for providers of the given CID
469
522
  */
package/src/network.ts CHANGED
@@ -16,6 +16,7 @@ import type { AbortOptions, Logger, Stream, PeerId, PeerInfo, Startable, Routing
16
16
  export interface NetworkInit {
17
17
  protocol: string
18
18
  logPrefix: string
19
+ metricsPrefix: string
19
20
  timeout?: Omit<AdaptiveTimeoutInit, 'metricsName' | 'metrics'>
20
21
  }
21
22
 
@@ -43,19 +44,18 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
43
44
  constructor (components: KadDHTComponents, init: NetworkInit) {
44
45
  super()
45
46
 
46
- const { protocol } = init
47
47
  this.components = components
48
48
  this.log = components.logger.forComponent(`${init.logPrefix}:network`)
49
49
  this.running = false
50
- this.protocol = protocol
50
+ this.protocol = init.protocol
51
51
  this.timeout = new AdaptiveTimeout({
52
52
  ...(init.timeout ?? {}),
53
53
  metrics: components.metrics,
54
- metricName: `${init.logPrefix.replaceAll(':', '_')}_network_message_send_times_milliseconds`
54
+ metricName: `${init.metricsPrefix}_network_message_send_times_milliseconds`
55
55
  })
56
56
  this.metrics = {
57
- operations: components.metrics?.registerCounterGroup(`${init.logPrefix.replaceAll(':', '_')}_outbound_rpc_requests_total`),
58
- errors: components.metrics?.registerCounterGroup(`${init.logPrefix.replaceAll(':', '_')}_outbound_rpc_errors_total`)
57
+ operations: components.metrics?.registerCounterGroup(`${init.metricsPrefix}_outbound_rpc_requests_total`),
58
+ errors: components.metrics?.registerCounterGroup(`${init.metricsPrefix}_outbound_rpc_errors_total`)
59
59
  }
60
60
  }
61
61
 
@@ -12,7 +12,7 @@ import {
12
12
  valueEvent
13
13
  } from '../query/events.js'
14
14
  import { verifyRecord } from '../record/validators.js'
15
- import * as utils from '../utils.js'
15
+ import { convertBuffer, keyForPublicKey } from '../utils.js'
16
16
  import type { KadDHTComponents, DHTRecord, FinalPeerEvent, QueryEvent, Validators } from '../index.js'
17
17
  import type { Message } from '../message/dht.js'
18
18
  import type { Network } from '../network.js'
@@ -39,15 +39,13 @@ export class PeerRouting {
39
39
  private readonly peerId: PeerId
40
40
 
41
41
  constructor (components: KadDHTComponents, init: PeerRoutingInit) {
42
- const { routingTable, network, validators, queryManager, logPrefix } = init
43
-
44
- this.routingTable = routingTable
45
- this.network = network
46
- this.validators = validators
47
- this.queryManager = queryManager
42
+ this.routingTable = init.routingTable
43
+ this.network = init.network
44
+ this.validators = init.validators
45
+ this.queryManager = init.queryManager
48
46
  this.peerStore = components.peerStore
49
47
  this.peerId = components.peerId
50
- this.log = components.logger.forComponent(`${logPrefix}:peer-routing`)
48
+ this.log = components.logger.forComponent(`${init.logPrefix}:peer-routing`)
51
49
  }
52
50
 
53
51
  /**
@@ -108,7 +106,7 @@ export class PeerRouting {
108
106
  * Get the public key directly from a node
109
107
  */
110
108
  async * getPublicKeyFromNode (peer: PeerId, options: RoutingOptions = {}): AsyncGenerator<QueryEvent> {
111
- const pkKey = utils.keyForPublicKey(peer)
109
+ const pkKey = keyForPublicKey(peer)
112
110
 
113
111
  for await (const event of this._getValueSingle(peer, pkKey, options)) {
114
112
  yield event
@@ -205,7 +203,7 @@ export class PeerRouting {
205
203
  */
206
204
  async * getClosestPeers (key: Uint8Array, options: QueryOptions = {}): AsyncGenerator<QueryEvent> {
207
205
  this.log('getClosestPeers to %b', key)
208
- const kadId = await utils.convertBuffer(key)
206
+ const kadId = await convertBuffer(key)
209
207
  const tablePeers = this.routingTable.closestPeers(kadId)
210
208
  const self = this // eslint-disable-line @typescript-eslint/no-this-alias
211
209
 
@@ -289,7 +287,7 @@ export class PeerRouting {
289
287
  * than self
290
288
  */
291
289
  async getCloserPeersOffline (key: Uint8Array, closerThan: PeerId): Promise<PeerInfo[]> {
292
- const id = await utils.convertBuffer(key)
290
+ const id = await convertBuffer(key)
293
291
  const ids = this.routingTable.closestPeers(id)
294
292
  const output: PeerInfo[] = []
295
293