@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,75 @@
1
+ syntax = "proto3";
2
+ // can't use, because protocol-buffers doesn't support imports
3
+ // so we have to duplicate for now :(
4
+ // import "record.proto";
5
+
6
+ message Record {
7
+ // adjusted for javascript
8
+ optional bytes key = 1;
9
+ optional bytes value = 2;
10
+ optional bytes author = 3;
11
+ optional bytes signature = 4;
12
+ optional string timeReceived = 5;
13
+ }
14
+
15
+ message Message {
16
+ enum MessageType {
17
+ PUT_VALUE = 0;
18
+ GET_VALUE = 1;
19
+ ADD_PROVIDER = 2;
20
+ GET_PROVIDERS = 3;
21
+ FIND_NODE = 4;
22
+ PING = 5;
23
+ }
24
+
25
+ enum ConnectionType {
26
+ // sender does not have a connection to peer, and no extra information (default)
27
+ NOT_CONNECTED = 0;
28
+
29
+ // sender has a live connection to peer
30
+ CONNECTED = 1;
31
+
32
+ // sender recently connected to peer
33
+ CAN_CONNECT = 2;
34
+
35
+ // sender recently tried to connect to peer repeatedly but failed to connect
36
+ // ("try" here is loose, but this should signal "made strong effort, failed")
37
+ CANNOT_CONNECT = 3;
38
+ }
39
+
40
+ message Peer {
41
+ // ID of a given peer.
42
+ optional bytes id = 1;
43
+
44
+ // multiaddrs for a given peer
45
+ repeated bytes addrs = 2;
46
+
47
+ // used to signal the sender's connection capabilities to the peer
48
+ optional ConnectionType connection = 3;
49
+ }
50
+
51
+ // defines what type of message it is.
52
+ optional MessageType type = 1;
53
+
54
+ // defines what coral cluster level this query/response belongs to.
55
+ // in case we want to implement coral's cluster rings in the future.
56
+ optional int32 clusterLevelRaw = 10;
57
+
58
+ // Used to specify the key associated with this message.
59
+ // PUT_VALUE, GET_VALUE, ADD_PROVIDER, GET_PROVIDERS
60
+ // adjusted for javascript
61
+ optional bytes key = 2;
62
+
63
+ // Used to return a value
64
+ // PUT_VALUE, GET_VALUE
65
+ // adjusted Record to bytes for js
66
+ optional bytes record = 3;
67
+
68
+ // Used to return peers closer to a key in a query
69
+ // GET_VALUE, GET_PROVIDERS, FIND_NODE
70
+ repeated Peer closerPeers = 8;
71
+
72
+ // Used to return Providers
73
+ // GET_VALUE, ADD_PROVIDER, GET_PROVIDERS
74
+ repeated Peer providerPeers = 9;
75
+ }
@@ -0,0 +1,111 @@
1
+ import { peerIdFromBytes } from '@libp2p/peer-id'
2
+ import { Multiaddr } from '@multiformats/multiaddr'
3
+ import { Libp2pRecord } from '@libp2p/record'
4
+ import Proto from './dht.js'
5
+ import type { PeerData } from '@libp2p/interfaces/peer-data'
6
+
7
+ export const MESSAGE_TYPE = Proto.Message.MessageType
8
+ export const CONNECTION_TYPE = Proto.Message.ConnectionType
9
+ export const MESSAGE_TYPE_LOOKUP = Object.keys(MESSAGE_TYPE)
10
+
11
+ type ConnectionType = 0|1|2|3|4
12
+
13
+ interface PBPeer {
14
+ id: Uint8Array
15
+ addrs: Uint8Array[]
16
+ connection: ConnectionType
17
+ }
18
+
19
+ /**
20
+ * Represents a single DHT control message.
21
+ */
22
+ export class Message {
23
+ public type: Proto.Message.MessageType
24
+ public key: Uint8Array
25
+ private clusterLevelRaw: number
26
+ public closerPeers: PeerData[]
27
+ public providerPeers: PeerData[]
28
+ public record?: Libp2pRecord
29
+
30
+ constructor (type: Proto.Message.MessageType, key: Uint8Array, level: number) {
31
+ if (!(key instanceof Uint8Array)) {
32
+ throw new Error('Key must be a Uint8Array')
33
+ }
34
+
35
+ this.type = type
36
+ this.key = key
37
+ this.clusterLevelRaw = level
38
+ this.closerPeers = []
39
+ this.providerPeers = []
40
+ this.record = undefined
41
+ }
42
+
43
+ /**
44
+ * @type {number}
45
+ */
46
+ get clusterLevel () {
47
+ const level = this.clusterLevelRaw - 1
48
+ if (level < 0) {
49
+ return 0
50
+ }
51
+
52
+ return level
53
+ }
54
+
55
+ set clusterLevel (level) {
56
+ this.clusterLevelRaw = level
57
+ }
58
+
59
+ /**
60
+ * Encode into protobuf
61
+ */
62
+ serialize () {
63
+ return Proto.Message.encode({
64
+ key: this.key,
65
+ type: this.type,
66
+ clusterLevelRaw: this.clusterLevelRaw,
67
+ closerPeers: this.closerPeers.map(toPbPeer),
68
+ providerPeers: this.providerPeers.map(toPbPeer),
69
+ record: this.record == null ? undefined : this.record.serialize()
70
+ }).finish()
71
+ }
72
+
73
+ /**
74
+ * Decode from protobuf
75
+ */
76
+ static deserialize (raw: Uint8Array) {
77
+ const dec = Proto.Message.decode(raw)
78
+
79
+ const msg = new Message(dec.type ?? 0, dec.key ?? Uint8Array.from([]), dec.clusterLevelRaw ?? 0)
80
+ msg.closerPeers = dec.closerPeers.map(fromPbPeer)
81
+ msg.providerPeers = dec.providerPeers.map(fromPbPeer)
82
+
83
+ if (dec.record?.length != null) {
84
+ msg.record = Libp2pRecord.deserialize(dec.record)
85
+ }
86
+
87
+ return msg
88
+ }
89
+ }
90
+
91
+ function toPbPeer (peer: PeerData) {
92
+ const output: PBPeer = {
93
+ id: peer.id.toBytes(),
94
+ addrs: (peer.multiaddrs ?? []).map((m) => m.bytes),
95
+ connection: CONNECTION_TYPE.CONNECTED
96
+ }
97
+
98
+ return output
99
+ }
100
+
101
+ function fromPbPeer (peer: Proto.Message.IPeer) {
102
+ if (peer.id == null) {
103
+ throw new Error('Invalid peer in message')
104
+ }
105
+
106
+ return {
107
+ id: peerIdFromBytes(peer.id),
108
+ multiaddrs: (peer.addrs ?? []).map((a) => new Multiaddr(a)),
109
+ protocols: []
110
+ }
111
+ }
package/src/network.ts ADDED
@@ -0,0 +1,185 @@
1
+ import errcode from 'err-code'
2
+ import { pipe } from 'it-pipe'
3
+ import * as lp from 'it-length-prefixed'
4
+ import drain from 'it-drain'
5
+ import first from 'it-first'
6
+ import { Message, MESSAGE_TYPE_LOOKUP } from './message/index.js'
7
+ import { EventEmitter, CustomEvent } from '@libp2p/interfaces'
8
+ import {
9
+ dialingPeerEvent,
10
+ sendingQueryEvent,
11
+ peerResponseEvent,
12
+ queryErrorEvent
13
+ } from './query/events.js'
14
+ import { logger } from '@libp2p/logger'
15
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
16
+ import type { AbortOptions, Dialer, Startable } from '@libp2p/interfaces'
17
+ import type { Logger } from '@libp2p/logger'
18
+ import type { Duplex } from 'it-stream-types'
19
+ import type { PeerData } from '@libp2p/interfaces/peer-data'
20
+
21
+ export interface NetworkOptions {
22
+ dialer: Dialer
23
+ protocol: string
24
+ lan: boolean
25
+ peerId: PeerId
26
+ }
27
+
28
+ interface NetworkEvents {
29
+ 'peer': CustomEvent<PeerData>
30
+ }
31
+
32
+ /**
33
+ * Handle network operations for the dht
34
+ */
35
+ export class Network extends EventEmitter<NetworkEvents> implements Startable {
36
+ private readonly log: Logger
37
+ public dialer: Dialer
38
+ private readonly protocol: string
39
+ private running: boolean
40
+
41
+ /**
42
+ * Create a new network
43
+ */
44
+ constructor (options: NetworkOptions) {
45
+ super()
46
+
47
+ const { dialer, protocol, lan, peerId } = options
48
+ this.log = logger(`libp2p:kad-dht:${lan ? 'lan' : 'wan'}:network:${peerId.toString()}`)
49
+ this.running = false
50
+ this.dialer = dialer
51
+ this.protocol = protocol
52
+ }
53
+
54
+ /**
55
+ * Start the network
56
+ */
57
+ async start () {
58
+ if (this.running) {
59
+ return
60
+ }
61
+
62
+ this.running = true
63
+ }
64
+
65
+ /**
66
+ * Stop all network activity
67
+ */
68
+ async stop () {
69
+ this.running = false
70
+ }
71
+
72
+ /**
73
+ * Is the network online?
74
+ */
75
+ isStarted () {
76
+ return this.running
77
+ }
78
+
79
+ /**
80
+ * Send a request and record RTT for latency measurements
81
+ */
82
+ async * sendRequest (to: PeerId, msg: Message, options: AbortOptions = {}) {
83
+ if (!this.running) {
84
+ return
85
+ }
86
+
87
+ this.log('sending %s to %p', MESSAGE_TYPE_LOOKUP[msg.type], to)
88
+
89
+ try {
90
+ yield dialingPeerEvent({ peer: to })
91
+
92
+ const { stream } = await this.dialer.dialProtocol(to, this.protocol, options)
93
+
94
+ yield sendingQueryEvent({ to, type: msg.type })
95
+
96
+ const response = await this._writeReadMessage(stream, msg.serialize())
97
+
98
+ yield peerResponseEvent({
99
+ from: to,
100
+ messageType: response.type,
101
+ closer: response.closerPeers,
102
+ providers: response.providerPeers,
103
+ record: response.record
104
+ })
105
+ } catch (err: any) {
106
+ yield queryErrorEvent({ from: to, error: err })
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Sends a message without expecting an answer
112
+ */
113
+ async * sendMessage (to: PeerId, msg: Message, options: AbortOptions = {}) {
114
+ if (!this.running) {
115
+ return
116
+ }
117
+
118
+ this.log('sending %s to %p', MESSAGE_TYPE_LOOKUP[msg.type], to)
119
+
120
+ yield dialingPeerEvent({ peer: to })
121
+
122
+ const { stream } = await this.dialer.dialProtocol(to, this.protocol, options)
123
+
124
+ yield sendingQueryEvent({ to, type: msg.type })
125
+
126
+ try {
127
+ await this._writeMessage(stream, msg.serialize())
128
+
129
+ yield peerResponseEvent({ from: to, messageType: msg.type })
130
+ } catch (err: any) {
131
+ yield queryErrorEvent({ from: to, error: err })
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Write a message to the given stream
137
+ */
138
+ async _writeMessage (stream: Duplex<Uint8Array>, msg: Uint8Array) {
139
+ await pipe(
140
+ [msg],
141
+ lp.encode(),
142
+ stream,
143
+ drain
144
+ )
145
+ }
146
+
147
+ /**
148
+ * Write a message and read its response.
149
+ * If no response is received after the specified timeout
150
+ * this will error out.
151
+ */
152
+ async _writeReadMessage (stream: Duplex<Uint8Array>, msg: Uint8Array) {
153
+ const res = await pipe(
154
+ [msg],
155
+ lp.encode(),
156
+ stream,
157
+ lp.decode(),
158
+ async source => {
159
+ const buf = await first(source)
160
+
161
+ if (buf != null) {
162
+ return buf
163
+ }
164
+
165
+ throw errcode(new Error('No message received'), 'ERR_NO_MESSAGE_RECEIVED')
166
+ }
167
+ )
168
+
169
+ const message = Message.deserialize(res)
170
+
171
+ // tell any listeners about new peers we've seen
172
+ message.closerPeers.forEach(peerData => {
173
+ this.dispatchEvent(new CustomEvent('peer', {
174
+ detail: peerData
175
+ }))
176
+ })
177
+ message.providerPeers.forEach(peerData => {
178
+ this.dispatchEvent(new CustomEvent('peer', {
179
+ detail: peerData
180
+ }))
181
+ })
182
+
183
+ return message
184
+ }
185
+ }
@@ -0,0 +1,54 @@
1
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
2
+
3
+ /**
4
+ * A list of unique peers.
5
+ */
6
+ export class PeerList {
7
+ private readonly list: PeerId[]
8
+
9
+ constructor () {
10
+ this.list = []
11
+ }
12
+
13
+ /**
14
+ * Add a new peer. Returns `true` if it was a new one
15
+ */
16
+ push (peerId: PeerId) {
17
+ if (!this.has(peerId)) {
18
+ this.list.push(peerId)
19
+
20
+ return true
21
+ }
22
+
23
+ return false
24
+ }
25
+
26
+ /**
27
+ * Check if this PeerData is already in here
28
+ */
29
+ has (peerId: PeerId) {
30
+ const match = this.list.find((i) => i.equals(peerId))
31
+ return Boolean(match)
32
+ }
33
+
34
+ /**
35
+ * Get the list as an array
36
+ */
37
+ toArray () {
38
+ return this.list.slice()
39
+ }
40
+
41
+ /**
42
+ * Remove the last element
43
+ */
44
+ pop () {
45
+ return this.list.pop()
46
+ }
47
+
48
+ /**
49
+ * The length of the list
50
+ */
51
+ get length () {
52
+ return this.list.length
53
+ }
54
+ }
@@ -0,0 +1,93 @@
1
+ import * as utils from '../utils.js'
2
+ import pMap from 'p-map'
3
+ import { compare as uint8ArrayCompare } from 'uint8arrays/compare'
4
+ import { xor as uint8ArrayXor } from 'uint8arrays/xor'
5
+ import type { PeerId } from '@libp2p/interfaces/peer-id'
6
+
7
+ interface PeerDistance {
8
+ peerId: PeerId
9
+ distance: Uint8Array
10
+ }
11
+
12
+ /**
13
+ * Maintains a list of peerIds sorted by distance from a DHT key.
14
+ */
15
+ export class PeerDistanceList {
16
+ /**
17
+ * The DHT key from which distance is calculated
18
+ */
19
+ private readonly originDhtKey: Uint8Array
20
+
21
+ /**
22
+ * The maximum size of the list
23
+ */
24
+ private readonly capacity: number
25
+
26
+ private peerDistances: PeerDistance[]
27
+
28
+ constructor (originDhtKey: Uint8Array, capacity: number) {
29
+ this.originDhtKey = originDhtKey
30
+ this.capacity = capacity
31
+ this.peerDistances = []
32
+ }
33
+
34
+ /**
35
+ * The length of the list
36
+ */
37
+ get length () {
38
+ return this.peerDistances.length
39
+ }
40
+
41
+ /**
42
+ * The peerIds in the list, in order of distance from the origin key
43
+ */
44
+ get peers () {
45
+ return this.peerDistances.map(pd => pd.peerId)
46
+ }
47
+
48
+ /**
49
+ * Add a peerId to the list.
50
+ */
51
+ async add (peerId: PeerId) {
52
+ if (this.peerDistances.find(pd => pd.peerId.equals(peerId)) != null) {
53
+ return
54
+ }
55
+
56
+ const dhtKey = await utils.convertPeerId(peerId)
57
+ const el = {
58
+ peerId,
59
+ distance: uint8ArrayXor(this.originDhtKey, dhtKey)
60
+ }
61
+
62
+ this.peerDistances.push(el)
63
+ this.peerDistances.sort((a, b) => uint8ArrayCompare(a.distance, b.distance))
64
+ this.peerDistances = this.peerDistances.slice(0, this.capacity)
65
+ }
66
+
67
+ /**
68
+ * Indicates whether any of the peerIds passed as a parameter are closer
69
+ * to the origin key than the furthest peerId in the PeerDistanceList.
70
+ */
71
+ async anyCloser (peerIds: PeerId[]) {
72
+ if (peerIds.length === 0) {
73
+ return false
74
+ }
75
+
76
+ if (this.length === 0) {
77
+ return true
78
+ }
79
+
80
+ const dhtKeys = await pMap(peerIds, async (peerId) => await utils.convertPeerId(peerId))
81
+ const furthestDistance = this.peerDistances[this.peerDistances.length - 1].distance
82
+
83
+ for (const dhtKey of dhtKeys) {
84
+ const keyDistance = uint8ArrayXor(this.originDhtKey, dhtKey)
85
+
86
+ if (uint8ArrayCompare(keyDistance, furthestDistance) < 0) {
87
+ return true
88
+ }
89
+ }
90
+
91
+ return false
92
+ }
93
+ }