@libp2p/kad-dht 0.28.6

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 (166) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +105 -0
  3. package/dist/src/constants.d.ts +20 -0
  4. package/dist/src/constants.d.ts.map +1 -0
  5. package/dist/src/constants.js +34 -0
  6. package/dist/src/constants.js.map +1 -0
  7. package/dist/src/content-fetching/index.d.ts +55 -0
  8. package/dist/src/content-fetching/index.d.ts.map +1 -0
  9. package/dist/src/content-fetching/index.js +190 -0
  10. package/dist/src/content-fetching/index.js.map +1 -0
  11. package/dist/src/content-routing/index.d.ts +42 -0
  12. package/dist/src/content-routing/index.d.ts.map +1 -0
  13. package/dist/src/content-routing/index.js +129 -0
  14. package/dist/src/content-routing/index.js.map +1 -0
  15. package/dist/src/dual-kad-dht.d.ts +65 -0
  16. package/dist/src/dual-kad-dht.d.ts.map +1 -0
  17. package/dist/src/dual-kad-dht.js +191 -0
  18. package/dist/src/dual-kad-dht.js.map +1 -0
  19. package/dist/src/index.d.ts +4 -0
  20. package/dist/src/index.d.ts.map +1 -0
  21. package/dist/src/index.js +15 -0
  22. package/dist/src/index.js.map +1 -0
  23. package/dist/src/kad-dht.d.ts +131 -0
  24. package/dist/src/kad-dht.d.ts.map +1 -0
  25. package/dist/src/kad-dht.js +268 -0
  26. package/dist/src/kad-dht.js.map +1 -0
  27. package/dist/src/message/dht.d.ts +297 -0
  28. package/dist/src/message/dht.js +921 -0
  29. package/dist/src/message/index.d.ts +32 -0
  30. package/dist/src/message/index.d.ts.map +1 -0
  31. package/dist/src/message/index.js +81 -0
  32. package/dist/src/message/index.js.map +1 -0
  33. package/dist/src/network.d.ts +60 -0
  34. package/dist/src/network.d.ts.map +1 -0
  35. package/dist/src/network.js +124 -0
  36. package/dist/src/network.js.map +1 -0
  37. package/dist/src/peer-list/index.d.ts +29 -0
  38. package/dist/src/peer-list/index.d.ts.map +1 -0
  39. package/dist/src/peer-list/index.js +44 -0
  40. package/dist/src/peer-list/index.js.map +1 -0
  41. package/dist/src/peer-list/peer-distance-list.d.ts +34 -0
  42. package/dist/src/peer-list/peer-distance-list.d.ts.map +1 -0
  43. package/dist/src/peer-list/peer-distance-list.js +64 -0
  44. package/dist/src/peer-list/peer-distance-list.js.map +1 -0
  45. package/dist/src/peer-routing/index.d.ts +71 -0
  46. package/dist/src/peer-routing/index.d.ts.map +1 -0
  47. package/dist/src/peer-routing/index.js +256 -0
  48. package/dist/src/peer-routing/index.js.map +1 -0
  49. package/dist/src/providers.d.ts +64 -0
  50. package/dist/src/providers.d.ts.map +1 -0
  51. package/dist/src/providers.js +208 -0
  52. package/dist/src/providers.js.map +1 -0
  53. package/dist/src/query/events.d.ts +46 -0
  54. package/dist/src/query/events.d.ts.map +1 -0
  55. package/dist/src/query/events.js +73 -0
  56. package/dist/src/query/events.js.map +1 -0
  57. package/dist/src/query/manager.d.ts +40 -0
  58. package/dist/src/query/manager.d.ts.map +1 -0
  59. package/dist/src/query/manager.js +140 -0
  60. package/dist/src/query/manager.js.map +1 -0
  61. package/dist/src/query/query-path.d.ts +58 -0
  62. package/dist/src/query/query-path.d.ts.map +1 -0
  63. package/dist/src/query/query-path.js +171 -0
  64. package/dist/src/query/query-path.js.map +1 -0
  65. package/dist/src/query/types.d.ts +16 -0
  66. package/dist/src/query/types.d.ts.map +1 -0
  67. package/dist/src/query/types.js +2 -0
  68. package/dist/src/query/types.js.map +1 -0
  69. package/dist/src/query-self.d.ts +31 -0
  70. package/dist/src/query-self.d.ts.map +1 -0
  71. package/dist/src/query-self.js +73 -0
  72. package/dist/src/query-self.js.map +1 -0
  73. package/dist/src/routing-table/generated-prefix-list-browser.d.ts +3 -0
  74. package/dist/src/routing-table/generated-prefix-list-browser.d.ts.map +1 -0
  75. package/dist/src/routing-table/generated-prefix-list-browser.js +1027 -0
  76. package/dist/src/routing-table/generated-prefix-list-browser.js.map +1 -0
  77. package/dist/src/routing-table/generated-prefix-list.d.ts +3 -0
  78. package/dist/src/routing-table/generated-prefix-list.d.ts.map +1 -0
  79. package/dist/src/routing-table/generated-prefix-list.js +4099 -0
  80. package/dist/src/routing-table/generated-prefix-list.js.map +1 -0
  81. package/dist/src/routing-table/index.d.ts +91 -0
  82. package/dist/src/routing-table/index.d.ts.map +1 -0
  83. package/dist/src/routing-table/index.js +183 -0
  84. package/dist/src/routing-table/index.js.map +1 -0
  85. package/dist/src/routing-table/refresh.d.ts +50 -0
  86. package/dist/src/routing-table/refresh.d.ts.map +1 -0
  87. package/dist/src/routing-table/refresh.js +204 -0
  88. package/dist/src/routing-table/refresh.js.map +1 -0
  89. package/dist/src/routing-table/types.d.ts +24 -0
  90. package/dist/src/routing-table/types.d.ts.map +1 -0
  91. package/dist/src/rpc/handlers/add-provider.d.ts +13 -0
  92. package/dist/src/rpc/handlers/add-provider.d.ts.map +1 -0
  93. package/dist/src/rpc/handlers/add-provider.js +42 -0
  94. package/dist/src/rpc/handlers/add-provider.js.map +1 -0
  95. package/dist/src/rpc/handlers/find-node.d.ts +18 -0
  96. package/dist/src/rpc/handlers/find-node.d.ts.map +1 -0
  97. package/dist/src/rpc/handlers/find-node.js +32 -0
  98. package/dist/src/rpc/handlers/find-node.js.map +1 -0
  99. package/dist/src/rpc/handlers/get-providers.d.ts +24 -0
  100. package/dist/src/rpc/handlers/get-providers.d.ts.map +1 -0
  101. package/dist/src/rpc/handlers/get-providers.js +60 -0
  102. package/dist/src/rpc/handlers/get-providers.js.map +1 -0
  103. package/dist/src/rpc/handlers/get-value.d.ts +27 -0
  104. package/dist/src/rpc/handlers/get-value.d.ts.map +1 -0
  105. package/dist/src/rpc/handlers/get-value.js +94 -0
  106. package/dist/src/rpc/handlers/get-value.js.map +1 -0
  107. package/dist/src/rpc/handlers/index.d.ts +13 -0
  108. package/dist/src/rpc/handlers/index.d.ts.map +1 -0
  109. package/dist/src/rpc/handlers/ping.d.ts +7 -0
  110. package/dist/src/rpc/handlers/ping.d.ts.map +1 -0
  111. package/dist/src/rpc/handlers/ping.js +9 -0
  112. package/dist/src/rpc/handlers/ping.js.map +1 -0
  113. package/dist/src/rpc/handlers/put-value.d.ts +18 -0
  114. package/dist/src/rpc/handlers/put-value.d.ts.map +1 -0
  115. package/dist/src/rpc/handlers/put-value.js +35 -0
  116. package/dist/src/rpc/handlers/put-value.js.map +1 -0
  117. package/dist/src/rpc/index.d.ts +38 -0
  118. package/dist/src/rpc/index.d.ts.map +1 -0
  119. package/dist/src/rpc/index.js +75 -0
  120. package/dist/src/rpc/index.js.map +1 -0
  121. package/dist/src/rpc/types.d.ts +6 -0
  122. package/dist/src/rpc/types.d.ts.map +1 -0
  123. package/dist/src/topology-listener.d.ts +33 -0
  124. package/dist/src/topology-listener.d.ts.map +1 -0
  125. package/dist/src/topology-listener.js +50 -0
  126. package/dist/src/topology-listener.js.map +1 -0
  127. package/dist/src/types.d.ts +143 -0
  128. package/dist/src/types.d.ts.map +1 -0
  129. package/dist/src/utils.d.ts +33 -0
  130. package/dist/src/utils.d.ts.map +1 -0
  131. package/dist/src/utils.js +89 -0
  132. package/dist/src/utils.js.map +1 -0
  133. package/package.json +200 -0
  134. package/src/constants.ts +50 -0
  135. package/src/content-fetching/index.ts +276 -0
  136. package/src/content-routing/index.ts +202 -0
  137. package/src/dual-kad-dht.ts +257 -0
  138. package/src/index.ts +21 -0
  139. package/src/kad-dht.ts +396 -0
  140. package/src/message/dht.d.ts +297 -0
  141. package/src/message/dht.js +921 -0
  142. package/src/message/dht.proto +75 -0
  143. package/src/message/index.ts +111 -0
  144. package/src/network.ts +185 -0
  145. package/src/peer-list/index.ts +54 -0
  146. package/src/peer-list/peer-distance-list.ts +93 -0
  147. package/src/peer-routing/index.ts +332 -0
  148. package/src/providers.ts +278 -0
  149. package/src/query/events.ts +126 -0
  150. package/src/query/manager.ts +188 -0
  151. package/src/query/query-path.ts +263 -0
  152. package/src/query/types.ts +22 -0
  153. package/src/query-self.ts +106 -0
  154. package/src/routing-table/generated-prefix-list-browser.ts +1026 -0
  155. package/src/routing-table/generated-prefix-list.ts +4098 -0
  156. package/src/routing-table/index.ts +265 -0
  157. package/src/routing-table/refresh.ts +263 -0
  158. package/src/rpc/handlers/add-provider.ts +63 -0
  159. package/src/rpc/handlers/find-node.ts +57 -0
  160. package/src/rpc/handlers/get-providers.ts +95 -0
  161. package/src/rpc/handlers/get-value.ts +130 -0
  162. package/src/rpc/handlers/ping.ts +13 -0
  163. package/src/rpc/handlers/put-value.ts +58 -0
  164. package/src/rpc/index.ts +118 -0
  165. package/src/topology-listener.ts +78 -0
  166. package/src/utils.ts +108 -0
@@ -0,0 +1,257 @@
1
+ import { logger } from '@libp2p/logger'
2
+ import errCode from 'err-code'
3
+ import merge from 'it-merge'
4
+ import { queryErrorEvent } from './query/events.js'
5
+ import type { KadDHT } from './kad-dht.js'
6
+ import type { DHT, QueryOptions } from '@libp2p/interfaces/dht'
7
+ import { AbortOptions, EventEmitter, CustomEvent } from '@libp2p/interfaces'
8
+ import type { CID } from 'multiformats'
9
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
10
+ import type { PeerDiscoveryEvents } from '@libp2p/interfaces/peer-discovery'
11
+ import type { PeerStore } from '@libp2p/interfaces/peer-store'
12
+
13
+ const log = logger('libp2p:kad-dht')
14
+
15
+ /**
16
+ * A DHT implementation modelled after Kademlia with S/Kademlia modifications.
17
+ * Original implementation in go: https://github.com/libp2p/go-libp2p-kad-dht.
18
+ */
19
+ export class DualKadDHT extends EventEmitter<PeerDiscoveryEvents> implements DHT {
20
+ public wan: KadDHT
21
+ public lan: KadDHT
22
+ public peerId: PeerId
23
+ public peerStore: PeerStore
24
+
25
+ constructor (wan: KadDHT, lan: KadDHT, peerId: PeerId, peerStore: PeerStore) {
26
+ super()
27
+
28
+ this.wan = wan
29
+ this.lan = lan
30
+ this.peerId = peerId
31
+ this.peerStore = peerStore
32
+
33
+ // handle peers being discovered during processing of DHT messages
34
+ this.wan.addEventListener('peer', (evt) => {
35
+ this.dispatchEvent(new CustomEvent('peer', {
36
+ detail: evt.detail
37
+ }))
38
+ })
39
+ this.lan.addEventListener('peer', (evt) => {
40
+ this.dispatchEvent(new CustomEvent('peer', {
41
+ detail: evt.detail
42
+ }))
43
+ })
44
+ }
45
+
46
+ /**
47
+ * Is this DHT running.
48
+ */
49
+ isStarted () {
50
+ return this.wan.isStarted() && this.lan.isStarted()
51
+ }
52
+
53
+ /**
54
+ * If 'server' this node will respond to DHT queries, if 'client' this node will not
55
+ */
56
+ async getMode () {
57
+ return await this.wan.getMode()
58
+ }
59
+
60
+ /**
61
+ * If 'server' this node will respond to DHT queries, if 'client' this node will not
62
+ */
63
+ async setMode (mode: 'client' | 'server') {
64
+ await this.wan.setMode(mode)
65
+ }
66
+
67
+ /**
68
+ * Start listening to incoming connections.
69
+ */
70
+ async start () {
71
+ await Promise.all([
72
+ this.lan.start(),
73
+ this.wan.start()
74
+ ])
75
+ }
76
+
77
+ /**
78
+ * Stop accepting incoming connections and sending outgoing
79
+ * messages.
80
+ */
81
+ async stop () {
82
+ await Promise.all([
83
+ this.lan.stop(),
84
+ this.wan.stop()
85
+ ])
86
+ }
87
+
88
+ /**
89
+ * Store the given key/value pair in the DHT
90
+ */
91
+ async * put (key: Uint8Array, value: Uint8Array, options: QueryOptions = {}) {
92
+ let counterAll = 0
93
+ let counterSuccess = 0
94
+
95
+ for await (const event of merge(
96
+ this.lan.put(key, value, options),
97
+ this.wan.put(key, value, options)
98
+ )) {
99
+ yield event
100
+
101
+ if (event.name === 'SENDING_QUERY' && event.messageName === 'PUT_VALUE') {
102
+ counterAll++
103
+ }
104
+
105
+ if (event.name === 'PEER_RESPONSE' && event.messageName === 'PUT_VALUE') {
106
+ counterSuccess++
107
+ }
108
+ }
109
+
110
+ // Ensure we have a default `minPeers`
111
+ const minPeers = options.minPeers == null ? counterAll ?? 1 : options.minPeers
112
+
113
+ // verify if we were able to put to enough peers
114
+ if (counterSuccess < minPeers) {
115
+ const error = errCode(new Error(`Failed to put value to enough peers: ${counterSuccess}/${minPeers}`), 'ERR_NOT_ENOUGH_PUT_PEERS')
116
+ log.error(error)
117
+ throw error
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Get the value that corresponds to the passed key
123
+ */
124
+ async * get (key: Uint8Array, options: QueryOptions = {}) { // eslint-disable-line require-await
125
+ let queriedPeers = false
126
+ let foundValue = false
127
+
128
+ for await (const event of merge(
129
+ this.lan.get(key, options),
130
+ this.wan.get(key, options)
131
+ )) {
132
+ yield event
133
+
134
+ if (event.name === 'DIALING_PEER') {
135
+ queriedPeers = true
136
+ }
137
+
138
+ if (event.name === 'VALUE') {
139
+ queriedPeers = true
140
+
141
+ if (event.value != null) {
142
+ foundValue = true
143
+ }
144
+ }
145
+
146
+ if (event.name === 'SENDING_QUERY') {
147
+ queriedPeers = true
148
+ }
149
+ }
150
+
151
+ if (!queriedPeers) {
152
+ throw errCode(new Error('No peers found in routing table!'), 'ERR_NO_PEERS_IN_ROUTING_TABLE')
153
+ }
154
+
155
+ if (!foundValue) {
156
+ yield queryErrorEvent({
157
+ from: this.peerId,
158
+ error: errCode(new Error('Not found'), 'ERR_NOT_FOUND')
159
+ })
160
+ }
161
+ }
162
+
163
+ // ----------- Content Routing
164
+
165
+ /**
166
+ * Announce to the network that we can provide given key's value
167
+ */
168
+ async * provide (key: CID, options: AbortOptions = {}) { // eslint-disable-line require-await
169
+ let sent = 0
170
+ let success = 0
171
+ const errors = []
172
+
173
+ const dhts = [this.lan]
174
+
175
+ // only run provide on the wan if we are in server mode
176
+ if ((await this.wan.getMode()) === 'server') {
177
+ dhts.push(this.wan)
178
+ }
179
+
180
+ for await (const event of merge(...dhts.map(dht => dht.provide(key, options)))) {
181
+ yield event
182
+
183
+ if (event.name === 'SENDING_QUERY') {
184
+ sent++
185
+ }
186
+
187
+ if (event.name === 'QUERY_ERROR') {
188
+ errors.push(event.error)
189
+ }
190
+
191
+ if (event.name === 'PEER_RESPONSE' && event.messageName === 'ADD_PROVIDER') {
192
+ log('sent provider record for %s to %p', key, event.from)
193
+ success++
194
+ }
195
+ }
196
+
197
+ if (success === 0) {
198
+ if (errors.length > 0) {
199
+ // if all sends failed, throw an error to inform the caller
200
+ throw errCode(new Error(`Failed to provide to ${errors.length} of ${sent} peers`), 'ERR_PROVIDES_FAILED', { errors })
201
+ }
202
+
203
+ throw errCode(new Error('Failed to provide - no peers found'), 'ERR_PROVIDES_FAILED')
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Search the dht for up to `K` providers of the given CID
209
+ */
210
+ async * findProviders (key: CID, options: QueryOptions = {}) {
211
+ yield * merge(
212
+ this.lan.findProviders(key, options),
213
+ this.wan.findProviders(key, options)
214
+ )
215
+ }
216
+
217
+ // ----------- Peer Routing -----------
218
+
219
+ /**
220
+ * Search for a peer with the given ID
221
+ */
222
+ async * findPeer (id: PeerId, options: QueryOptions = {}) {
223
+ let queriedPeers = false
224
+
225
+ for await (const event of merge(
226
+ this.lan.findPeer(id, options),
227
+ this.wan.findPeer(id, options)
228
+ )) {
229
+ yield event
230
+
231
+ if (event.name === 'SENDING_QUERY' || event.name === 'FINAL_PEER') {
232
+ queriedPeers = true
233
+ }
234
+ }
235
+
236
+ if (!queriedPeers) {
237
+ throw errCode(new Error('Peer lookup failed'), 'ERR_LOOKUP_FAILED')
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Kademlia 'node lookup' operation
243
+ */
244
+ async * getClosestPeers (key: Uint8Array, options: QueryOptions = {}) {
245
+ yield * merge(
246
+ this.lan.getClosestPeers(key, options),
247
+ this.wan.getClosestPeers(key, options)
248
+ )
249
+ }
250
+
251
+ async refreshRoutingTable () {
252
+ await Promise.all([
253
+ this.lan.refreshRoutingTable(),
254
+ this.wan.refreshRoutingTable()
255
+ ])
256
+ }
257
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { KadDHT, KadDHTOptions } from './kad-dht.js'
2
+ import { DualKadDHT } from './dual-kad-dht.js'
3
+ import type { DHT } from '@libp2p/interfaces/dht'
4
+
5
+ export function createKadDHT (opts: KadDHTOptions): DHT {
6
+ return new DualKadDHT(
7
+ new KadDHT({
8
+ ...opts,
9
+ protocol: '/ipfs/kad/1.0.0',
10
+ lan: false
11
+ }),
12
+ new KadDHT({
13
+ ...opts,
14
+ protocol: '/ipfs/lan/kad/1.0.0',
15
+ clientMode: false,
16
+ lan: true
17
+ }),
18
+ opts.peerId,
19
+ opts.peerStore
20
+ )
21
+ }
package/src/kad-dht.ts ADDED
@@ -0,0 +1,396 @@
1
+ import { RoutingTable } from './routing-table/index.js'
2
+ import { RoutingTableRefresh } from './routing-table/refresh.js'
3
+ import { Network } from './network.js'
4
+ import { ContentFetching } from './content-fetching/index.js'
5
+ import { ContentRouting } from './content-routing/index.js'
6
+ import { PeerRouting } from './peer-routing/index.js'
7
+ import { Providers } from './providers.js'
8
+ import { QueryManager } from './query/manager.js'
9
+ import { RPC } from './rpc/index.js'
10
+ import { TopologyListener } from './topology-listener.js'
11
+ import { QuerySelf } from './query-self.js'
12
+ import {
13
+ removePrivateAddresses,
14
+ removePublicAddresses
15
+ } from './utils.js'
16
+ import { Logger, logger } from '@libp2p/logger'
17
+ import type { DHT, QueryOptions, Validators, Selectors } from '@libp2p/interfaces/dht'
18
+ import type { PeerData } from '@libp2p/interfaces/peer-data'
19
+ import { CustomEvent, EventEmitter } from '@libp2p/interfaces'
20
+ import type { Addressable, Dialer } from '@libp2p/interfaces'
21
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
22
+ import type { PeerStore } from '@libp2p/interfaces/peer-store'
23
+ import type { ComponentMetricsTracker } from '@libp2p/interfaces/metrics'
24
+ import type { Datastore } from 'interface-datastore'
25
+ import type { Registrar } from '@libp2p/interfaces/registrar'
26
+ import type { CID } from 'multiformats/cid'
27
+ import type { PeerDiscoveryEvents } from '@libp2p/interfaces/peer-discovery'
28
+
29
+ export interface KadDHTOptions {
30
+ /**
31
+ * libp2p registrar handle protocol
32
+ */
33
+ protocol: string
34
+
35
+ /**
36
+ * k-bucket size (default 20)
37
+ */
38
+ kBucketSize?: number
39
+
40
+ /**
41
+ * If true, the DHT will not respond to queries. This should be true if your node will not be dialable. (default: false)
42
+ */
43
+ clientMode?: boolean
44
+
45
+ /**
46
+ * validators object with namespace as keys and function(key, record, callback)
47
+ */
48
+ validators: Validators
49
+
50
+ /**
51
+ * selectors object with namespace as keys and function(key, records)
52
+ */
53
+ selectors: Selectors
54
+
55
+ /**
56
+ * how often to search the network for peers close to ourselves
57
+ */
58
+ querySelfInterval: number
59
+ lan: boolean
60
+ bootstrapPeers: PeerData[]
61
+ dialer: Dialer
62
+ addressable: Addressable
63
+ peerStore: PeerStore
64
+ peerId: PeerId
65
+ datastore: Datastore
66
+ registrar: Registrar
67
+ metrics?: ComponentMetricsTracker
68
+ }
69
+
70
+ /**
71
+ * A DHT implementation modelled after Kademlia with S/Kademlia modifications.
72
+ * Original implementation in go: https://github.com/libp2p/go-libp2p-kad-dht.
73
+ */
74
+ export class KadDHT extends EventEmitter<PeerDiscoveryEvents> implements DHT {
75
+ private readonly log: Logger
76
+ private running: boolean
77
+ public protocol: string
78
+ private readonly kBucketSize: number
79
+ private clientMode: boolean
80
+ private readonly bootstrapPeers: PeerData[]
81
+ public routingTable: RoutingTable
82
+ public providers: Providers
83
+ private readonly lan: boolean
84
+ private readonly validators: Validators
85
+ private readonly selectors: Selectors
86
+ public network: Network
87
+ private readonly queryManager: QueryManager
88
+ public peerRouting: PeerRouting
89
+ private readonly contentFetching: ContentFetching
90
+ private readonly contentRouting: ContentRouting
91
+ private readonly routingTableRefresh: RoutingTableRefresh
92
+ private readonly rpc: RPC
93
+ private readonly topologyListener: TopologyListener
94
+ private readonly querySelf: QuerySelf
95
+ public addressable: Addressable
96
+ public registrar: Registrar
97
+ private registrarHandleId?: string
98
+
99
+ /**
100
+ * Create a new KadDHT
101
+ */
102
+ constructor (options: KadDHTOptions) {
103
+ super()
104
+
105
+ const {
106
+ kBucketSize,
107
+ clientMode,
108
+ validators,
109
+ selectors,
110
+ querySelfInterval,
111
+ lan,
112
+ protocol,
113
+ bootstrapPeers,
114
+ dialer,
115
+ addressable,
116
+ peerId,
117
+ peerStore,
118
+ metrics,
119
+ datastore,
120
+ registrar
121
+ } = options
122
+
123
+ this.running = false
124
+ this.log = logger(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}`)
125
+ this.protocol = protocol ?? '/ipfs/kad/1.0.0'
126
+ this.kBucketSize = kBucketSize ?? 20
127
+ this.clientMode = clientMode ?? true
128
+ this.bootstrapPeers = bootstrapPeers ?? []
129
+ this.addressable = addressable
130
+ this.registrar = registrar
131
+ this.routingTable = new RoutingTable({
132
+ peerId,
133
+ dialer,
134
+ kBucketSize,
135
+ metrics,
136
+ lan
137
+ })
138
+
139
+ this.providers = new Providers({
140
+ datastore
141
+ })
142
+ this.lan = lan
143
+ this.validators = validators ?? {}
144
+ this.selectors = selectors ?? {}
145
+ this.network = new Network({
146
+ dialer,
147
+ protocol: this.protocol,
148
+ lan,
149
+ peerId
150
+ })
151
+ this.queryManager = new QueryManager({
152
+ peerId: peerId,
153
+ // Number of disjoint query paths to use - This is set to `kBucketSize/2` per the S/Kademlia paper
154
+ disjointPaths: Math.ceil(this.kBucketSize / 2),
155
+ metrics,
156
+ lan
157
+ })
158
+
159
+ // DHT components
160
+ this.peerRouting = new PeerRouting({
161
+ peerId,
162
+ routingTable: this.routingTable,
163
+ peerStore,
164
+ network: this.network,
165
+ validators: this.validators,
166
+ queryManager: this.queryManager,
167
+ lan
168
+ })
169
+ this.contentFetching = new ContentFetching({
170
+ peerId,
171
+ datastore,
172
+ validators: this.validators,
173
+ selectors: this.selectors,
174
+ peerRouting: this.peerRouting,
175
+ queryManager: this.queryManager,
176
+ routingTable: this.routingTable,
177
+ network: this.network,
178
+ lan
179
+ })
180
+ this.contentRouting = new ContentRouting({
181
+ peerId,
182
+ network: this.network,
183
+ peerRouting: this.peerRouting,
184
+ queryManager: this.queryManager,
185
+ routingTable: this.routingTable,
186
+ providers: this.providers,
187
+ peerStore,
188
+ lan
189
+ })
190
+ this.routingTableRefresh = new RoutingTableRefresh({
191
+ peerRouting: this.peerRouting,
192
+ routingTable: this.routingTable,
193
+ lan
194
+ })
195
+ this.rpc = new RPC({
196
+ peerId,
197
+ routingTable: this.routingTable,
198
+ providers: this.providers,
199
+ peerRouting: this.peerRouting,
200
+ datastore,
201
+ validators: this.validators,
202
+ keyBook: peerStore.keyBook,
203
+ addressBook: peerStore.addressBook,
204
+ lan
205
+ })
206
+ this.topologyListener = new TopologyListener({
207
+ registrar,
208
+ protocol: this.protocol,
209
+ lan
210
+ })
211
+ this.querySelf = new QuerySelf({
212
+ peerId,
213
+ peerRouting: this.peerRouting,
214
+ interval: querySelfInterval,
215
+ lan
216
+ })
217
+
218
+ // handle peers being discovered during processing of DHT messages
219
+ this.network.addEventListener('peer', (evt) => {
220
+ const peerData = evt.detail
221
+
222
+ this.onPeerConnect(peerData).catch(err => {
223
+ this.log.error('could not add %p to routing table', peerData.id, err)
224
+ })
225
+
226
+ this.dispatchEvent(new CustomEvent('peer', {
227
+ detail: peerData
228
+ }))
229
+ })
230
+
231
+ // handle peers being discovered via other peer discovery mechanisms
232
+ this.topologyListener.addEventListener('peer', (evt) => {
233
+ const peerId = evt.detail
234
+
235
+ Promise.resolve().then(async () => {
236
+ const multiaddrs = await peerStore.addressBook.get(peerId)
237
+
238
+ const peerData = {
239
+ id: peerId,
240
+ multiaddrs: multiaddrs.map(addr => addr.multiaddr),
241
+ protocols: []
242
+ }
243
+
244
+ await this.onPeerConnect(peerData)
245
+ }).catch(err => {
246
+ this.log.error('could not add %p to routing table', peerId, err)
247
+ })
248
+ })
249
+ }
250
+
251
+ async onPeerConnect (peerData: PeerData) {
252
+ this.log('peer %p connected', peerData.id)
253
+
254
+ if (this.lan) {
255
+ peerData = removePublicAddresses(peerData)
256
+ } else {
257
+ peerData = removePrivateAddresses(peerData)
258
+ }
259
+
260
+ if (peerData.multiaddrs.length === 0) {
261
+ this.log('ignoring %p as they do not have any %s addresses in %s', peerData.id, this.lan ? 'private' : 'public', peerData.multiaddrs.map(addr => addr.toString()))
262
+ return
263
+ }
264
+
265
+ try {
266
+ await this.routingTable.add(peerData.id)
267
+ } catch (err: any) {
268
+ this.log.error('could not add %p to routing table', peerData.id, err)
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Is this DHT running.
274
+ */
275
+ isStarted () {
276
+ return this.running
277
+ }
278
+
279
+ /**
280
+ * If 'server' this node will respond to DHT queries, if 'client' this node will not
281
+ */
282
+ async getMode () {
283
+ return this.clientMode ? 'client' : 'server'
284
+ }
285
+
286
+ /**
287
+ * If 'server' this node will respond to DHT queries, if 'client' this node will not
288
+ */
289
+ async setMode (mode: 'client' | 'server') {
290
+ if (this.registrarHandleId != null) {
291
+ await this.registrar.unhandle(this.registrarHandleId)
292
+ }
293
+
294
+ if (mode === 'client') {
295
+ this.log('enabling client mode')
296
+ this.clientMode = true
297
+ } else {
298
+ this.log('enabling server mode')
299
+ this.clientMode = false
300
+ this.registrarHandleId = await this.registrar.handle(this.protocol, this.rpc.onIncomingStream.bind(this.rpc))
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Start listening to incoming connections.
306
+ */
307
+ async start () {
308
+ this.running = true
309
+
310
+ // Only respond to queries when not in client mode
311
+ await this.setMode(this.clientMode ? 'client' : 'server')
312
+
313
+ await Promise.all([
314
+ this.providers.start(),
315
+ this.queryManager.start(),
316
+ this.network.start(),
317
+ this.routingTable.start(),
318
+ this.topologyListener.start(),
319
+ this.querySelf.start()
320
+ ])
321
+
322
+ await Promise.all(
323
+ this.bootstrapPeers.map(async peerData => await this.routingTable.add(peerData.id))
324
+ )
325
+
326
+ await this.routingTableRefresh.start()
327
+ }
328
+
329
+ /**
330
+ * Stop accepting incoming connections and sending outgoing
331
+ * messages.
332
+ */
333
+ async stop () {
334
+ this.running = false
335
+
336
+ await Promise.all([
337
+ this.providers.stop(),
338
+ this.queryManager.stop(),
339
+ this.network.stop(),
340
+ this.routingTable.stop(),
341
+ this.routingTableRefresh.stop(),
342
+ this.topologyListener.stop(),
343
+ this.querySelf.stop()
344
+ ])
345
+ }
346
+
347
+ /**
348
+ * Store the given key/value pair in the DHT
349
+ */
350
+ async * put (key: Uint8Array, value: Uint8Array, options: QueryOptions = {}) { // eslint-disable-line require-await
351
+ yield * this.contentFetching.put(key, value, options)
352
+ }
353
+
354
+ /**
355
+ * Get the value that corresponds to the passed key
356
+ */
357
+ async * get (key: Uint8Array, options: QueryOptions = {}) { // eslint-disable-line require-await
358
+ yield * this.contentFetching.get(key, options)
359
+ }
360
+
361
+ // ----------- Content Routing
362
+
363
+ /**
364
+ * Announce to the network that we can provide given key's value
365
+ */
366
+ async * provide (key: CID, options: QueryOptions = {}) { // eslint-disable-line require-await
367
+ yield * this.contentRouting.provide(key, this.addressable.multiaddrs, options)
368
+ }
369
+
370
+ /**
371
+ * Search the dht for providers of the given CID
372
+ */
373
+ async * findProviders (key: CID, options: QueryOptions = {}) {
374
+ yield * this.contentRouting.findProviders(key, options)
375
+ }
376
+
377
+ // ----------- Peer Routing -----------
378
+
379
+ /**
380
+ * Search for a peer with the given ID
381
+ */
382
+ async * findPeer (id: PeerId, options: QueryOptions = {}) { // eslint-disable-line require-await
383
+ yield * this.peerRouting.findPeer(id, options)
384
+ }
385
+
386
+ /**
387
+ * Kademlia 'node lookup' operation
388
+ */
389
+ async * getClosestPeers (key: Uint8Array, options: QueryOptions = {}) {
390
+ yield * this.peerRouting.getClosestPeers(key, options)
391
+ }
392
+
393
+ async refreshRoutingTable () {
394
+ await this.routingTableRefresh.refreshTable(true)
395
+ }
396
+ }