@libp2p/kad-dht 15.1.1 → 15.1.2-3528df829

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 (86) 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 +2 -0
  4. package/dist/src/constants.d.ts.map +1 -1
  5. package/dist/src/constants.js +4 -0
  6. package/dist/src/constants.js.map +1 -1
  7. package/dist/src/content-fetching/index.d.ts +2 -2
  8. package/dist/src/content-fetching/index.d.ts.map +1 -1
  9. package/dist/src/content-fetching/index.js +6 -6
  10. package/dist/src/content-fetching/index.js.map +1 -1
  11. package/dist/src/content-routing/index.js +3 -3
  12. package/dist/src/content-routing/index.js.map +1 -1
  13. package/dist/src/errors.d.ts +0 -6
  14. package/dist/src/errors.d.ts.map +1 -1
  15. package/dist/src/errors.js +0 -9
  16. package/dist/src/errors.js.map +1 -1
  17. package/dist/src/index.d.ts +16 -5
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/kad-dht.d.ts +6 -4
  21. package/dist/src/kad-dht.d.ts.map +1 -1
  22. package/dist/src/kad-dht.js +23 -13
  23. package/dist/src/kad-dht.js.map +1 -1
  24. package/dist/src/peer-distance-list.d.ts +4 -4
  25. package/dist/src/peer-distance-list.d.ts.map +1 -1
  26. package/dist/src/peer-distance-list.js +6 -6
  27. package/dist/src/peer-distance-list.js.map +1 -1
  28. package/dist/src/peer-routing/index.d.ts +4 -3
  29. package/dist/src/peer-routing/index.d.ts.map +1 -1
  30. package/dist/src/peer-routing/index.js +17 -17
  31. package/dist/src/peer-routing/index.js.map +1 -1
  32. package/dist/src/providers.d.ts.map +1 -1
  33. package/dist/src/providers.js +6 -6
  34. package/dist/src/providers.js.map +1 -1
  35. package/dist/src/query/manager.d.ts.map +1 -1
  36. package/dist/src/query/manager.js +3 -1
  37. package/dist/src/query/manager.js.map +1 -1
  38. package/dist/src/query/query-path.d.ts.map +1 -1
  39. package/dist/src/query/query-path.js +11 -5
  40. package/dist/src/query/query-path.js.map +1 -1
  41. package/dist/src/query-self.d.ts.map +1 -1
  42. package/dist/src/query-self.js +1 -0
  43. package/dist/src/query-self.js.map +1 -1
  44. package/dist/src/record/validators.d.ts +2 -1
  45. package/dist/src/record/validators.d.ts.map +1 -1
  46. package/dist/src/record/validators.js +3 -3
  47. package/dist/src/record/validators.js.map +1 -1
  48. package/dist/src/reprovider.d.ts.map +1 -1
  49. package/dist/src/reprovider.js +11 -7
  50. package/dist/src/reprovider.js.map +1 -1
  51. package/dist/src/routing-table/index.d.ts +2 -2
  52. package/dist/src/routing-table/index.d.ts.map +1 -1
  53. package/dist/src/routing-table/index.js +14 -13
  54. package/dist/src/routing-table/index.js.map +1 -1
  55. package/dist/src/routing-table/k-bucket.d.ts +12 -7
  56. package/dist/src/routing-table/k-bucket.d.ts.map +1 -1
  57. package/dist/src/routing-table/k-bucket.js +20 -14
  58. package/dist/src/routing-table/k-bucket.js.map +1 -1
  59. package/dist/src/routing-table/refresh.d.ts +5 -5
  60. package/dist/src/routing-table/refresh.d.ts.map +1 -1
  61. package/dist/src/routing-table/refresh.js +20 -14
  62. package/dist/src/routing-table/refresh.js.map +1 -1
  63. package/dist/src/utils.d.ts +3 -3
  64. package/dist/src/utils.d.ts.map +1 -1
  65. package/dist/src/utils.js +4 -3
  66. package/dist/src/utils.js.map +1 -1
  67. package/package.json +15 -15
  68. package/src/constants.ts +6 -0
  69. package/src/content-fetching/index.ts +7 -7
  70. package/src/content-routing/index.ts +3 -3
  71. package/src/errors.ts +0 -10
  72. package/src/index.ts +18 -5
  73. package/src/kad-dht.ts +26 -14
  74. package/src/peer-distance-list.ts +7 -7
  75. package/src/peer-routing/index.ts +18 -17
  76. package/src/providers.ts +10 -6
  77. package/src/query/manager.ts +3 -1
  78. package/src/query/query-path.ts +11 -5
  79. package/src/query-self.ts +2 -0
  80. package/src/record/validators.ts +4 -3
  81. package/src/reprovider.ts +11 -7
  82. package/src/routing-table/index.ts +14 -13
  83. package/src/routing-table/k-bucket.ts +30 -18
  84. package/src/routing-table/refresh.ts +20 -15
  85. package/src/utils.ts +5 -4
  86. package/dist/typedoc-urls.json +0 -63
@@ -23,6 +23,7 @@ import type { QueryFunc } from '../query/types.js'
23
23
  import type { RoutingTable } from '../routing-table/index.js'
24
24
  import type { ComponentLogger, Logger, Metrics, PeerId, PeerInfo, PeerStore, RoutingOptions } from '@libp2p/interface'
25
25
  import type { ConnectionManager } from '@libp2p/interface-internal'
26
+ import type { AbortOptions } from 'it-pushable'
26
27
 
27
28
  export interface PeerRoutingComponents {
28
29
  peerId: PeerId
@@ -68,15 +69,15 @@ export class PeerRouting {
68
69
  * Look if we are connected to a peer with the given id.
69
70
  * Returns its id and addresses, if found, otherwise `undefined`.
70
71
  */
71
- async findPeerLocal (peer: PeerId): Promise<PeerInfo | undefined> {
72
+ async findPeerLocal (peer: PeerId, options?: AbortOptions): Promise<PeerInfo | undefined> {
72
73
  let peerData
73
- const p = await this.routingTable.find(peer)
74
+ const p = await this.routingTable.find(peer, options)
74
75
 
75
76
  if (p != null) {
76
77
  this.log('findPeerLocal found %p in routing table', peer)
77
78
 
78
79
  try {
79
- peerData = await this.components.peerStore.get(p)
80
+ peerData = await this.components.peerStore.get(p, options)
80
81
  } catch (err: any) {
81
82
  if (err.name !== 'NotFoundError') {
82
83
  throw err
@@ -86,7 +87,7 @@ export class PeerRouting {
86
87
 
87
88
  if (peerData == null) {
88
89
  try {
89
- peerData = await this.components.peerStore.get(peer)
90
+ peerData = await this.components.peerStore.get(peer, options)
90
91
  } catch (err: any) {
91
92
  if (err.name !== 'NotFoundError') {
92
93
  throw err
@@ -168,7 +169,7 @@ export class PeerRouting {
168
169
 
169
170
  if (options.useCache !== false) {
170
171
  // Try to find locally
171
- const pi = await this.findPeerLocal(id)
172
+ const pi = await this.findPeerLocal(id, options)
172
173
 
173
174
  // already got it
174
175
  if (pi != null) {
@@ -240,7 +241,7 @@ export class PeerRouting {
240
241
  */
241
242
  async * getClosestPeers (key: Uint8Array, options: QueryOptions = {}): AsyncGenerator<QueryEvent> {
242
243
  this.log('getClosestPeers to %b', key)
243
- const kadId = await convertBuffer(key)
244
+ const kadId = await convertBuffer(key, options)
244
245
  const peers = new PeerDistanceList(kadId, this.routingTable.kBucketSize)
245
246
  const self = this
246
247
 
@@ -268,7 +269,7 @@ export class PeerRouting {
268
269
  for (let { peer, path } of peers.peers) {
269
270
  try {
270
271
  if (peer.multiaddrs.length === 0) {
271
- peer = await self.components.peerStore.getInfo(peer.id)
272
+ peer = await self.components.peerStore.getInfo(peer.id, options)
272
273
  }
273
274
 
274
275
  if (peer.multiaddrs.length === 0) {
@@ -277,7 +278,7 @@ export class PeerRouting {
277
278
 
278
279
  yield finalPeerEvent({
279
280
  from: this.components.peerId,
280
- peer: await self.components.peerStore.getInfo(peer.id),
281
+ peer: await self.components.peerStore.getInfo(peer.id, options),
281
282
  path: {
282
283
  index: path.index,
283
284
  queued: 0,
@@ -303,7 +304,7 @@ export class PeerRouting {
303
304
  if (event.record != null) {
304
305
  // We have a record
305
306
  try {
306
- await this._verifyRecordOnline(event.record)
307
+ await this._verifyRecordOnline(event.record, options)
307
308
  } catch (err: any) {
308
309
  const errMsg = 'invalid record received, discarded'
309
310
  this.log(errMsg)
@@ -326,19 +327,19 @@ export class PeerRouting {
326
327
  * Verify a record, fetching missing public keys from the network.
327
328
  * Throws an error if the record is invalid.
328
329
  */
329
- async _verifyRecordOnline (record: DHTRecord): Promise<void> {
330
+ async _verifyRecordOnline (record: DHTRecord, options?: AbortOptions): Promise<void> {
330
331
  if (record.timeReceived == null) {
331
332
  throw new InvalidRecordError('invalid record received')
332
333
  }
333
334
 
334
- await verifyRecord(this.validators, new Libp2pRecord(record.key, record.value, record.timeReceived))
335
+ await verifyRecord(this.validators, new Libp2pRecord(record.key, record.value, record.timeReceived), options)
335
336
  }
336
337
 
337
338
  /**
338
339
  * Get the peers in our routing table that are closer than the passed PeerId
339
340
  * to the passed key
340
341
  */
341
- async getCloserPeersOffline (key: Uint8Array, closerThan: PeerId): Promise<PeerInfo[]> {
342
+ async getCloserPeersOffline (key: Uint8Array, closerThan: PeerId, options?: AbortOptions): Promise<PeerInfo[]> {
342
343
  const output: PeerInfo[] = []
343
344
 
344
345
  // try getting the peer directly
@@ -346,7 +347,7 @@ export class PeerRouting {
346
347
  const multihash = Digest.decode(key)
347
348
  const targetPeerId = peerIdFromMultihash(multihash)
348
349
 
349
- const peer = await this.components.peerStore.get(targetPeerId)
350
+ const peer = await this.components.peerStore.get(targetPeerId, options)
350
351
 
351
352
  output.push({
352
353
  id: peer.id,
@@ -354,13 +355,13 @@ export class PeerRouting {
354
355
  })
355
356
  } catch {}
356
357
 
357
- const keyKadId = await convertBuffer(key)
358
+ const keyKadId = await convertBuffer(key, options)
358
359
  const ids = this.routingTable.closestPeers(keyKadId)
359
- const closerThanKadId = await convertPeerId(closerThan)
360
+ const closerThanKadId = await convertPeerId(closerThan, options)
360
361
  const requesterXor = uint8ArrayXor(closerThanKadId, keyKadId)
361
362
 
362
363
  for (const peerId of ids) {
363
- const peerKadId = await convertPeerId(peerId)
364
+ const peerKadId = await convertPeerId(peerId, options)
364
365
  const peerXor = uint8ArrayXor(peerKadId, keyKadId)
365
366
 
366
367
  // only include if peer is closer than requester
@@ -369,7 +370,7 @@ export class PeerRouting {
369
370
  }
370
371
 
371
372
  try {
372
- output.push(await this.components.peerStore.getInfo(peerId))
373
+ output.push(await this.components.peerStore.getInfo(peerId, options))
373
374
  } catch (err: any) {
374
375
  if (err.name !== 'NotFoundError') {
375
376
  throw err
package/src/providers.ts CHANGED
@@ -18,6 +18,10 @@ export interface ProvidersComponents {
18
18
  metrics?: Metrics
19
19
  }
20
20
 
21
+ interface WriteProviderEntryOptions extends AbortOptions {
22
+ time?: Date
23
+ }
24
+
21
25
  /**
22
26
  * Provides a mechanism to add and remove provider records from the datastore
23
27
  */
@@ -38,11 +42,11 @@ export class Providers {
38
42
  * Add a new provider for the given CID
39
43
  */
40
44
  async addProvider (cid: CID, provider: PeerId, options?: AbortOptions): Promise<void> {
41
- const release = await this.lock.readLock()
45
+ const release = await this.lock.readLock(options)
42
46
 
43
47
  try {
44
48
  this.log.trace('%p provides %s', provider, cid)
45
- await this.writeProviderEntry(cid, provider)
49
+ await this.writeProviderEntry(cid, provider, options)
46
50
  } finally {
47
51
  release()
48
52
  }
@@ -52,7 +56,7 @@ export class Providers {
52
56
  * Remove a provider for the given CID
53
57
  */
54
58
  async removeProvider (cid: CID, provider: PeerId, options?: AbortOptions): Promise<void> {
55
- const release = await this.lock.writeLock()
59
+ const release = await this.lock.writeLock(options)
56
60
 
57
61
  try {
58
62
  const key = toProviderKey(this.datastorePrefix, cid, provider)
@@ -67,7 +71,7 @@ export class Providers {
67
71
  * Get a list of providers for the given CID
68
72
  */
69
73
  async getProviders (cid: CID, options?: AbortOptions): Promise<PeerId[]> {
70
- const release = await this.lock.readLock()
74
+ const release = await this.lock.readLock(options)
71
75
 
72
76
  try {
73
77
  this.log.trace('get providers for %c', cid)
@@ -83,9 +87,9 @@ export class Providers {
83
87
  /**
84
88
  * Write a provider into the given store
85
89
  */
86
- private async writeProviderEntry (cid: CID, peerId: PeerId, time: Date = new Date(), options?: AbortOptions): Promise<void> {
90
+ private async writeProviderEntry (cid: CID, peerId: PeerId, options?: WriteProviderEntryOptions): Promise<void> {
87
91
  const key = toProviderKey(this.datastorePrefix, cid, peerId)
88
- const buffer = varint.encode(time.getTime())
92
+ const buffer = varint.encode(options?.time?.getTime() ?? Date.now())
89
93
 
90
94
  await this.datastore.put(key, buffer, options)
91
95
  }
@@ -165,7 +165,9 @@ export class QueryManager implements Startable {
165
165
 
166
166
  log('query:start')
167
167
 
168
- const id = await convertBuffer(key)
168
+ const id = await convertBuffer(key, {
169
+ signal
170
+ })
169
171
  const peers = this.routingTable.closestPeers(id, this.routingTable.kBucketSize)
170
172
 
171
173
  // split peers into d buckets evenly(ish)
@@ -1,8 +1,8 @@
1
+ import { AbortError } from '@libp2p/interface'
1
2
  import { Queue } from '@libp2p/utils/queue'
2
3
  import { pushable } from 'it-pushable'
3
4
  import { xor as uint8ArrayXor } from 'uint8arrays/xor'
4
5
  import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare'
5
- import { QueryAbortedError } from '../errors.js'
6
6
  import { convertPeerId, convertBuffer } from '../utils.js'
7
7
  import { pathEndedEvent, queryErrorEvent } from './events.js'
8
8
  import type { QueryEvent } from '../index.js'
@@ -106,11 +106,13 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
106
106
 
107
107
  signal.addEventListener('abort', () => {
108
108
  queue.abort()
109
- events.end(new QueryAbortedError())
109
+ events.end(new AbortError())
110
110
  })
111
111
 
112
112
  // perform lookups on kadId, not the actual value
113
- const kadId = await convertBuffer(key)
113
+ const kadId = await convertBuffer(key, {
114
+ signal
115
+ })
114
116
 
115
117
  /**
116
118
  * Adds the passed peer to the query queue if it's not us and no other path
@@ -159,7 +161,9 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
159
161
  continue
160
162
  }
161
163
 
162
- const closerPeerKadId = await convertPeerId(closerPeer.id)
164
+ const closerPeerKadId = await convertPeerId(closerPeer.id, {
165
+ signal
166
+ })
163
167
  const closerPeerXor = uint8ArrayXor(closerPeerKadId, kadId)
164
168
 
165
169
  // only continue query if closer peer is actually closer
@@ -206,7 +210,9 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
206
210
  // begin the query with the starting peers
207
211
  await Promise.all(
208
212
  startingPeers.map(async startingPeer => {
209
- queryPeer({ id: startingPeer, multiaddrs: [] }, await convertPeerId(startingPeer))
213
+ queryPeer({ id: startingPeer, multiaddrs: [] }, await convertPeerId(startingPeer, {
214
+ signal
215
+ }))
210
216
  })
211
217
  )
212
218
 
package/src/query-self.ts CHANGED
@@ -133,6 +133,8 @@ export class QuerySelf implements Startable {
133
133
  async (source) => length(source)
134
134
  )
135
135
 
136
+ signal?.throwIfAborted()
137
+
136
138
  const duration = Date.now() - start
137
139
 
138
140
  this.log('self-query found %d peers in %dms', peers, duration)
@@ -4,13 +4,14 @@ import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
4
4
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
5
5
  import type { Validators } from '../index.js'
6
6
  import type { Libp2pRecord } from '@libp2p/record'
7
+ import type { AbortOptions } from 'it-pushable'
7
8
 
8
9
  /**
9
10
  * Checks a record and ensures it is still valid.
10
11
  * It runs the needed validators.
11
12
  * If verification fails the returned Promise will reject with the error.
12
13
  */
13
- export async function verifyRecord (validators: Validators, record: Libp2pRecord): Promise<void> {
14
+ export async function verifyRecord (validators: Validators, record: Libp2pRecord, options?: AbortOptions): Promise<void> {
14
15
  const key = record.key
15
16
  const keyString = uint8ArrayToString(key)
16
17
  const parts = keyString.split('/')
@@ -26,7 +27,7 @@ export async function verifyRecord (validators: Validators, record: Libp2pRecord
26
27
  throw new InvalidParametersError(`No validator available for key type "${parts[1]}"`)
27
28
  }
28
29
 
29
- await validator(key, record.value)
30
+ await validator(key, record.value, options)
30
31
  }
31
32
 
32
33
  /**
@@ -38,7 +39,7 @@ export async function verifyRecord (validators: Validators, record: Libp2pRecord
38
39
  * @param {Uint8Array} key - A valid key is of the form `'/pk/<keymultihash>'`
39
40
  * @param {Uint8Array} publicKey - The public key to validate against (protobuf encoded).
40
41
  */
41
- const validatePublicKeyRecord = async (key: Uint8Array, publicKey: Uint8Array): Promise<void> => {
42
+ const validatePublicKeyRecord = async (key: Uint8Array, publicKey: Uint8Array, options?: AbortOptions): Promise<void> => {
42
43
  if (!(key instanceof Uint8Array)) {
43
44
  throw new InvalidParametersError('"key" must be a Uint8Array')
44
45
  }
package/src/reprovider.ts CHANGED
@@ -2,7 +2,7 @@ import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
2
  import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout'
3
3
  import { Queue } from '@libp2p/utils/queue'
4
4
  import drain from 'it-drain'
5
- import { PROVIDERS_VALIDITY, REPROVIDE_CONCURRENCY, REPROVIDE_INTERVAL, REPROVIDE_MAX_QUEUE_SIZE, REPROVIDE_THRESHOLD } from './constants.js'
5
+ import { PROVIDERS_VALIDITY, REPROVIDE_CONCURRENCY, REPROVIDE_INTERVAL, REPROVIDE_MAX_QUEUE_SIZE, REPROVIDE_THRESHOLD, REPROVIDE_TIMEOUT } from './constants.js'
6
6
  import { parseProviderKey, readProviderTime, timeOperationMethod } from './utils.js'
7
7
  import type { ContentRouting } from './content-routing/index.js'
8
8
  import type { OperationMetrics } from './kad-dht.js'
@@ -103,7 +103,9 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
103
103
  setMaxListeners(Infinity, this.shutdownController.signal)
104
104
 
105
105
  this.timeout = setTimeout(() => {
106
- this.cleanUp().catch(err => {
106
+ this.cleanUp({
107
+ signal: AbortSignal.timeout(REPROVIDE_TIMEOUT)
108
+ }).catch(err => {
107
109
  this.log.error('error running reprovide/cleanup - %e', err)
108
110
  })
109
111
  }, this.interval)
@@ -120,8 +122,8 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
120
122
  * Check all provider records. Delete them if they have expired, reprovide
121
123
  * them if the provider is us and the expiry is within the reprovide window.
122
124
  */
123
- private async cleanUp (): Promise<void> {
124
- const release = await this.lock.writeLock()
125
+ private async cleanUp (options?: AbortOptions): Promise<void> {
126
+ const release = await this.lock.writeLock(options)
125
127
 
126
128
  try {
127
129
  this.safeDispatchEvent('reprovide:start')
@@ -165,7 +167,9 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
165
167
 
166
168
  if (this.running) {
167
169
  this.timeout = setTimeout(() => {
168
- this.cleanUp().catch(err => {
170
+ this.cleanUp({
171
+ signal: AbortSignal.timeout(REPROVIDE_TIMEOUT)
172
+ }).catch(err => {
169
173
  this.log.error('error running re-provide - %e', err)
170
174
  })
171
175
  }, this.interval)
@@ -173,13 +177,13 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
173
177
  }
174
178
  }
175
179
 
176
- private async queueReprovide (cid: CID): Promise<void> {
180
+ private async queueReprovide (cid: CID, options?: AbortOptions): Promise<void> {
177
181
  if (!this.running) {
178
182
  return
179
183
  }
180
184
 
181
185
  this.log.trace('waiting for queue capacity before adding %c to re-provide queue', cid)
182
- await this.reprovideQueue.onSizeLessThan(this.maxQueueSize)
186
+ await this.reprovideQueue.onSizeLessThan(this.maxQueueSize, options)
183
187
 
184
188
  const existingJob = this.reprovideQueue.queue.find(job => job.options.cid.equals(cid))
185
189
 
@@ -139,7 +139,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
139
139
  metricName: `${init.metricsPrefix}_routing_table_ping_new_contact_time_milliseconds`
140
140
  })
141
141
 
142
- this.kb = new KBucket({
142
+ this.kb = new KBucket(components, {
143
143
  kBucketSize: init.kBucketSize,
144
144
  prefixLength: init.prefixLength,
145
145
  splitThreshold: init.splitThreshold,
@@ -148,7 +148,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
148
148
  ping: this.pingOldContacts,
149
149
  verify: this.verifyNewContact,
150
150
  onAdd: this.peerAdded,
151
- onRemove: this.peerRemoved
151
+ onRemove: this.peerRemoved,
152
+ metricsPrefix: init.metricsPrefix
152
153
  })
153
154
 
154
155
  this.closestPeerTagger = new ClosestPeers(this.components, {
@@ -184,7 +185,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
184
185
 
185
186
  this.running = true
186
187
 
187
- await start(this.closestPeerTagger)
188
+ await start(this.closestPeerTagger, this.kb)
188
189
  await this.kb.addSelfPeer(this.components.peerId)
189
190
  }
190
191
 
@@ -232,12 +233,12 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
232
233
 
233
234
  async stop (): Promise<void> {
234
235
  this.running = false
235
- await stop(this.closestPeerTagger)
236
+ await stop(this.closestPeerTagger, this.kb)
236
237
  this.pingOldContactQueue.abort()
237
238
  this.pingNewContactQueue.abort()
238
239
  }
239
240
 
240
- private async peerAdded (peer: Peer, bucket: LeafBucket): Promise<void> {
241
+ private async peerAdded (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void> {
241
242
  if (!this.components.peerId.equals(peer.peerId)) {
242
243
  await this.components.peerStore.merge(peer.peerId, {
243
244
  tags: {
@@ -245,7 +246,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
245
246
  value: this.peerTagValue
246
247
  }
247
248
  }
248
- })
249
+ }, options)
249
250
  }
250
251
 
251
252
  this.updateMetrics()
@@ -253,13 +254,13 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
253
254
  this.safeDispatchEvent('peer:add', { detail: peer.peerId })
254
255
  }
255
256
 
256
- private async peerRemoved (peer: Peer, bucket: LeafBucket): Promise<void> {
257
+ private async peerRemoved (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void> {
257
258
  if (!this.components.peerId.equals(peer.peerId)) {
258
259
  await this.components.peerStore.merge(peer.peerId, {
259
260
  tags: {
260
261
  [this.peerTagName]: undefined
261
262
  }
262
- })
263
+ }, options)
263
264
  }
264
265
 
265
266
  this.updateMetrics()
@@ -408,8 +409,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
408
409
  /**
409
410
  * Find a specific peer by id
410
411
  */
411
- async find (peer: PeerId): Promise<PeerId | undefined> {
412
- const kadId = await utils.convertPeerId(peer)
412
+ async find (peer: PeerId, options?: AbortOptions): Promise<PeerId | undefined> {
413
+ const kadId = await utils.convertPeerId(peer, options)
413
414
  return this.kb.get(kadId)?.peerId
414
415
  }
415
416
 
@@ -451,14 +452,14 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
451
452
  /**
452
453
  * Remove a given peer from the table
453
454
  */
454
- async remove (peer: PeerId): Promise<void> {
455
+ async remove (peer: PeerId, options?: AbortOptions): Promise<void> {
455
456
  if (this.kb == null) {
456
457
  throw new Error('RoutingTable is not started')
457
458
  }
458
459
 
459
- const kadId = await utils.convertPeerId(peer)
460
+ const kadId = await utils.convertPeerId(peer, options)
460
461
 
461
- await this.kb.remove(kadId)
462
+ await this.kb.remove(kadId, options)
462
463
  }
463
464
 
464
465
  private updateMetrics (): void {
@@ -1,4 +1,4 @@
1
- import { PeerMap } from '@libp2p/peer-collections'
1
+ import { PeerMap, trackedPeerMap } from '@libp2p/peer-collections'
2
2
  import map from 'it-map'
3
3
  import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
4
4
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
@@ -6,7 +6,7 @@ import { xor as uint8ArrayXor } from 'uint8arrays/xor'
6
6
  import { PeerDistanceList } from '../peer-distance-list.js'
7
7
  import { convertPeerId } from '../utils.js'
8
8
  import { KBUCKET_SIZE, LAST_PING_THRESHOLD, PING_OLD_CONTACT_COUNT, PREFIX_LENGTH } from './index.js'
9
- import type { PeerId, AbortOptions } from '@libp2p/interface'
9
+ import type { PeerId, AbortOptions, Metrics } from '@libp2p/interface'
10
10
 
11
11
  export interface PingFunction {
12
12
  /**
@@ -28,21 +28,25 @@ export interface OnAddCallback {
28
28
  /**
29
29
  * Invoked when a new peer is added to the routing tables
30
30
  */
31
- (peer: Peer, bucket: LeafBucket): Promise<void>
31
+ (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void>
32
32
  }
33
33
 
34
34
  export interface OnRemoveCallback {
35
35
  /**
36
36
  * Invoked when a peer is evicted from the routing tables
37
37
  */
38
- (peer: Peer, bucket: LeafBucket): Promise<void>
38
+ (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void>
39
39
  }
40
40
 
41
41
  export interface OnMoveCallback {
42
42
  /**
43
43
  * Invoked when a peer is moved between buckets in the routing tables
44
44
  */
45
- (peer: Peer, oldBucket: LeafBucket, newBucket: LeafBucket): Promise<void>
45
+ (peer: Peer, oldBucket: LeafBucket, newBucket: LeafBucket, options?: AbortOptions): Promise<void>
46
+ }
47
+
48
+ export interface KBucketComponents {
49
+ metrics?: Metrics
46
50
  }
47
51
 
48
52
  export interface KBucketOptions {
@@ -97,6 +101,7 @@ export interface KBucketOptions {
97
101
  verify: VerifyFunction
98
102
  onAdd?: OnAddCallback
99
103
  onRemove?: OnRemoveCallback
104
+ metricsPrefix?: string
100
105
  }
101
106
 
102
107
  export interface Peer {
@@ -143,7 +148,7 @@ export class KBucket {
143
148
  private readonly onMove?: OnMoveCallback
144
149
  private readonly addingPeerMap: PeerMap<Promise<void>>
145
150
 
146
- constructor (options: KBucketOptions) {
151
+ constructor (components: KBucketComponents, options: KBucketOptions) {
147
152
  this.prefixLength = options.prefixLength ?? PREFIX_LENGTH
148
153
  this.kBucketSize = options.kBucketSize ?? KBUCKET_SIZE
149
154
  this.splitThreshold = options.splitThreshold ?? this.kBucketSize
@@ -153,7 +158,10 @@ export class KBucket {
153
158
  this.verify = options.verify
154
159
  this.onAdd = options.onAdd
155
160
  this.onRemove = options.onRemove
156
- this.addingPeerMap = new PeerMap()
161
+ this.addingPeerMap = trackedPeerMap({
162
+ name: `${options.metricsPrefix}_adding_peer_map`,
163
+ metrics: components.metrics
164
+ })
157
165
 
158
166
  this.root = {
159
167
  prefix: '',
@@ -162,10 +170,14 @@ export class KBucket {
162
170
  }
163
171
  }
164
172
 
165
- async addSelfPeer (peerId: PeerId): Promise<void> {
173
+ stop (): void {
174
+ this.addingPeerMap.clear()
175
+ }
176
+
177
+ async addSelfPeer (peerId: PeerId, options?: AbortOptions): Promise<void> {
166
178
  this.localPeer = {
167
179
  peerId,
168
- kadId: await convertPeerId(peerId),
180
+ kadId: await convertPeerId(peerId, options),
169
181
  lastPing: Date.now()
170
182
  }
171
183
  }
@@ -176,7 +188,7 @@ export class KBucket {
176
188
  async add (peerId: PeerId, options?: AbortOptions): Promise<void> {
177
189
  const peer = {
178
190
  peerId,
179
- kadId: await convertPeerId(peerId),
191
+ kadId: await convertPeerId(peerId, options),
180
192
  lastPing: 0
181
193
  }
182
194
 
@@ -206,7 +218,7 @@ export class KBucket {
206
218
  // are there too many peers in the bucket and can we make the trie deeper?
207
219
  if (bucket.peers.length === this.splitThreshold && bucket.depth < this.prefixLength) {
208
220
  // split the bucket
209
- await this._split(bucket)
221
+ await this._split(bucket, options)
210
222
 
211
223
  // try again
212
224
  await this._add(peer, options)
@@ -219,7 +231,7 @@ export class KBucket {
219
231
  // we've ping this peer previously, just add them to the bucket
220
232
  if (!needsPing(peer, this.lastPingThreshold)) {
221
233
  bucket.peers.push(peer)
222
- await this.onAdd?.(peer, bucket)
234
+ await this.onAdd?.(peer, bucket, options)
223
235
  return
224
236
  }
225
237
 
@@ -274,7 +286,7 @@ export class KBucket {
274
286
 
275
287
  for await (const toEvict of this.ping(toPing, options)) {
276
288
  evicted = true
277
- await this.remove(toEvict.kadId)
289
+ await this.remove(toEvict.kadId, options)
278
290
  }
279
291
 
280
292
  // did not evict any peers, cannot add new contact
@@ -351,14 +363,14 @@ export class KBucket {
351
363
  *
352
364
  * @param {Uint8Array} kadId - The ID of the contact to remove
353
365
  */
354
- async remove (kadId: Uint8Array): Promise<void> {
366
+ async remove (kadId: Uint8Array, options?: AbortOptions): Promise<void> {
355
367
  const bucket = this._determineBucket(kadId)
356
368
  const index = this._indexOf(bucket, kadId)
357
369
 
358
370
  if (index > -1) {
359
371
  const peer = bucket.peers.splice(index, 1)[0]
360
372
 
361
- await this.onRemove?.(peer, bucket)
373
+ await this.onRemove?.(peer, bucket, options)
362
374
  }
363
375
  }
364
376
 
@@ -439,7 +451,7 @@ export class KBucket {
439
451
  *
440
452
  * @param {any} bucket - bucket for splitting
441
453
  */
442
- private async _split (bucket: LeafBucket): Promise<void> {
454
+ private async _split (bucket: LeafBucket, options?: AbortOptions): Promise<void> {
443
455
  // create child buckets
444
456
  const left: LeafBucket = {
445
457
  prefix: '0',
@@ -458,10 +470,10 @@ export class KBucket {
458
470
 
459
471
  if (bitString[bucket.depth] === '0') {
460
472
  left.peers.push(peer)
461
- await this.onMove?.(peer, bucket, left)
473
+ await this.onMove?.(peer, bucket, left, options)
462
474
  } else {
463
475
  right.peers.push(peer)
464
- await this.onMove?.(peer, bucket, right)
476
+ await this.onMove?.(peer, bucket, right, options)
465
477
  }
466
478
  }
467
479