@libp2p/kad-dht 11.0.8 → 12.0.0-4e0135c7d

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 (137) hide show
  1. package/README.md +78 -0
  2. package/dist/index.min.js +23 -23
  3. package/dist/src/constants.d.ts +2 -4
  4. package/dist/src/constants.d.ts.map +1 -1
  5. package/dist/src/constants.js +7 -9
  6. package/dist/src/constants.js.map +1 -1
  7. package/dist/src/content-fetching/index.d.ts +7 -7
  8. package/dist/src/content-fetching/index.d.ts.map +1 -1
  9. package/dist/src/content-fetching/index.js +13 -7
  10. package/dist/src/content-fetching/index.js.map +1 -1
  11. package/dist/src/content-routing/index.d.ts +5 -4
  12. package/dist/src/content-routing/index.d.ts.map +1 -1
  13. package/dist/src/content-routing/index.js +23 -13
  14. package/dist/src/content-routing/index.js.map +1 -1
  15. package/dist/src/index.d.ts +142 -28
  16. package/dist/src/index.d.ts.map +1 -1
  17. package/dist/src/index.js +87 -2
  18. package/dist/src/index.js.map +1 -1
  19. package/dist/src/kad-dht.d.ts +20 -21
  20. package/dist/src/kad-dht.d.ts.map +1 -1
  21. package/dist/src/kad-dht.js +181 -35
  22. package/dist/src/kad-dht.js.map +1 -1
  23. package/dist/src/message/dht.d.ts +35 -35
  24. package/dist/src/message/dht.d.ts.map +1 -1
  25. package/dist/src/message/dht.js +150 -130
  26. package/dist/src/message/dht.js.map +1 -1
  27. package/dist/src/message/utils.d.ts +5 -0
  28. package/dist/src/message/utils.d.ts.map +1 -0
  29. package/dist/src/message/utils.js +20 -0
  30. package/dist/src/message/utils.js.map +1 -0
  31. package/dist/src/network.d.ts +8 -8
  32. package/dist/src/network.d.ts.map +1 -1
  33. package/dist/src/network.js +30 -18
  34. package/dist/src/network.js.map +1 -1
  35. package/dist/src/peer-routing/index.d.ts +6 -6
  36. package/dist/src/peer-routing/index.d.ts.map +1 -1
  37. package/dist/src/peer-routing/index.js +48 -35
  38. package/dist/src/peer-routing/index.js.map +1 -1
  39. package/dist/src/providers.d.ts +7 -0
  40. package/dist/src/providers.d.ts.map +1 -1
  41. package/dist/src/providers.js.map +1 -1
  42. package/dist/src/query/events.d.ts +13 -12
  43. package/dist/src/query/events.d.ts.map +1 -1
  44. package/dist/src/query/events.js +2 -2
  45. package/dist/src/query/events.js.map +1 -1
  46. package/dist/src/query/manager.d.ts +8 -5
  47. package/dist/src/query/manager.d.ts.map +1 -1
  48. package/dist/src/query/manager.js +6 -6
  49. package/dist/src/query/manager.js.map +1 -1
  50. package/dist/src/query/query-path.d.ts +3 -3
  51. package/dist/src/query/query-path.d.ts.map +1 -1
  52. package/dist/src/query-self.d.ts +1 -1
  53. package/dist/src/query-self.d.ts.map +1 -1
  54. package/dist/src/query-self.js +2 -2
  55. package/dist/src/query-self.js.map +1 -1
  56. package/dist/src/routing-table/index.d.ts +5 -6
  57. package/dist/src/routing-table/index.d.ts.map +1 -1
  58. package/dist/src/routing-table/index.js +72 -58
  59. package/dist/src/routing-table/index.js.map +1 -1
  60. package/dist/src/routing-table/refresh.d.ts +1 -1
  61. package/dist/src/routing-table/refresh.d.ts.map +1 -1
  62. package/dist/src/routing-table/refresh.js +2 -2
  63. package/dist/src/routing-table/refresh.js.map +1 -1
  64. package/dist/src/rpc/handlers/add-provider.d.ts +2 -1
  65. package/dist/src/rpc/handlers/add-provider.d.ts.map +1 -1
  66. package/dist/src/rpc/handlers/add-provider.js +8 -6
  67. package/dist/src/rpc/handlers/add-provider.js.map +1 -1
  68. package/dist/src/rpc/handlers/find-node.d.ts +5 -3
  69. package/dist/src/rpc/handlers/find-node.d.ts.map +1 -1
  70. package/dist/src/rpc/handlers/find-node.js +22 -14
  71. package/dist/src/rpc/handlers/find-node.js.map +1 -1
  72. package/dist/src/rpc/handlers/get-providers.d.ts +5 -3
  73. package/dist/src/rpc/handlers/get-providers.d.ts.map +1 -1
  74. package/dist/src/rpc/handlers/get-providers.js +29 -16
  75. package/dist/src/rpc/handlers/get-providers.js.map +1 -1
  76. package/dist/src/rpc/handlers/get-value.d.ts +2 -1
  77. package/dist/src/rpc/handlers/get-value.d.ts.map +1 -1
  78. package/dist/src/rpc/handlers/get-value.js +16 -7
  79. package/dist/src/rpc/handlers/get-value.js.map +1 -1
  80. package/dist/src/rpc/handlers/ping.d.ts +5 -2
  81. package/dist/src/rpc/handlers/ping.d.ts.map +1 -1
  82. package/dist/src/rpc/handlers/ping.js +2 -2
  83. package/dist/src/rpc/handlers/ping.js.map +1 -1
  84. package/dist/src/rpc/handlers/put-value.d.ts +2 -1
  85. package/dist/src/rpc/handlers/put-value.d.ts.map +1 -1
  86. package/dist/src/rpc/handlers/put-value.js +8 -7
  87. package/dist/src/rpc/handlers/put-value.js.map +1 -1
  88. package/dist/src/rpc/index.d.ts +4 -3
  89. package/dist/src/rpc/index.d.ts.map +1 -1
  90. package/dist/src/rpc/index.js +11 -11
  91. package/dist/src/rpc/index.js.map +1 -1
  92. package/dist/src/topology-listener.d.ts +1 -1
  93. package/dist/src/topology-listener.d.ts.map +1 -1
  94. package/dist/src/topology-listener.js +2 -2
  95. package/dist/src/topology-listener.js.map +1 -1
  96. package/dist/src/utils.d.ts +5 -2
  97. package/dist/src/utils.d.ts.map +1 -1
  98. package/dist/src/utils.js +32 -2
  99. package/dist/src/utils.js.map +1 -1
  100. package/package.json +13 -11
  101. package/src/constants.ts +7 -11
  102. package/src/content-fetching/index.ts +21 -14
  103. package/src/content-routing/index.ts +29 -18
  104. package/src/index.ts +148 -32
  105. package/src/kad-dht.ts +225 -56
  106. package/src/message/dht.proto +32 -32
  107. package/src/message/dht.ts +155 -138
  108. package/src/message/utils.ts +25 -0
  109. package/src/network.ts +41 -25
  110. package/src/peer-routing/index.ts +57 -42
  111. package/src/providers.ts +7 -0
  112. package/src/query/events.ts +14 -14
  113. package/src/query/manager.ts +14 -10
  114. package/src/query/query-path.ts +3 -3
  115. package/src/query-self.ts +3 -3
  116. package/src/routing-table/index.ts +86 -64
  117. package/src/routing-table/refresh.ts +4 -4
  118. package/src/rpc/handlers/add-provider.ts +10 -7
  119. package/src/rpc/handlers/find-node.ts +27 -18
  120. package/src/rpc/handlers/get-providers.ts +33 -20
  121. package/src/rpc/handlers/get-value.ts +18 -7
  122. package/src/rpc/handlers/ping.ts +7 -3
  123. package/src/rpc/handlers/put-value.ts +11 -9
  124. package/src/rpc/index.ts +14 -13
  125. package/src/topology-listener.ts +3 -3
  126. package/src/utils.ts +41 -2
  127. package/dist/src/dual-kad-dht.d.ts +0 -69
  128. package/dist/src/dual-kad-dht.d.ts.map +0 -1
  129. package/dist/src/dual-kad-dht.js +0 -304
  130. package/dist/src/dual-kad-dht.js.map +0 -1
  131. package/dist/src/message/index.d.ts +0 -35
  132. package/dist/src/message/index.d.ts.map +0 -1
  133. package/dist/src/message/index.js +0 -92
  134. package/dist/src/message/index.js.map +0 -1
  135. package/dist/typedoc-urls.json +0 -55
  136. package/src/dual-kad-dht.ts +0 -384
  137. package/src/message/index.ts +0 -117
@@ -2,7 +2,7 @@ import { keys } from '@libp2p/crypto'
2
2
  import { CodeError } from '@libp2p/interface'
3
3
  import { peerIdFromKeys } from '@libp2p/peer-id'
4
4
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
5
- import { Message, MESSAGE_TYPE } from '../message/index.js'
5
+ import { MessageType } from '../message/dht.js'
6
6
  import { PeerDistanceList } from '../peer-list/peer-distance-list.js'
7
7
  import {
8
8
  queryErrorEvent,
@@ -13,18 +13,19 @@ import { Libp2pRecord } from '../record/index.js'
13
13
  import { verifyRecord } from '../record/validators.js'
14
14
  import * as utils from '../utils.js'
15
15
  import type { KadDHTComponents, DHTRecord, DialPeerEvent, FinalPeerEvent, QueryEvent, Validators } from '../index.js'
16
+ import type { Message } from '../message/dht.js'
16
17
  import type { Network } from '../network.js'
17
18
  import type { QueryManager, QueryOptions } from '../query/manager.js'
18
19
  import type { QueryFunc } from '../query/types.js'
19
20
  import type { RoutingTable } from '../routing-table/index.js'
20
- import type { AbortOptions, Logger, PeerId, PeerInfo, PeerStore } from '@libp2p/interface'
21
+ import type { Logger, PeerId, PeerInfo, PeerStore, RoutingOptions } from '@libp2p/interface'
21
22
 
22
23
  export interface PeerRoutingInit {
23
24
  routingTable: RoutingTable
24
25
  network: Network
25
26
  validators: Validators
26
27
  queryManager: QueryManager
27
- lan: boolean
28
+ logPrefix: string
28
29
  }
29
30
 
30
31
  export class PeerRouting {
@@ -37,7 +38,7 @@ export class PeerRouting {
37
38
  private readonly peerId: PeerId
38
39
 
39
40
  constructor (components: KadDHTComponents, init: PeerRoutingInit) {
40
- const { routingTable, network, validators, queryManager, lan } = init
41
+ const { routingTable, network, validators, queryManager, logPrefix } = init
41
42
 
42
43
  this.routingTable = routingTable
43
44
  this.network = network
@@ -45,7 +46,7 @@ export class PeerRouting {
45
46
  this.queryManager = queryManager
46
47
  this.peerStore = components.peerStore
47
48
  this.peerId = components.peerId
48
- this.log = components.logger.forComponent(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:peer-routing`)
49
+ this.log = components.logger.forComponent(`${logPrefix}:peer-routing`)
49
50
  }
50
51
 
51
52
  /**
@@ -93,15 +94,19 @@ export class PeerRouting {
93
94
  /**
94
95
  * Get a value via rpc call for the given parameters
95
96
  */
96
- async * _getValueSingle (peer: PeerId, key: Uint8Array, options: AbortOptions = {}): AsyncGenerator<QueryEvent> {
97
- const msg = new Message(MESSAGE_TYPE.GET_VALUE, key, 0)
97
+ async * _getValueSingle (peer: PeerId, key: Uint8Array, options: RoutingOptions = {}): AsyncGenerator<QueryEvent> {
98
+ const msg: Partial<Message> = {
99
+ type: MessageType.GET_VALUE,
100
+ key
101
+ }
102
+
98
103
  yield * this.network.sendRequest(peer, msg, options)
99
104
  }
100
105
 
101
106
  /**
102
107
  * Get the public key directly from a node
103
108
  */
104
- async * getPublicKeyFromNode (peer: PeerId, options: AbortOptions = {}): AsyncGenerator<QueryEvent> {
109
+ async * getPublicKeyFromNode (peer: PeerId, options: RoutingOptions = {}): AsyncGenerator<QueryEvent> {
105
110
  const pkKey = utils.keyForPublicKey(peer)
106
111
 
107
112
  for await (const event of this._getValueSingle(peer, pkKey, options)) {
@@ -129,52 +134,59 @@ export class PeerRouting {
129
134
  /**
130
135
  * Search for a peer with the given ID
131
136
  */
132
- async * findPeer (id: PeerId, options: QueryOptions = {}): AsyncGenerator<FinalPeerEvent | QueryEvent> {
137
+ async * findPeer (id: PeerId, options: RoutingOptions = {}): AsyncGenerator<FinalPeerEvent | QueryEvent> {
133
138
  this.log('findPeer %p', id)
134
139
 
135
- // Try to find locally
136
- const pi = await this.findPeerLocal(id)
137
-
138
- // already got it
139
- if (pi != null) {
140
- this.log('found local')
141
- yield finalPeerEvent({
142
- from: this.peerId,
143
- peer: pi
144
- }, options)
145
- return
140
+ if (options.useCache !== false) {
141
+ // Try to find locally
142
+ const pi = await this.findPeerLocal(id)
143
+
144
+ // already got it
145
+ if (pi != null) {
146
+ this.log('found local')
147
+ yield finalPeerEvent({
148
+ from: this.peerId,
149
+ peer: pi
150
+ }, options)
151
+ return
152
+ }
146
153
  }
147
154
 
148
- const self = this // eslint-disable-line @typescript-eslint/no-this-alias
155
+ let foundPeer = false
149
156
 
150
- const findPeerQuery: QueryFunc = async function * ({ peer, signal }) {
151
- const request = new Message(MESSAGE_TYPE.FIND_NODE, id.toBytes(), 0)
157
+ if (options.useNetwork !== false) {
158
+ const self = this // eslint-disable-line @typescript-eslint/no-this-alias
152
159
 
153
- for await (const event of self.network.sendRequest(peer, request, {
154
- ...options,
155
- signal
156
- })) {
157
- yield event
160
+ const findPeerQuery: QueryFunc = async function * ({ peer, signal }) {
161
+ const request: Partial<Message> = {
162
+ type: MessageType.FIND_NODE,
163
+ key: id.toBytes()
164
+ }
165
+
166
+ for await (const event of self.network.sendRequest(peer, request, {
167
+ ...options,
168
+ signal
169
+ })) {
170
+ yield event
158
171
 
159
- if (event.name === 'PEER_RESPONSE') {
160
- const match = event.closer.find((p) => p.id.equals(id))
172
+ if (event.name === 'PEER_RESPONSE') {
173
+ const match = event.closer.find((p) => p.id.equals(id))
161
174
 
162
- // found the peer
163
- if (match != null) {
164
- yield finalPeerEvent({ from: event.from, peer: match }, options)
175
+ // found the peer
176
+ if (match != null) {
177
+ yield finalPeerEvent({ from: event.from, peer: match }, options)
178
+ }
165
179
  }
166
180
  }
167
181
  }
168
- }
169
182
 
170
- let foundPeer = false
183
+ for await (const event of this.queryManager.run(id.toBytes(), findPeerQuery, options)) {
184
+ if (event.name === 'FINAL_PEER') {
185
+ foundPeer = true
186
+ }
171
187
 
172
- for await (const event of this.queryManager.run(id.toBytes(), findPeerQuery, options)) {
173
- if (event.name === 'FINAL_PEER') {
174
- foundPeer = true
188
+ yield event
175
189
  }
176
-
177
- yield event
178
190
  }
179
191
 
180
192
  if (!foundPeer) {
@@ -197,7 +209,10 @@ export class PeerRouting {
197
209
 
198
210
  const getCloserPeersQuery: QueryFunc = async function * ({ peer, signal }) {
199
211
  self.log('closerPeersSingle %s from %p', uint8ArrayToString(key, 'base32'), peer)
200
- const request = new Message(MESSAGE_TYPE.FIND_NODE, key, 0)
212
+ const request: Partial<Message> = {
213
+ type: MessageType.FIND_NODE,
214
+ key
215
+ }
201
216
 
202
217
  yield * self.network.sendRequest(peer, request, {
203
218
  ...options,
@@ -240,7 +255,7 @@ export class PeerRouting {
240
255
  *
241
256
  * Note: The peerStore is updated with new addresses found for the given peer.
242
257
  */
243
- async * getValueOrPeers (peer: PeerId, key: Uint8Array, options: AbortOptions = {}): AsyncGenerator<DialPeerEvent | QueryEvent> {
258
+ async * getValueOrPeers (peer: PeerId, key: Uint8Array, options: RoutingOptions = {}): AsyncGenerator<DialPeerEvent | QueryEvent> {
244
259
  for await (const event of this._getValueSingle(peer, key, options)) {
245
260
  if (event.name === 'PEER_RESPONSE') {
246
261
  if (event.record != null) {
package/src/providers.ts CHANGED
@@ -15,13 +15,20 @@ import type { Datastore } from 'interface-datastore'
15
15
  import type { CID } from 'multiformats'
16
16
 
17
17
  export interface ProvidersInit {
18
+ /**
19
+ * @default 256
20
+ */
18
21
  cacheSize?: number
19
22
  /**
20
23
  * How often invalid records are cleaned. (in seconds)
24
+ *
25
+ * @default 5400
21
26
  */
22
27
  cleanupInterval?: number
23
28
  /**
24
29
  * How long is a provider valid for. (in seconds)
30
+ *
31
+ * @default 86400
25
32
  */
26
33
  provideValidity?: number
27
34
  }
@@ -1,22 +1,22 @@
1
1
  import { CustomEvent } from '@libp2p/interface'
2
- import { MESSAGE_TYPE_LOOKUP } from '../message/index.js'
3
- import type { SendQueryEvent, PeerResponseEvent, DialPeerEvent, AddPeerEvent, ValueEvent, ProviderEvent, QueryErrorEvent, FinalPeerEvent, QueryOptions } from '../index.js'
4
- import type { Message } from '../message/dht.js'
2
+ import { MessageType } from '../message/dht.js'
3
+ import type { SendQueryEvent, PeerResponseEvent, DialPeerEvent, AddPeerEvent, ValueEvent, ProviderEvent, QueryErrorEvent, FinalPeerEvent } from '../index.js'
5
4
  import type { Libp2pRecord } from '../record/index.js'
6
5
  import type { PeerId, PeerInfo } from '@libp2p/interface'
6
+ import type { ProgressOptions } from 'progress-events'
7
7
 
8
8
  export interface QueryEventFields {
9
9
  to: PeerId
10
- type: Message.MessageType
10
+ type: MessageType
11
11
  }
12
12
 
13
- export function sendQueryEvent (fields: QueryEventFields, options: QueryOptions = {}): SendQueryEvent {
13
+ export function sendQueryEvent (fields: QueryEventFields, options: ProgressOptions = {}): SendQueryEvent {
14
14
  const event: SendQueryEvent = {
15
15
  ...fields,
16
16
  name: 'SEND_QUERY',
17
17
  type: 0,
18
18
  messageName: fields.type,
19
- messageType: MESSAGE_TYPE_LOOKUP.indexOf(fields.type.toString())
19
+ messageType: MessageType[fields.type]
20
20
  }
21
21
 
22
22
  options.onProgress?.(new CustomEvent('kad-dht:query:send-query', { detail: event }))
@@ -26,13 +26,13 @@ export function sendQueryEvent (fields: QueryEventFields, options: QueryOptions
26
26
 
27
27
  export interface PeerResponseEventField {
28
28
  from: PeerId
29
- messageType: Message.MessageType
29
+ messageType: MessageType
30
30
  closer?: PeerInfo[]
31
31
  providers?: PeerInfo[]
32
32
  record?: Libp2pRecord
33
33
  }
34
34
 
35
- export function peerResponseEvent (fields: PeerResponseEventField, options: QueryOptions = {}): PeerResponseEvent {
35
+ export function peerResponseEvent (fields: PeerResponseEventField, options: ProgressOptions = {}): PeerResponseEvent {
36
36
  const event: PeerResponseEvent = {
37
37
  ...fields,
38
38
  name: 'PEER_RESPONSE',
@@ -52,7 +52,7 @@ export interface FinalPeerEventFields {
52
52
  peer: PeerInfo
53
53
  }
54
54
 
55
- export function finalPeerEvent (fields: FinalPeerEventFields, options: QueryOptions = {}): FinalPeerEvent {
55
+ export function finalPeerEvent (fields: FinalPeerEventFields, options: ProgressOptions = {}): FinalPeerEvent {
56
56
  const event: FinalPeerEvent = {
57
57
  ...fields,
58
58
  name: 'FINAL_PEER',
@@ -69,7 +69,7 @@ export interface ErrorEventFields {
69
69
  error: Error
70
70
  }
71
71
 
72
- export function queryErrorEvent (fields: ErrorEventFields, options: QueryOptions = {}): QueryErrorEvent {
72
+ export function queryErrorEvent (fields: ErrorEventFields, options: ProgressOptions = {}): QueryErrorEvent {
73
73
  const event: QueryErrorEvent = {
74
74
  ...fields,
75
75
  name: 'QUERY_ERROR',
@@ -86,7 +86,7 @@ export interface ProviderEventFields {
86
86
  providers: PeerInfo[]
87
87
  }
88
88
 
89
- export function providerEvent (fields: ProviderEventFields, options: QueryOptions = {}): ProviderEvent {
89
+ export function providerEvent (fields: ProviderEventFields, options: ProgressOptions = {}): ProviderEvent {
90
90
  const event: ProviderEvent = {
91
91
  ...fields,
92
92
  name: 'PROVIDER',
@@ -103,7 +103,7 @@ export interface ValueEventFields {
103
103
  value: Uint8Array
104
104
  }
105
105
 
106
- export function valueEvent (fields: ValueEventFields, options: QueryOptions = {}): ValueEvent {
106
+ export function valueEvent (fields: ValueEventFields, options: ProgressOptions = {}): ValueEvent {
107
107
  const event: ValueEvent = {
108
108
  ...fields,
109
109
  name: 'VALUE',
@@ -119,7 +119,7 @@ export interface PeerEventFields {
119
119
  peer: PeerId
120
120
  }
121
121
 
122
- export function addPeerEvent (fields: PeerEventFields, options: QueryOptions = {}): AddPeerEvent {
122
+ export function addPeerEvent (fields: PeerEventFields, options: ProgressOptions = {}): AddPeerEvent {
123
123
  const event: AddPeerEvent = {
124
124
  ...fields,
125
125
  name: 'ADD_PEER',
@@ -135,7 +135,7 @@ export interface DialPeerEventFields {
135
135
  peer: PeerId
136
136
  }
137
137
 
138
- export function dialPeerEvent (fields: DialPeerEventFields, options: QueryOptions = {}): DialPeerEvent {
138
+ export function dialPeerEvent (fields: DialPeerEventFields, options: ProgressOptions = {}): DialPeerEvent {
139
139
  const event: DialPeerEvent = {
140
140
  ...fields,
141
141
  name: 'DIAL_PEER',
@@ -9,9 +9,9 @@ import {
9
9
  import { convertBuffer } from '../utils.js'
10
10
  import { queryPath } from './query-path.js'
11
11
  import type { QueryFunc } from './types.js'
12
- import type { QueryEvent, QueryOptions as RootQueryOptions } from '../index.js'
12
+ import type { QueryEvent } from '../index.js'
13
13
  import type { RoutingTable } from '../routing-table/index.js'
14
- import type { ComponentLogger, Metric, Metrics, PeerId, Startable } from '@libp2p/interface'
14
+ import type { ComponentLogger, Metric, Metrics, PeerId, RoutingOptions, Startable } from '@libp2p/interface'
15
15
  import type { DeferredPromise } from 'p-defer'
16
16
 
17
17
  export interface CleanUpEvents {
@@ -19,7 +19,7 @@ export interface CleanUpEvents {
19
19
  }
20
20
 
21
21
  export interface QueryManagerInit {
22
- lan?: boolean
22
+ logPrefix: string
23
23
  disjointPaths?: number
24
24
  alpha?: number
25
25
  initialQuerySelfHasRun: DeferredPromise<void>
@@ -32,8 +32,12 @@ export interface QueryManagerComponents {
32
32
  logger: ComponentLogger
33
33
  }
34
34
 
35
- export interface QueryOptions extends RootQueryOptions {
35
+ export interface QueryOptions extends RoutingOptions {
36
+ /**
37
+ * A timeout for subqueries executed as part of the main query
38
+ */
36
39
  queryFuncTimeout?: number
40
+
37
41
  isSelfQuery?: boolean
38
42
  }
39
43
 
@@ -41,7 +45,6 @@ export interface QueryOptions extends RootQueryOptions {
41
45
  * Keeps track of all running queries
42
46
  */
43
47
  export class QueryManager implements Startable {
44
- private readonly lan: boolean
45
48
  public disjointPaths: number
46
49
  private readonly alpha: number
47
50
  private readonly shutDownController: AbortController
@@ -51,18 +54,19 @@ export class QueryManager implements Startable {
51
54
  private readonly peerId: PeerId
52
55
  private readonly routingTable: RoutingTable
53
56
  private initialQuerySelfHasRun?: DeferredPromise<void>
57
+ private readonly logPrefix: string
54
58
  private readonly metrics?: {
55
59
  runningQueries: Metric
56
60
  queryTime: Metric
57
61
  }
58
62
 
59
63
  constructor (components: QueryManagerComponents, init: QueryManagerInit) {
60
- const { lan = false, disjointPaths = K, alpha = ALPHA } = init
64
+ const { disjointPaths = K, alpha = ALPHA, logPrefix } = init
61
65
 
66
+ this.logPrefix = logPrefix
62
67
  this.disjointPaths = disjointPaths ?? K
63
68
  this.running = false
64
69
  this.alpha = alpha ?? ALPHA
65
- this.lan = lan
66
70
  this.queries = 0
67
71
  this.initialQuerySelfHasRun = init.initialQuerySelfHasRun
68
72
  this.routingTable = init.routingTable
@@ -71,8 +75,8 @@ export class QueryManager implements Startable {
71
75
 
72
76
  if (components.metrics != null) {
73
77
  this.metrics = {
74
- runningQueries: components.metrics.registerMetric(`libp2p_kad_dht_${this.lan ? 'lan' : 'wan'}_running_queries`),
75
- queryTime: components.metrics.registerMetric(`libp2p_kad_dht_${this.lan ? 'lan' : 'wan'}_query_time_seconds`)
78
+ runningQueries: components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_running_queries`),
79
+ queryTime: components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_query_time_seconds`)
76
80
  }
77
81
  }
78
82
 
@@ -129,7 +133,7 @@ export class QueryManager implements Startable {
129
133
  // so make sure we don't make a lot of noise in the logs
130
134
  setMaxListeners(Infinity, signal)
131
135
 
132
- const log = this.logger.forComponent(`libp2p:kad-dht:${this.lan ? 'lan' : 'wan'}:query:` + uint8ArrayToString(key, 'base58btc'))
136
+ const log = this.logger.forComponent(`${this.logPrefix}:query:` + uint8ArrayToString(key, 'base58btc'))
133
137
 
134
138
  // query a subset of peers up to `kBucketSize / 2` in length
135
139
  const startTime = Date.now()
@@ -6,14 +6,14 @@ import { convertPeerId, convertBuffer } from '../utils.js'
6
6
  import { queryErrorEvent } from './events.js'
7
7
  import { queueToGenerator } from './utils.js'
8
8
  import type { CleanUpEvents } from './manager.js'
9
- import type { QueryEvent, QueryOptions } from '../index.js'
9
+ import type { QueryEvent } from '../index.js'
10
10
  import type { QueryFunc } from '../query/types.js'
11
- import type { Logger, TypedEventTarget, PeerId } from '@libp2p/interface'
11
+ import type { Logger, TypedEventTarget, PeerId, RoutingOptions } from '@libp2p/interface'
12
12
  import type { PeerSet } from '@libp2p/peer-collections'
13
13
 
14
14
  const MAX_XOR = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')
15
15
 
16
- export interface QueryPathOptions extends QueryOptions {
16
+ export interface QueryPathOptions extends RoutingOptions {
17
17
  /**
18
18
  * What are we trying to find
19
19
  */
package/src/query-self.ts CHANGED
@@ -12,7 +12,7 @@ import type { ComponentLogger, Logger, PeerId, Startable } from '@libp2p/interfa
12
12
  import type { DeferredPromise } from 'p-defer'
13
13
 
14
14
  export interface QuerySelfInit {
15
- lan: boolean
15
+ logPrefix: string
16
16
  peerRouting: PeerRouting
17
17
  routingTable: RoutingTable
18
18
  count?: number
@@ -46,10 +46,10 @@ export class QuerySelf implements Startable {
46
46
  private querySelfPromise?: DeferredPromise<void>
47
47
 
48
48
  constructor (components: QuerySelfComponents, init: QuerySelfInit) {
49
- const { peerRouting, lan, count, interval, queryTimeout, routingTable } = init
49
+ const { peerRouting, logPrefix, count, interval, queryTimeout, routingTable } = init
50
50
 
51
51
  this.peerId = components.peerId
52
- this.log = components.logger.forComponent(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:query-self`)
52
+ this.log = components.logger.forComponent(`${logPrefix}:query-self`)
53
53
  this.started = false
54
54
  this.peerRouting = peerRouting
55
55
  this.routingTable = routingTable
@@ -1,9 +1,11 @@
1
- import { TypedEventEmitter } from '@libp2p/interface'
1
+ import { CodeError, TypedEventEmitter } from '@libp2p/interface'
2
2
  import { PeerSet } from '@libp2p/peer-collections'
3
- import Queue from 'p-queue'
3
+ import { PeerJobQueue } from '@libp2p/utils/peer-job-queue'
4
+ import { pbStream } from 'it-protobuf-stream'
5
+ import { Message, MessageType } from '../message/dht.js'
4
6
  import * as utils from '../utils.js'
5
7
  import { KBucket, type PingEventDetails } from './k-bucket.js'
6
- import type { ComponentLogger, Logger, Metric, Metrics, PeerId, PeerStore, Startable } from '@libp2p/interface'
8
+ import type { ComponentLogger, Logger, Metric, Metrics, PeerId, PeerStore, Startable, Stream } from '@libp2p/interface'
7
9
  import type { ConnectionManager } from '@libp2p/interface-internal'
8
10
 
9
11
  export const KAD_CLOSE_TAG_NAME = 'kad-close'
@@ -13,7 +15,7 @@ export const PING_TIMEOUT = 10000
13
15
  export const PING_CONCURRENCY = 10
14
16
 
15
17
  export interface RoutingTableInit {
16
- lan: boolean
18
+ logPrefix: string
17
19
  protocol: string
18
20
  kBucketSize?: number
19
21
  pingTimeout?: number
@@ -42,18 +44,17 @@ export interface RoutingTableEvents {
42
44
  export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implements Startable {
43
45
  public kBucketSize: number
44
46
  public kb?: KBucket
45
- public pingQueue: Queue
47
+ public pingQueue: PeerJobQueue
46
48
 
47
49
  private readonly log: Logger
48
50
  private readonly components: RoutingTableComponents
49
- private readonly lan: boolean
50
51
  private readonly pingTimeout: number
51
52
  private readonly pingConcurrency: number
52
53
  private running: boolean
53
54
  private readonly protocol: string
54
55
  private readonly tagName: string
55
56
  private readonly tagValue: number
56
- private metrics?: {
57
+ private readonly metrics?: {
57
58
  routingTableSize: Metric
58
59
  pingQueueSize: Metric
59
60
  pingRunning: Metric
@@ -62,14 +63,13 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
62
63
  constructor (components: RoutingTableComponents, init: RoutingTableInit) {
63
64
  super()
64
65
 
65
- const { kBucketSize, pingTimeout, lan, pingConcurrency, protocol, tagName, tagValue } = init
66
+ const { kBucketSize, pingTimeout, logPrefix, pingConcurrency, protocol, tagName, tagValue } = init
66
67
 
67
68
  this.components = components
68
- this.log = components.logger.forComponent(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:routing-table`)
69
+ this.log = components.logger.forComponent(`${logPrefix}:routing-table`)
69
70
  this.kBucketSize = kBucketSize ?? KBUCKET_SIZE
70
71
  this.pingTimeout = pingTimeout ?? PING_TIMEOUT
71
72
  this.pingConcurrency = pingConcurrency ?? PING_CONCURRENCY
72
- this.lan = lan
73
73
  this.running = false
74
74
  this.protocol = protocol
75
75
  this.tagName = tagName ?? KAD_CLOSE_TAG_NAME
@@ -80,11 +80,20 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
80
80
  this.metrics?.pingRunning.update(this.pingQueue.pending)
81
81
  }
82
82
 
83
- this.pingQueue = new Queue({ concurrency: this.pingConcurrency })
83
+ this.pingQueue = new PeerJobQueue({ concurrency: this.pingConcurrency })
84
84
  this.pingQueue.addListener('add', updatePingQueueSizeMetric)
85
85
  this.pingQueue.addListener('next', updatePingQueueSizeMetric)
86
+ this.pingQueue.addListener('error', err => {
87
+ this.log.error('error pinging peer', err)
88
+ })
86
89
 
87
- this._onPing = this._onPing.bind(this)
90
+ if (this.components.metrics != null) {
91
+ this.metrics = {
92
+ routingTableSize: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_routing_table_size`),
93
+ pingQueueSize: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_ping_queue_size`),
94
+ pingRunning: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_ping_running`)
95
+ }
96
+ }
88
97
  }
89
98
 
90
99
  isStarted (): boolean {
@@ -94,14 +103,6 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
94
103
  async start (): Promise<void> {
95
104
  this.running = true
96
105
 
97
- if (this.components.metrics != null) {
98
- this.metrics = {
99
- routingTableSize: this.components.metrics.registerMetric(`libp2p_kad_dht_${this.lan ? 'lan' : 'wan'}_routing_table_size`),
100
- pingQueueSize: this.components.metrics.registerMetric(`libp2p_kad_dht_${this.lan ? 'lan' : 'wan'}_ping_queue_size`),
101
- pingRunning: this.components.metrics.registerMetric(`libp2p_kad_dht_${this.lan ? 'lan' : 'wan'}_ping_running`)
102
- }
103
- }
104
-
105
106
  const kBuck = new KBucket({
106
107
  localNodeId: await utils.convertPeerId(this.components.peerId),
107
108
  numberOfNodesPerKBucket: this.kBucketSize,
@@ -110,7 +111,11 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
110
111
  this.kb = kBuck
111
112
 
112
113
  // test whether to evict peers
113
- kBuck.addEventListener('ping', this._onPing)
114
+ kBuck.addEventListener('ping', (evt) => {
115
+ this._onPing(evt).catch(err => {
116
+ this.log.error('could not process k-bucket ping event', err)
117
+ })
118
+ })
114
119
 
115
120
  // tag kad-close peers
116
121
  this._tagPeers(kBuck)
@@ -186,60 +191,77 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
186
191
  * `oldContacts` will not be empty and is the list of contacts that
187
192
  * have not been contacted for the longest.
188
193
  */
189
- _onPing (evt: CustomEvent<PingEventDetails>): void {
194
+ async _onPing (evt: CustomEvent<PingEventDetails>): Promise<void> {
195
+ if (!this.running) {
196
+ return
197
+ }
198
+
190
199
  const {
191
200
  oldContacts,
192
201
  newContact
193
202
  } = evt.detail
194
203
 
195
- // add to a queue so multiple ping requests do not overlap and we don't
196
- // flood the network with ping requests if lots of newContact requests
197
- // are received
198
- this.pingQueue.add(async () => {
199
- if (!this.running) {
200
- return
201
- }
204
+ const results = await Promise.all(
205
+ oldContacts.map(async oldContact => {
206
+ // if a previous ping wants us to ping this contact, re-use the result
207
+ if (this.pingQueue.hasJob(oldContact.peer)) {
208
+ return this.pingQueue.joinJob(oldContact.peer)
209
+ }
202
210
 
203
- let responded = 0
211
+ return this.pingQueue.add(async () => {
212
+ let stream: Stream | undefined
204
213
 
205
- try {
206
- await Promise.all(
207
- oldContacts.map(async oldContact => {
208
- try {
209
- const options = {
210
- signal: AbortSignal.timeout(this.pingTimeout)
211
- }
214
+ try {
215
+ const options = {
216
+ signal: AbortSignal.timeout(this.pingTimeout)
217
+ }
212
218
 
213
- this.log('pinging old contact %p', oldContact.peer)
214
- const connection = await this.components.connectionManager.openConnection(oldContact.peer, options)
215
- const stream = await connection.newStream(this.protocol, options)
216
- await stream.close()
217
- responded++
218
- } catch (err: any) {
219
- if (this.running && this.kb != null) {
220
- // only evict peers if we are still running, otherwise we evict when dialing is
221
- // cancelled due to shutdown in progress
222
- this.log.error('could not ping peer %p', oldContact.peer, err)
223
- this.log('evicting old contact after ping failed %p', oldContact.peer)
224
- this.kb.remove(oldContact.id)
225
- }
226
- } finally {
227
- this.metrics?.routingTableSize.update(this.size)
219
+ this.log('pinging old contact %p', oldContact.peer)
220
+ const connection = await this.components.connectionManager.openConnection(oldContact.peer, options)
221
+ stream = await connection.newStream(this.protocol, options)
222
+
223
+ const pb = pbStream(stream)
224
+ await pb.write({
225
+ type: MessageType.PING
226
+ }, Message, options)
227
+ const response = await pb.read(Message, options)
228
+
229
+ await pb.unwrap().close()
230
+
231
+ if (response.type !== MessageType.PING) {
232
+ throw new CodeError(`Incorrect message type received, expected PING got ${response.type}`, 'ERR_BAD_PING_RESPONSE')
228
233
  }
229
- })
230
- )
231
234
 
232
- if (this.running && responded < oldContacts.length && this.kb != null) {
233
- this.log('adding new contact %p', newContact.peer)
234
- this.kb.add(newContact)
235
- }
236
- } catch (err: any) {
237
- this.log.error('could not process k-bucket ping event', err)
238
- }
239
- })
240
- .catch(err => {
241
- this.log.error('could not process k-bucket ping event', err)
235
+ return true
236
+ } catch (err: any) {
237
+ if (this.running && this.kb != null) {
238
+ // only evict peers if we are still running, otherwise we evict
239
+ // when dialing is cancelled due to shutdown in progress
240
+ this.log.error('could not ping peer %p', oldContact.peer, err)
241
+ this.log('evicting old contact after ping failed %p', oldContact.peer)
242
+ this.kb.remove(oldContact.id)
243
+ }
244
+
245
+ stream?.abort(err)
246
+
247
+ return false
248
+ } finally {
249
+ this.metrics?.routingTableSize.update(this.size)
250
+ }
251
+ }, {
252
+ peerId: oldContact.peer
253
+ })
242
254
  })
255
+ )
256
+
257
+ const responded = results
258
+ .filter(res => res)
259
+ .length
260
+
261
+ if (this.running && responded < oldContacts.length && this.kb != null) {
262
+ this.log('adding new contact %p', newContact.peer)
263
+ this.kb.add(newContact)
264
+ }
243
265
  }
244
266
 
245
267
  // -- Public Interface