@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,95 @@
1
+ import { CID } from 'multiformats/cid'
2
+ import errcode from 'err-code'
3
+ import { Message } from '../../message/index.js'
4
+ import {
5
+ removePrivateAddresses,
6
+ removePublicAddresses
7
+ } from '../../utils.js'
8
+ import { logger } from '@libp2p/logger'
9
+ import type { DHTMessageHandler } from '../index.js'
10
+ import type { Providers } from '../../providers.js'
11
+ import type { AddressBook } from '@libp2p/interfaces/peer-store'
12
+ import type { PeerRouting } from '../../peer-routing/index.js'
13
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
14
+ import type { PeerData } from '@libp2p/interfaces/peer-data'
15
+
16
+ const log = logger('libp2p:kad-dht:rpc:handlers:get-providers')
17
+
18
+ export interface GetProvidersHandlerOptions {
19
+ peerRouting: PeerRouting
20
+ providers: Providers
21
+ addressBook: AddressBook
22
+ lan: boolean
23
+ }
24
+
25
+ export class GetProvidersHandler implements DHTMessageHandler {
26
+ private readonly peerRouting: PeerRouting
27
+ private readonly providers: Providers
28
+ private readonly addressBook: AddressBook
29
+ private readonly lan: boolean
30
+
31
+ constructor (options: GetProvidersHandlerOptions) {
32
+ const { peerRouting, providers, addressBook, lan } = options
33
+
34
+ this.peerRouting = peerRouting
35
+ this.providers = providers
36
+ this.addressBook = addressBook
37
+ this.lan = Boolean(lan)
38
+ }
39
+
40
+ async handle (peerId: PeerId, msg: Message) {
41
+ let cid
42
+ try {
43
+ cid = CID.decode(msg.key)
44
+ } catch (err: any) {
45
+ throw errcode(new Error('Invalid CID'), 'ERR_INVALID_CID')
46
+ }
47
+
48
+ log('%p asking for providers for %s', peerId, cid)
49
+
50
+ const [peers, closer] = await Promise.all([
51
+ this.providers.getProviders(cid),
52
+ this.peerRouting.getCloserPeersOffline(msg.key, peerId)
53
+ ])
54
+
55
+ const providerPeers = await this._getPeers(peers)
56
+ const closerPeers = await this._getPeers(closer.map(({ id }) => id))
57
+ const response = new Message(msg.type, msg.key, msg.clusterLevel)
58
+
59
+ if (providerPeers.length > 0) {
60
+ response.providerPeers = providerPeers
61
+ }
62
+
63
+ if (closerPeers.length > 0) {
64
+ response.closerPeers = closerPeers
65
+ }
66
+
67
+ log('got %s providers %s closerPeers', providerPeers.length, closerPeers.length)
68
+ return response
69
+ }
70
+
71
+ async _getAddresses (peerId: PeerId) {
72
+ const addrs = await this.addressBook.get(peerId)
73
+
74
+ return addrs.map(address => address.multiaddr)
75
+ }
76
+
77
+ async _getPeers (peerIds: PeerId[]) {
78
+ const output: PeerData[] = []
79
+ const addrFilter = this.lan ? removePublicAddresses : removePrivateAddresses
80
+
81
+ for (const peerId of peerIds) {
82
+ const peer = addrFilter({
83
+ id: peerId,
84
+ multiaddrs: await this._getAddresses(peerId),
85
+ protocols: []
86
+ })
87
+
88
+ if (peer.multiaddrs.length > 0) {
89
+ output.push(peer)
90
+ }
91
+ }
92
+
93
+ return output
94
+ }
95
+ }
@@ -0,0 +1,130 @@
1
+ import { Libp2pRecord } from '@libp2p/record'
2
+ import errcode from 'err-code'
3
+ import { Message, MESSAGE_TYPE } from '../../message/index.js'
4
+ import {
5
+ MAX_RECORD_AGE
6
+ } from '../../constants.js'
7
+ import { bufferToRecordKey, isPublicKeyKey, fromPublicKeyKey } from '../../utils.js'
8
+ import { logger } from '@libp2p/logger'
9
+ import type { DHTMessageHandler } from '../index.js'
10
+ import type { Datastore } from 'interface-datastore'
11
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
12
+ import type { KeyBook } from '@libp2p/interfaces/peer-store'
13
+ import type { PeerRouting } from '../../peer-routing/index.js'
14
+
15
+ const log = logger('libp2p:kad-dht:rpc:handlers:get-value')
16
+
17
+ export interface GetValueHandlerOptions {
18
+ keyBook: KeyBook
19
+ peerRouting: PeerRouting
20
+ datastore: Datastore
21
+ }
22
+
23
+ export class GetValueHandler implements DHTMessageHandler {
24
+ private readonly keyBook: KeyBook
25
+ private readonly peerRouting: PeerRouting
26
+ private readonly datastore: Datastore
27
+
28
+ constructor (options: GetValueHandlerOptions) {
29
+ const { keyBook, peerRouting, datastore } = options
30
+
31
+ this.keyBook = keyBook
32
+ this.peerRouting = peerRouting
33
+ this.datastore = datastore
34
+ }
35
+
36
+ async handle (peerId: PeerId, msg: Message) {
37
+ const key = msg.key
38
+
39
+ log('%p asked for key %b', peerId, key)
40
+
41
+ if (key == null || key.length === 0) {
42
+ throw errcode(new Error('Invalid key'), 'ERR_INVALID_KEY')
43
+ }
44
+
45
+ const response = new Message(MESSAGE_TYPE.GET_VALUE, key, msg.clusterLevel)
46
+
47
+ if (isPublicKeyKey(key)) {
48
+ log('is public key')
49
+ const idFromKey = fromPublicKeyKey(key)
50
+ let pubKey: Uint8Array | undefined
51
+
52
+ try {
53
+ const key = await this.keyBook.get(idFromKey)
54
+
55
+ if (key == null) {
56
+ throw errcode(new Error('No public key found in key book'), 'ERR_NOT_FOUND')
57
+ }
58
+
59
+ pubKey = key
60
+ } catch (err: any) {
61
+ if (err.code !== 'ERR_NOT_FOUND') {
62
+ throw err
63
+ }
64
+ }
65
+
66
+ if (pubKey != null) {
67
+ log('returning found public key')
68
+ response.record = new Libp2pRecord(key, pubKey)
69
+ return response
70
+ }
71
+ }
72
+
73
+ const [record, closer] = await Promise.all([
74
+ this._checkLocalDatastore(key),
75
+ this.peerRouting.getCloserPeersOffline(msg.key, peerId)
76
+ ])
77
+
78
+ if (record != null) {
79
+ log('had record for %b in local datastore', key)
80
+ response.record = record
81
+ }
82
+
83
+ if (closer.length > 0) {
84
+ log('had %s closer peers in routing table', closer.length)
85
+ response.closerPeers = closer
86
+ }
87
+
88
+ return response
89
+ }
90
+
91
+ /**
92
+ * Try to fetch a given record by from the local datastore.
93
+ * Returns the record iff it is still valid, meaning
94
+ * - it was either authored by this node, or
95
+ * - it was received less than `MAX_RECORD_AGE` ago.
96
+ */
97
+ async _checkLocalDatastore (key: Uint8Array) {
98
+ log('checkLocalDatastore looking for %b', key)
99
+ const dsKey = bufferToRecordKey(key)
100
+
101
+ // Fetch value from ds
102
+ let rawRecord
103
+ try {
104
+ rawRecord = await this.datastore.get(dsKey)
105
+ } catch (err: any) {
106
+ if (err.code === 'ERR_NOT_FOUND') {
107
+ return undefined
108
+ }
109
+ throw err
110
+ }
111
+
112
+ // Create record from the returned bytes
113
+ const record = Libp2pRecord.deserialize(rawRecord)
114
+
115
+ if (record == null) {
116
+ throw errcode(new Error('Invalid record'), 'ERR_INVALID_RECORD')
117
+ }
118
+
119
+ // Check validity: compare time received with max record age
120
+ if (record.timeReceived == null ||
121
+ Date.now() - record.timeReceived.getTime() > MAX_RECORD_AGE) {
122
+ // If record is bad delete it and return
123
+ await this.datastore.delete(dsKey)
124
+ return undefined
125
+ }
126
+
127
+ // Record is valid
128
+ return record
129
+ }
130
+ }
@@ -0,0 +1,13 @@
1
+ import { logger } from '@libp2p/logger'
2
+ import type { Message } from '../../message/index.js'
3
+ import type { DHTMessageHandler } from '../index.js'
4
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
5
+
6
+ const log = logger('libp2p:kad-dht:rpc:handlers:ping')
7
+
8
+ export class PingHandler implements DHTMessageHandler {
9
+ async handle (peerId: PeerId, msg: Message) {
10
+ log('ping from %p', peerId)
11
+ return msg
12
+ }
13
+ }
@@ -0,0 +1,58 @@
1
+ import { bufferToRecordKey } from '../../utils.js'
2
+ import errcode from 'err-code'
3
+ import { verifyRecord } from '@libp2p/record/validators'
4
+ import { Logger, logger } from '@libp2p/logger'
5
+ import type { DHTMessageHandler } from '../index.js'
6
+ import type { Validators } from '@libp2p/interfaces/dht'
7
+ import type { Datastore } from 'interface-datastore'
8
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
9
+ import type { Message } from '../../message/index.js'
10
+ import { base58btc } from 'multiformats/bases/base58'
11
+
12
+ export interface PutValueHandlerOptions {
13
+ peerId: PeerId
14
+ validators: Validators
15
+ datastore: Datastore
16
+ }
17
+
18
+ export class PutValueHandler implements DHTMessageHandler {
19
+ private readonly validators: Validators
20
+ private readonly datastore: Datastore
21
+ private readonly log: Logger
22
+
23
+ constructor (options: PutValueHandlerOptions) {
24
+ const { validators, datastore, peerId } = options
25
+
26
+ this.log = logger('libp2p:kad-dht:rpc:handlers:put-value:' + peerId.toString())
27
+
28
+ this.validators = validators
29
+ this.datastore = datastore
30
+ }
31
+
32
+ async handle (peerId: PeerId, msg: Message) {
33
+ const key = msg.key
34
+ this.log('%p asked us to store value for key %b', peerId, key)
35
+
36
+ const record = msg.record
37
+
38
+ if (record == null) {
39
+ const errMsg = `Empty record from: ${peerId.toString(base58btc)}`
40
+
41
+ this.log.error(errMsg)
42
+ throw errcode(new Error(errMsg), 'ERR_EMPTY_RECORD')
43
+ }
44
+
45
+ try {
46
+ await verifyRecord(this.validators, record)
47
+
48
+ record.timeReceived = new Date()
49
+ const recordKey = bufferToRecordKey(record.key)
50
+ await this.datastore.put(recordKey, record.serialize())
51
+ this.log('put record for %b into datastore under key %k', key, recordKey)
52
+ } catch (err: any) {
53
+ this.log('did not put record for key %b into datastore %o', key, err)
54
+ }
55
+
56
+ return msg
57
+ }
58
+ }
@@ -0,0 +1,118 @@
1
+ import { pipe } from 'it-pipe'
2
+ import * as lp from 'it-length-prefixed'
3
+ import { Logger, logger } from '@libp2p/logger'
4
+ import type { RoutingTable } from '../routing-table'
5
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
6
+ import { Message, MESSAGE_TYPE, MESSAGE_TYPE_LOOKUP } from '../message/index.js'
7
+ import { AddProviderHandler } from './handlers/add-provider.js'
8
+ import { FindNodeHandler } from './handlers/find-node.js'
9
+ import { GetProvidersHandler } from './handlers/get-providers.js'
10
+ import { GetValueHandler } from './handlers/get-value.js'
11
+ import { PingHandler } from './handlers/ping.js'
12
+ import { PutValueHandler } from './handlers/put-value.js'
13
+ import type { IncomingStreamData } from '@libp2p/interfaces/registrar'
14
+ import type { KeyBook, AddressBook } from '@libp2p/interfaces/peer-store'
15
+ import type { Providers } from '../providers'
16
+ import type { PeerRouting } from '../peer-routing'
17
+ import type { Datastore } from 'interface-datastore'
18
+ import type { Validators } from '@libp2p/interfaces/dht'
19
+
20
+ export interface DHTMessageHandler {
21
+ handle: (peerId: PeerId, msg: Message) => Promise<Message | undefined>
22
+ }
23
+
24
+ export interface RPCOptions {
25
+ peerId: PeerId
26
+ routingTable: RoutingTable
27
+ keyBook: KeyBook
28
+ addressBook: AddressBook
29
+ providers: Providers
30
+ peerRouting: PeerRouting
31
+ datastore: Datastore
32
+ validators: Validators
33
+ lan: boolean
34
+ }
35
+
36
+ export class RPC {
37
+ private readonly handlers: Record<number, DHTMessageHandler>
38
+ private readonly routingTable: RoutingTable
39
+ private readonly log: Logger
40
+
41
+ constructor (options: RPCOptions) {
42
+ const { keyBook, addressBook, providers, peerRouting, datastore, validators, lan, peerId } = options
43
+
44
+ this.log = logger('libp2p:kad-dht:rpc:' + peerId.toString())
45
+
46
+ this.routingTable = options.routingTable
47
+ this.handlers = {
48
+ [MESSAGE_TYPE.GET_VALUE]: new GetValueHandler({ keyBook, peerRouting, datastore }),
49
+ [MESSAGE_TYPE.PUT_VALUE]: new PutValueHandler({ peerId, validators, datastore }),
50
+ [MESSAGE_TYPE.FIND_NODE]: new FindNodeHandler({ peerRouting, lan }),
51
+ [MESSAGE_TYPE.ADD_PROVIDER]: new AddProviderHandler({ providers }),
52
+ [MESSAGE_TYPE.GET_PROVIDERS]: new GetProvidersHandler({ peerRouting, providers, addressBook, lan }),
53
+ [MESSAGE_TYPE.PING]: new PingHandler()
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Process incoming DHT messages
59
+ */
60
+ async handleMessage (peerId: PeerId, msg: Message) {
61
+ try {
62
+ await this.routingTable.add(peerId)
63
+ } catch (err: any) {
64
+ this.log.error('Failed to update the kbucket store', err)
65
+ }
66
+
67
+ // get handler & execute it
68
+ const handler = this.handlers[msg.type]
69
+
70
+ if (handler == null) {
71
+ this.log.error(`no handler found for message type: ${msg.type}`)
72
+ return
73
+ }
74
+
75
+ return await handler.handle(peerId, msg)
76
+ }
77
+
78
+ /**
79
+ * Handle incoming streams on the dht protocol
80
+ */
81
+ onIncomingStream (evt: CustomEvent<IncomingStreamData>) {
82
+ Promise.resolve().then(async () => {
83
+ const { stream, connection } = evt.detail
84
+ const peerId = connection.remotePeer
85
+
86
+ try {
87
+ await this.routingTable.add(peerId)
88
+ } catch (err: any) {
89
+ this.log.error(err)
90
+ }
91
+
92
+ const self = this // eslint-disable-line @typescript-eslint/no-this-alias
93
+
94
+ await pipe(
95
+ stream.source,
96
+ lp.decode(),
97
+ source => (async function * () {
98
+ for await (const msg of source) {
99
+ // handle the message
100
+ const desMessage = Message.deserialize(msg.slice())
101
+ self.log('incoming %s from %p', MESSAGE_TYPE_LOOKUP[desMessage.type], peerId)
102
+ const res = await self.handleMessage(peerId, desMessage)
103
+
104
+ // Not all handlers will return a response
105
+ if (res != null) {
106
+ yield res.serialize()
107
+ }
108
+ }
109
+ })(),
110
+ lp.encode(),
111
+ stream.sink
112
+ )
113
+ })
114
+ .catch(err => {
115
+ this.log.error(err)
116
+ })
117
+ }
118
+ }
@@ -0,0 +1,78 @@
1
+ import { Topology } from '@libp2p/topology'
2
+ import { CustomEvent, EventEmitter } from '@libp2p/interfaces'
3
+ import { logger } from '@libp2p/logger'
4
+ import type { Registrar } from '@libp2p/interfaces/registrar'
5
+ import type { Logger } from '@libp2p/logger'
6
+ import type { Startable } from '@libp2p/interfaces'
7
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
8
+
9
+ export interface TopologyListenerOptions {
10
+ registrar: Registrar
11
+ protocol: string
12
+ lan: boolean
13
+ }
14
+
15
+ export interface TopologyListenerEvents {
16
+ 'peer': CustomEvent<PeerId>
17
+ }
18
+
19
+ /**
20
+ * Receives notifications of new peers joining the network that support the DHT protocol
21
+ */
22
+ export class TopologyListener extends EventEmitter<TopologyListenerEvents> implements Startable {
23
+ private readonly log: Logger
24
+ private readonly registrar: Registrar
25
+ private readonly protocol: string
26
+ private running: boolean
27
+ private registrarId?: string
28
+
29
+ constructor (options: TopologyListenerOptions) {
30
+ super()
31
+
32
+ const { registrar, protocol, lan } = options
33
+
34
+ this.log = logger(`libp2p:kad-dht:topology-listener:${lan ? 'lan' : 'wan'}`)
35
+ this.running = false
36
+ this.registrar = registrar
37
+ this.protocol = protocol
38
+ }
39
+
40
+ isStarted () {
41
+ return this.running
42
+ }
43
+
44
+ /**
45
+ * Start the network
46
+ */
47
+ async start () {
48
+ if (this.running) {
49
+ return
50
+ }
51
+
52
+ this.running = true
53
+
54
+ // register protocol with topology
55
+ const topology = new Topology({
56
+ onConnect: (peerId) => {
57
+ this.log('observed peer %p with protocol %s', this.protocol, peerId)
58
+ this.dispatchEvent(new CustomEvent('peer', {
59
+ detail: peerId
60
+ }))
61
+ }
62
+ })
63
+ this.registrarId = await this.registrar.register(this.protocol, topology)
64
+ }
65
+
66
+ /**
67
+ * Stop all network activity
68
+ */
69
+ stop () {
70
+ this.running = false
71
+
72
+ // unregister protocol and handlers
73
+ if (this.registrarId != null) {
74
+ this.registrar.unregister(this.registrarId)
75
+ this.registrarId = undefined
76
+ }
77
+ }
78
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,108 @@
1
+ import { sha256 } from 'multiformats/hashes/sha2'
2
+ import { Key } from 'interface-datastore/key'
3
+ import { Libp2pRecord } from '@libp2p/record'
4
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
5
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
6
+ import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
7
+ import isPrivateIp from 'private-ip'
8
+ import type { PeerData } from '@libp2p/interfaces/peer-data'
9
+ import { peerIdFromBytes } from '@libp2p/peer-id'
10
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
11
+ import { RECORD_KEY_PREFIX } from './constants.js'
12
+
13
+ // const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
14
+ const PK_PREFIX = uint8ArrayFromString('/pk/')
15
+
16
+ export function removePrivateAddresses (peer: PeerData): PeerData {
17
+ return {
18
+ ...peer,
19
+ multiaddrs: peer.multiaddrs.filter(multiaddr => {
20
+ const [[type, addr]] = multiaddr.stringTuples()
21
+
22
+ if (type !== 4 && type !== 6) {
23
+ return false
24
+ }
25
+
26
+ // @ts-expect-error types are wrong https://github.com/frenchbread/private-ip/issues/18
27
+ return !isPrivateIp(addr)
28
+ })
29
+ }
30
+ }
31
+
32
+ export function removePublicAddresses (peer: PeerData): PeerData {
33
+ return {
34
+ ...peer,
35
+ multiaddrs: peer.multiaddrs.filter(multiaddr => {
36
+ const [[type, addr]] = multiaddr.stringTuples()
37
+
38
+ if (type !== 4 && type !== 6) {
39
+ return false
40
+ }
41
+
42
+ // @ts-expect-error types are wrong https://github.com/frenchbread/private-ip/issues/18
43
+ return isPrivateIp(addr)
44
+ })
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Creates a DHT ID by hashing a given Uint8Array
50
+ */
51
+ export async function convertBuffer (buf: Uint8Array) {
52
+ const multihash = await sha256.digest(buf)
53
+
54
+ return multihash.digest
55
+ }
56
+
57
+ /**
58
+ * Creates a DHT ID by hashing a Peer ID
59
+ */
60
+ export async function convertPeerId (peerId: PeerId) {
61
+ return await convertBuffer(peerId.toBytes())
62
+ }
63
+
64
+ /**
65
+ * Convert a Uint8Array to their SHA2-256 hash
66
+ */
67
+ export function bufferToKey (buf: Uint8Array) {
68
+ return new Key('/' + uint8ArrayToString(buf, 'base32'), false)
69
+ }
70
+
71
+ /**
72
+ * Convert a Uint8Array to their SHA2-256 hash
73
+ */
74
+ export function bufferToRecordKey (buf: Uint8Array) {
75
+ return new Key(`${RECORD_KEY_PREFIX}/${uint8ArrayToString(buf, 'base32')}`, false)
76
+ }
77
+
78
+ /**
79
+ * Generate the key for a public key.
80
+ */
81
+ export function keyForPublicKey (peer: PeerId) {
82
+ return uint8ArrayConcat([
83
+ PK_PREFIX,
84
+ peer.toBytes()
85
+ ])
86
+ }
87
+
88
+ export function isPublicKeyKey (key: Uint8Array) {
89
+ return uint8ArrayToString(key.slice(0, 4)) === '/pk/'
90
+ }
91
+
92
+ export function isIPNSKey (key: Uint8Array) {
93
+ return uint8ArrayToString(key.slice(0, 4)) === '/ipns/'
94
+ }
95
+
96
+ export function fromPublicKeyKey (key: Uint8Array) {
97
+ return peerIdFromBytes(key.slice(4))
98
+ }
99
+
100
+ /**
101
+ * Create a new put record, encodes and signs it if enabled
102
+ */
103
+ export function createPutRecord (key: Uint8Array, value: Uint8Array) {
104
+ const timeReceived = new Date()
105
+ const rec = new Libp2pRecord(key, value, timeReceived)
106
+
107
+ return rec.serialize()
108
+ }