@libp2p/kad-dht 14.0.2 → 14.1.0-a5cd8cfbe

Sign up to get free protection for your applications and to get access to all the features.
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