@libp2p/kad-dht 15.1.2 → 15.1.3-6c42ea64a
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.
- package/dist/index.min.js +2 -2
- package/dist/index.min.js.map +4 -4
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +4 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/content-fetching/index.d.ts +2 -2
- package/dist/src/content-fetching/index.d.ts.map +1 -1
- package/dist/src/content-fetching/index.js +6 -6
- package/dist/src/content-fetching/index.js.map +1 -1
- package/dist/src/content-routing/index.js +3 -3
- package/dist/src/content-routing/index.js.map +1 -1
- package/dist/src/errors.d.ts +0 -6
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +0 -9
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +24 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/kad-dht.d.ts +8 -5
- package/dist/src/kad-dht.d.ts.map +1 -1
- package/dist/src/kad-dht.js +25 -18
- package/dist/src/kad-dht.js.map +1 -1
- package/dist/src/network.d.ts +1 -1
- package/dist/src/network.d.ts.map +1 -1
- package/dist/src/network.js +2 -1
- package/dist/src/network.js.map +1 -1
- package/dist/src/peer-distance-list.d.ts +4 -4
- package/dist/src/peer-distance-list.d.ts.map +1 -1
- package/dist/src/peer-distance-list.js +6 -6
- package/dist/src/peer-distance-list.js.map +1 -1
- package/dist/src/peer-routing/index.d.ts +6 -5
- package/dist/src/peer-routing/index.d.ts.map +1 -1
- package/dist/src/peer-routing/index.js +21 -32
- package/dist/src/peer-routing/index.js.map +1 -1
- package/dist/src/providers.d.ts +0 -3
- package/dist/src/providers.d.ts.map +1 -1
- package/dist/src/providers.js +11 -31
- package/dist/src/providers.js.map +1 -1
- package/dist/src/query/manager.d.ts.map +1 -1
- package/dist/src/query/manager.js +7 -3
- package/dist/src/query/manager.js.map +1 -1
- package/dist/src/query/query-path.d.ts.map +1 -1
- package/dist/src/query/query-path.js +11 -5
- package/dist/src/query/query-path.js.map +1 -1
- package/dist/src/query-self.d.ts.map +1 -1
- package/dist/src/query-self.js +2 -1
- package/dist/src/query-self.js.map +1 -1
- package/dist/src/record/validators.d.ts +2 -1
- package/dist/src/record/validators.d.ts.map +1 -1
- package/dist/src/record/validators.js +3 -3
- package/dist/src/record/validators.js.map +1 -1
- package/dist/src/reprovider.d.ts +1 -4
- package/dist/src/reprovider.d.ts.map +1 -1
- package/dist/src/reprovider.js +13 -13
- package/dist/src/reprovider.js.map +1 -1
- package/dist/src/routing-table/index.d.ts +6 -5
- package/dist/src/routing-table/index.d.ts.map +1 -1
- package/dist/src/routing-table/index.js +72 -44
- package/dist/src/routing-table/index.js.map +1 -1
- package/dist/src/routing-table/k-bucket.d.ts +20 -8
- package/dist/src/routing-table/k-bucket.d.ts.map +1 -1
- package/dist/src/routing-table/k-bucket.js +35 -16
- package/dist/src/routing-table/k-bucket.js.map +1 -1
- package/dist/src/routing-table/refresh.d.ts +5 -5
- package/dist/src/routing-table/refresh.d.ts.map +1 -1
- package/dist/src/routing-table/refresh.js +21 -15
- package/dist/src/routing-table/refresh.js.map +1 -1
- package/dist/src/rpc/handlers/find-node.d.ts.map +1 -1
- package/dist/src/rpc/handlers/find-node.js +40 -24
- package/dist/src/rpc/handlers/find-node.js.map +1 -1
- package/dist/src/rpc/handlers/get-providers.js +1 -1
- package/dist/src/rpc/handlers/get-providers.js.map +1 -1
- package/dist/src/rpc/handlers/get-value.js +1 -1
- package/dist/src/rpc/handlers/get-value.js.map +1 -1
- package/dist/src/rpc/index.d.ts +2 -1
- package/dist/src/rpc/index.d.ts.map +1 -1
- package/dist/src/rpc/index.js +50 -19
- package/dist/src/rpc/index.js.map +1 -1
- package/dist/src/topology-listener.d.ts +1 -1
- package/dist/src/topology-listener.d.ts.map +1 -1
- package/dist/src/topology-listener.js +1 -1
- package/dist/src/topology-listener.js.map +1 -1
- package/dist/src/utils.d.ts +3 -3
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +4 -3
- package/dist/src/utils.js.map +1 -1
- package/package.json +18 -18
- package/src/constants.ts +6 -0
- package/src/content-fetching/index.ts +7 -7
- package/src/content-routing/index.ts +3 -3
- package/src/errors.ts +0 -10
- package/src/index.ts +27 -5
- package/src/kad-dht.ts +28 -20
- package/src/network.ts +2 -1
- package/src/peer-distance-list.ts +7 -7
- package/src/peer-routing/index.ts +23 -34
- package/src/providers.ts +15 -33
- package/src/query/manager.ts +7 -3
- package/src/query/query-path.ts +11 -5
- package/src/query-self.ts +3 -1
- package/src/record/validators.ts +4 -3
- package/src/reprovider.ts +13 -16
- package/src/routing-table/index.ts +74 -45
- package/src/routing-table/k-bucket.ts +54 -20
- package/src/routing-table/refresh.ts +21 -16
- package/src/rpc/handlers/find-node.ts +41 -27
- package/src/rpc/handlers/get-providers.ts +1 -1
- package/src/rpc/handlers/get-value.ts +1 -1
- package/src/rpc/index.ts +54 -23
- package/src/topology-listener.ts +1 -1
- package/src/utils.ts +5 -4
- package/dist/typedoc-urls.json +0 -63
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PeerMap } from '@libp2p/peer-collections'
|
|
1
|
+
import { PeerMap, trackedPeerMap } from '@libp2p/peer-collections'
|
|
2
2
|
import map from 'it-map'
|
|
3
3
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
4
4
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
@@ -6,7 +6,7 @@ import { xor as uint8ArrayXor } from 'uint8arrays/xor'
|
|
|
6
6
|
import { PeerDistanceList } from '../peer-distance-list.js'
|
|
7
7
|
import { convertPeerId } from '../utils.js'
|
|
8
8
|
import { KBUCKET_SIZE, LAST_PING_THRESHOLD, PING_OLD_CONTACT_COUNT, PREFIX_LENGTH } from './index.js'
|
|
9
|
-
import type { PeerId, AbortOptions } from '@libp2p/interface'
|
|
9
|
+
import type { PeerId, AbortOptions, Metrics } from '@libp2p/interface'
|
|
10
10
|
|
|
11
11
|
export interface PingFunction {
|
|
12
12
|
/**
|
|
@@ -28,21 +28,26 @@ export interface OnAddCallback {
|
|
|
28
28
|
/**
|
|
29
29
|
* Invoked when a new peer is added to the routing tables
|
|
30
30
|
*/
|
|
31
|
-
(peer: Peer, bucket: LeafBucket): Promise<void>
|
|
31
|
+
(peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void>
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface OnRemoveCallback {
|
|
35
35
|
/**
|
|
36
36
|
* Invoked when a peer is evicted from the routing tables
|
|
37
37
|
*/
|
|
38
|
-
(peer: Peer, bucket: LeafBucket): Promise<void>
|
|
38
|
+
(peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void>
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export interface OnMoveCallback {
|
|
42
42
|
/**
|
|
43
43
|
* Invoked when a peer is moved between buckets in the routing tables
|
|
44
44
|
*/
|
|
45
|
-
(peer: Peer, oldBucket: LeafBucket, newBucket: LeafBucket): Promise<void>
|
|
45
|
+
(peer: Peer, oldBucket: LeafBucket, newBucket: LeafBucket, options?: AbortOptions): Promise<void>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface KBucketComponents {
|
|
49
|
+
peerId: PeerId
|
|
50
|
+
metrics?: Metrics
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export interface KBucketOptions {
|
|
@@ -97,6 +102,7 @@ export interface KBucketOptions {
|
|
|
97
102
|
verify: VerifyFunction
|
|
98
103
|
onAdd?: OnAddCallback
|
|
99
104
|
onRemove?: OnRemoveCallback
|
|
105
|
+
metricsPrefix?: string
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
export interface Peer {
|
|
@@ -124,11 +130,17 @@ export function isLeafBucket (obj: any): obj is LeafBucket {
|
|
|
124
130
|
return Array.isArray(obj?.peers)
|
|
125
131
|
}
|
|
126
132
|
|
|
133
|
+
export interface GetClosestPeersOptions extends AbortOptions {
|
|
134
|
+
count?: number
|
|
135
|
+
exclude?: PeerId[]
|
|
136
|
+
}
|
|
137
|
+
|
|
127
138
|
/**
|
|
128
139
|
* Implementation of a Kademlia DHT routing table as a prefix binary trie with
|
|
129
140
|
* configurable prefix length, bucket split threshold and size.
|
|
130
141
|
*/
|
|
131
142
|
export class KBucket {
|
|
143
|
+
private readonly peerId: PeerId
|
|
132
144
|
public root: Bucket
|
|
133
145
|
public localPeer?: Peer
|
|
134
146
|
private readonly prefixLength: number
|
|
@@ -143,7 +155,8 @@ export class KBucket {
|
|
|
143
155
|
private readonly onMove?: OnMoveCallback
|
|
144
156
|
private readonly addingPeerMap: PeerMap<Promise<void>>
|
|
145
157
|
|
|
146
|
-
constructor (options: KBucketOptions) {
|
|
158
|
+
constructor (components: KBucketComponents, options: KBucketOptions) {
|
|
159
|
+
this.peerId = components.peerId
|
|
147
160
|
this.prefixLength = options.prefixLength ?? PREFIX_LENGTH
|
|
148
161
|
this.kBucketSize = options.kBucketSize ?? KBUCKET_SIZE
|
|
149
162
|
this.splitThreshold = options.splitThreshold ?? this.kBucketSize
|
|
@@ -153,7 +166,10 @@ export class KBucket {
|
|
|
153
166
|
this.verify = options.verify
|
|
154
167
|
this.onAdd = options.onAdd
|
|
155
168
|
this.onRemove = options.onRemove
|
|
156
|
-
this.addingPeerMap =
|
|
169
|
+
this.addingPeerMap = trackedPeerMap({
|
|
170
|
+
name: `${options.metricsPrefix}_adding_peer_map`,
|
|
171
|
+
metrics: components.metrics
|
|
172
|
+
})
|
|
157
173
|
|
|
158
174
|
this.root = {
|
|
159
175
|
prefix: '',
|
|
@@ -162,10 +178,24 @@ export class KBucket {
|
|
|
162
178
|
}
|
|
163
179
|
}
|
|
164
180
|
|
|
165
|
-
async
|
|
181
|
+
async start (): Promise<void> {
|
|
182
|
+
await this.addSelfPeer(this.peerId)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
stop (): void {
|
|
186
|
+
this.addingPeerMap.clear()
|
|
187
|
+
|
|
188
|
+
this.root = {
|
|
189
|
+
prefix: '',
|
|
190
|
+
depth: 0,
|
|
191
|
+
peers: []
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async addSelfPeer (peerId: PeerId, options?: AbortOptions): Promise<void> {
|
|
166
196
|
this.localPeer = {
|
|
167
197
|
peerId,
|
|
168
|
-
kadId: await convertPeerId(peerId),
|
|
198
|
+
kadId: await convertPeerId(peerId, options),
|
|
169
199
|
lastPing: Date.now()
|
|
170
200
|
}
|
|
171
201
|
}
|
|
@@ -176,7 +206,7 @@ export class KBucket {
|
|
|
176
206
|
async add (peerId: PeerId, options?: AbortOptions): Promise<void> {
|
|
177
207
|
const peer = {
|
|
178
208
|
peerId,
|
|
179
|
-
kadId: await convertPeerId(peerId),
|
|
209
|
+
kadId: await convertPeerId(peerId, options),
|
|
180
210
|
lastPing: 0
|
|
181
211
|
}
|
|
182
212
|
|
|
@@ -206,7 +236,7 @@ export class KBucket {
|
|
|
206
236
|
// are there too many peers in the bucket and can we make the trie deeper?
|
|
207
237
|
if (bucket.peers.length === this.splitThreshold && bucket.depth < this.prefixLength) {
|
|
208
238
|
// split the bucket
|
|
209
|
-
await this._split(bucket)
|
|
239
|
+
await this._split(bucket, options)
|
|
210
240
|
|
|
211
241
|
// try again
|
|
212
242
|
await this._add(peer, options)
|
|
@@ -219,7 +249,7 @@ export class KBucket {
|
|
|
219
249
|
// we've ping this peer previously, just add them to the bucket
|
|
220
250
|
if (!needsPing(peer, this.lastPingThreshold)) {
|
|
221
251
|
bucket.peers.push(peer)
|
|
222
|
-
await this.onAdd?.(peer, bucket)
|
|
252
|
+
await this.onAdd?.(peer, bucket, options)
|
|
223
253
|
return
|
|
224
254
|
}
|
|
225
255
|
|
|
@@ -274,7 +304,7 @@ export class KBucket {
|
|
|
274
304
|
|
|
275
305
|
for await (const toEvict of this.ping(toPing, options)) {
|
|
276
306
|
evicted = true
|
|
277
|
-
await this.remove(toEvict.kadId)
|
|
307
|
+
await this.remove(toEvict.kadId, options)
|
|
278
308
|
}
|
|
279
309
|
|
|
280
310
|
// did not evict any peers, cannot add new contact
|
|
@@ -293,10 +323,14 @@ export class KBucket {
|
|
|
293
323
|
* @param {Uint8Array} id - Contact node id
|
|
294
324
|
* @returns {Generator<Peer, void, undefined>} Array Maximum of n closest contacts to the node id
|
|
295
325
|
*/
|
|
296
|
-
* closest (id: Uint8Array,
|
|
297
|
-
const list = new PeerDistanceList(id,
|
|
326
|
+
* closest (id: Uint8Array, options?: GetClosestPeersOptions): Generator<PeerId, void, undefined> {
|
|
327
|
+
const list = new PeerDistanceList(id, options?.count ?? this.kBucketSize)
|
|
298
328
|
|
|
299
329
|
for (const peer of this.toIterable()) {
|
|
330
|
+
if (options?.exclude?.some(p => p.equals(peer.peerId)) === true) {
|
|
331
|
+
continue
|
|
332
|
+
}
|
|
333
|
+
|
|
300
334
|
list.addWithKadId({ id: peer.peerId, multiaddrs: [] }, peer.kadId)
|
|
301
335
|
}
|
|
302
336
|
|
|
@@ -351,14 +385,14 @@ export class KBucket {
|
|
|
351
385
|
*
|
|
352
386
|
* @param {Uint8Array} kadId - The ID of the contact to remove
|
|
353
387
|
*/
|
|
354
|
-
async remove (kadId: Uint8Array): Promise<void> {
|
|
388
|
+
async remove (kadId: Uint8Array, options?: AbortOptions): Promise<void> {
|
|
355
389
|
const bucket = this._determineBucket(kadId)
|
|
356
390
|
const index = this._indexOf(bucket, kadId)
|
|
357
391
|
|
|
358
392
|
if (index > -1) {
|
|
359
393
|
const peer = bucket.peers.splice(index, 1)[0]
|
|
360
394
|
|
|
361
|
-
await this.onRemove?.(peer, bucket)
|
|
395
|
+
await this.onRemove?.(peer, bucket, options)
|
|
362
396
|
}
|
|
363
397
|
}
|
|
364
398
|
|
|
@@ -439,7 +473,7 @@ export class KBucket {
|
|
|
439
473
|
*
|
|
440
474
|
* @param {any} bucket - bucket for splitting
|
|
441
475
|
*/
|
|
442
|
-
private async _split (bucket: LeafBucket): Promise<void> {
|
|
476
|
+
private async _split (bucket: LeafBucket, options?: AbortOptions): Promise<void> {
|
|
443
477
|
// create child buckets
|
|
444
478
|
const left: LeafBucket = {
|
|
445
479
|
prefix: '0',
|
|
@@ -458,10 +492,10 @@ export class KBucket {
|
|
|
458
492
|
|
|
459
493
|
if (bitString[bucket.depth] === '0') {
|
|
460
494
|
left.peers.push(peer)
|
|
461
|
-
await this.onMove?.(peer, bucket, left)
|
|
495
|
+
await this.onMove?.(peer, bucket, left, options)
|
|
462
496
|
} else {
|
|
463
497
|
right.peers.push(peer)
|
|
464
|
-
await this.onMove?.(peer, bucket, right)
|
|
498
|
+
await this.onMove?.(peer, bucket, right, options)
|
|
465
499
|
}
|
|
466
500
|
}
|
|
467
501
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from '@libp2p/crypto'
|
|
2
|
-
import { setMaxListeners } from '@libp2p/interface'
|
|
3
2
|
import { peerIdFromMultihash } from '@libp2p/peer-id'
|
|
3
|
+
import { anySignal } from 'any-signal'
|
|
4
4
|
import length from 'it-length'
|
|
5
|
+
import { setMaxListeners } from 'main-event'
|
|
5
6
|
import * as Digest from 'multiformats/hashes/digest'
|
|
6
7
|
import { sha256 } from 'multiformats/hashes/sha2'
|
|
7
8
|
import { xor as uint8ArrayXor } from 'uint8arrays/xor'
|
|
@@ -9,7 +10,7 @@ import { TABLE_REFRESH_INTERVAL, TABLE_REFRESH_QUERY_TIMEOUT } from '../constant
|
|
|
9
10
|
import GENERATED_PREFIXES from './generated-prefix-list.js'
|
|
10
11
|
import type { RoutingTable } from './index.js'
|
|
11
12
|
import type { PeerRouting } from '../peer-routing/index.js'
|
|
12
|
-
import type { ComponentLogger, Logger, PeerId } from '@libp2p/interface'
|
|
13
|
+
import type { AbortOptions, ComponentLogger, Logger, PeerId } from '@libp2p/interface'
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Cannot generate random KadIds longer than this + 1
|
|
@@ -70,7 +71,7 @@ export class RoutingTableRefresh {
|
|
|
70
71
|
* that is close to the requested peer ID and query that, then network
|
|
71
72
|
* peers will tell us who they know who is close to the fake ID
|
|
72
73
|
*/
|
|
73
|
-
refreshTable (force: boolean = false): void {
|
|
74
|
+
refreshTable (force: boolean = false, options?: AbortOptions): void {
|
|
74
75
|
this.log('refreshing routing table')
|
|
75
76
|
|
|
76
77
|
const prefixLength = this._maxCommonPrefix()
|
|
@@ -97,14 +98,14 @@ export class RoutingTableRefresh {
|
|
|
97
98
|
Promise.all(
|
|
98
99
|
refreshCommonPrefixLengths.map(async (lastRefresh, index) => {
|
|
99
100
|
try {
|
|
100
|
-
await this._refreshCommonPrefixLength(index, lastRefresh, force)
|
|
101
|
+
await this._refreshCommonPrefixLength(index, lastRefresh, force, options)
|
|
101
102
|
|
|
102
103
|
if (this._numPeersForCpl(prefixLength) === 0) {
|
|
103
104
|
const lastCpl = Math.min(2 * (index + 1), refreshCommonPrefixLengths.length - 1)
|
|
104
105
|
|
|
105
106
|
for (let n = index + 1; n < lastCpl + 1; n++) {
|
|
106
107
|
try {
|
|
107
|
-
await this._refreshCommonPrefixLength(n, lastRefresh, force)
|
|
108
|
+
await this._refreshCommonPrefixLength(n, lastRefresh, force, options)
|
|
108
109
|
} catch (err: any) {
|
|
109
110
|
this.log.error(err)
|
|
110
111
|
}
|
|
@@ -127,26 +128,30 @@ export class RoutingTableRefresh {
|
|
|
127
128
|
})
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
async _refreshCommonPrefixLength (cpl: number, lastRefresh: Date, force: boolean): Promise<void> {
|
|
131
|
+
async _refreshCommonPrefixLength (cpl: number, lastRefresh: Date, force: boolean, options?: AbortOptions): Promise<void> {
|
|
131
132
|
if (!force && lastRefresh.getTime() > (Date.now() - this.refreshInterval)) {
|
|
132
133
|
this.log('not running refresh for cpl %s as time since last refresh not above interval', cpl)
|
|
133
134
|
return
|
|
134
135
|
}
|
|
135
136
|
|
|
136
137
|
// gen a key for the query to refresh the cpl
|
|
137
|
-
const peerId =
|
|
138
|
+
const peerId = this._generateRandomPeerId(cpl)
|
|
138
139
|
|
|
139
140
|
this.log('starting refreshing cpl %s with key %p (routing table size was %s)', cpl, peerId, this.routingTable.size)
|
|
140
141
|
|
|
141
|
-
const signal = AbortSignal.timeout(this.refreshQueryTimeout)
|
|
142
|
+
const signal = anySignal([options?.signal, AbortSignal.timeout(this.refreshQueryTimeout)])
|
|
142
143
|
setMaxListeners(Infinity, signal)
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
try {
|
|
146
|
+
const peers = await length(this.peerRouting.getClosestPeers(peerId.toMultihash().bytes, {
|
|
147
|
+
signal
|
|
148
|
+
}))
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
this.log(`found ${peers} peers that were close to imaginary peer %p`, peerId)
|
|
151
|
+
this.log('finished refreshing cpl %s with key %p (routing table size is now %s)', cpl, peerId, this.routingTable.size)
|
|
152
|
+
} finally {
|
|
153
|
+
signal.clear()
|
|
154
|
+
}
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
_getTrackedCommonPrefixLengthsForRefresh (maxCommonPrefix: number): Date[] {
|
|
@@ -164,7 +169,7 @@ export class RoutingTableRefresh {
|
|
|
164
169
|
return dates
|
|
165
170
|
}
|
|
166
171
|
|
|
167
|
-
|
|
172
|
+
_generateRandomPeerId (targetCommonPrefixLength: number): PeerId {
|
|
168
173
|
if (this.routingTable.kb == null) {
|
|
169
174
|
throw new Error('Routing table not started')
|
|
170
175
|
}
|
|
@@ -176,13 +181,13 @@ export class RoutingTableRefresh {
|
|
|
176
181
|
const randomData = randomBytes(2)
|
|
177
182
|
const randomUint16 = (randomData[1] << 8) + randomData[0]
|
|
178
183
|
|
|
179
|
-
const key =
|
|
184
|
+
const key = this._makePeerId(this.routingTable.kb.localPeer.kadId, randomUint16, targetCommonPrefixLength)
|
|
180
185
|
const multihash = Digest.decode(key)
|
|
181
186
|
|
|
182
187
|
return peerIdFromMultihash(multihash)
|
|
183
188
|
}
|
|
184
189
|
|
|
185
|
-
|
|
190
|
+
_makePeerId (localKadId: Uint8Array, randomPrefix: number, targetCommonPrefixLength: number): Uint8Array {
|
|
186
191
|
if (targetCommonPrefixLength > MAX_COMMON_PREFIX_LENGTH) {
|
|
187
192
|
throw new Error(`Cannot generate peer ID for common prefix length greater than ${MAX_COMMON_PREFIX_LENGTH}`)
|
|
188
193
|
}
|
|
@@ -42,38 +42,52 @@ export class FindNodeHandler implements DHTMessageHandler {
|
|
|
42
42
|
* Process `FindNode` DHT messages
|
|
43
43
|
*/
|
|
44
44
|
async handle (peerId: PeerId, msg: Message): Promise<Message> {
|
|
45
|
-
this.log('incoming request from %p for peers
|
|
45
|
+
this.log('incoming request from %p for peers close to %b', peerId, msg.key)
|
|
46
|
+
try {
|
|
47
|
+
if (msg.key == null) {
|
|
48
|
+
throw new InvalidMessageError('Invalid FIND_NODE message received - key was missing')
|
|
49
|
+
}
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const closer: PeerInfo[] = await this.peerRouting.getCloserPeersOffline(msg.key, peerId)
|
|
51
|
+
const closer: PeerInfo[] = await this.peerRouting.getClosestPeersOffline(msg.key, {
|
|
52
|
+
exclude: [
|
|
53
|
+
// never tell a peer about itself
|
|
54
|
+
peerId,
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
multiaddrs: this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
|
|
56
|
+
// do not include the server in the results
|
|
57
|
+
this.peerId
|
|
58
|
+
]
|
|
57
59
|
})
|
|
58
|
-
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.map(peerInfo => ({
|
|
67
|
-
id: peerInfo.id.toMultihash().bytes,
|
|
68
|
-
multiaddrs: peerInfo.multiaddrs.map(ma => ma.bytes)
|
|
69
|
-
})),
|
|
70
|
-
providers: []
|
|
71
|
-
}
|
|
61
|
+
if (uint8ArrayEquals(this.peerId.toMultihash().bytes, msg.key)) {
|
|
62
|
+
closer.push({
|
|
63
|
+
id: this.peerId,
|
|
64
|
+
multiaddrs: this.addressManager.getAddresses().map(ma => ma.decapsulateCode(protocols('p2p').code))
|
|
65
|
+
})
|
|
66
|
+
}
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
const response: Message = {
|
|
69
|
+
type: MessageType.FIND_NODE,
|
|
70
|
+
clusterLevel: msg.clusterLevel,
|
|
71
|
+
closer: closer
|
|
72
|
+
.map(this.peerInfoMapper)
|
|
73
|
+
.filter(({ multiaddrs }) => multiaddrs.length)
|
|
74
|
+
.map(peerInfo => ({
|
|
75
|
+
id: peerInfo.id.toMultihash().bytes,
|
|
76
|
+
multiaddrs: peerInfo.multiaddrs.map(ma => ma.bytes)
|
|
77
|
+
})),
|
|
78
|
+
providers: []
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (response.closer.length === 0) {
|
|
82
|
+
this.log('could not find any peers closer to %b for %p', msg.key, peerId)
|
|
83
|
+
} else {
|
|
84
|
+
this.log('found %d peers close to %b for %p', response.closer.length, msg.key, peerId)
|
|
85
|
+
}
|
|
76
86
|
|
|
77
|
-
|
|
87
|
+
return response
|
|
88
|
+
} catch (err: any) {
|
|
89
|
+
this.log('error during finding peers closer to %b for %p - %e', msg.key, peerId, err)
|
|
90
|
+
throw err
|
|
91
|
+
}
|
|
78
92
|
}
|
|
79
93
|
}
|
|
@@ -84,7 +84,7 @@ export class GetValueHandler implements DHTMessageHandler {
|
|
|
84
84
|
|
|
85
85
|
const [record, closer] = await Promise.all([
|
|
86
86
|
this._checkLocalDatastore(key),
|
|
87
|
-
this.peerRouting.
|
|
87
|
+
this.peerRouting.getClosestPeersOffline(key)
|
|
88
88
|
])
|
|
89
89
|
|
|
90
90
|
if (record != null) {
|
package/src/rpc/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { TimeoutError } from '@libp2p/interface'
|
|
2
|
+
import { pbStream } from 'it-protobuf-stream'
|
|
3
3
|
import { Message, MessageType } from '../message/dht.js'
|
|
4
4
|
import { AddProviderHandler } from './handlers/add-provider.js'
|
|
5
5
|
import { FindNodeHandler } from './handlers/find-node.js'
|
|
@@ -15,7 +15,7 @@ import type { GetProvidersHandlerComponents } from './handlers/get-providers.js'
|
|
|
15
15
|
import type { GetValueHandlerComponents } from './handlers/get-value.js'
|
|
16
16
|
import type { PutValueHandlerComponents } from './handlers/put-value.js'
|
|
17
17
|
import type { RoutingTable } from '../routing-table/index.js'
|
|
18
|
-
import type { CounterGroup, Logger, Metrics, PeerId, IncomingStreamData } from '@libp2p/interface'
|
|
18
|
+
import type { CounterGroup, Logger, Metrics, PeerId, IncomingStreamData, MetricGroup } from '@libp2p/interface'
|
|
19
19
|
|
|
20
20
|
export interface DHTMessageHandler {
|
|
21
21
|
handle(peerId: PeerId, msg: Message): Promise<Message | undefined>
|
|
@@ -30,6 +30,7 @@ export interface RPCInit {
|
|
|
30
30
|
metricsPrefix: string
|
|
31
31
|
datastorePrefix: string
|
|
32
32
|
peerInfoMapper: PeerInfoMapper
|
|
33
|
+
incomingMessageTimeout?: number
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export interface RPCComponents extends GetValueHandlerComponents, PutValueHandlerComponents, FindNodeHandlerComponents, GetProvidersHandlerComponents {
|
|
@@ -38,21 +39,24 @@ export interface RPCComponents extends GetValueHandlerComponents, PutValueHandle
|
|
|
38
39
|
|
|
39
40
|
export class RPC {
|
|
40
41
|
private readonly handlers: Record<string, DHTMessageHandler>
|
|
41
|
-
private readonly routingTable: RoutingTable
|
|
42
42
|
private readonly log: Logger
|
|
43
43
|
private readonly metrics: {
|
|
44
44
|
operations?: CounterGroup
|
|
45
45
|
errors?: CounterGroup
|
|
46
|
+
rpcTime?: MetricGroup
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
private readonly incomingMessageTimeout: number
|
|
50
|
+
|
|
48
51
|
constructor (components: RPCComponents, init: RPCInit) {
|
|
49
52
|
this.metrics = {
|
|
50
53
|
operations: components.metrics?.registerCounterGroup(`${init.metricsPrefix}_inbound_rpc_requests_total`),
|
|
51
|
-
errors: components.metrics?.registerCounterGroup(`${init.metricsPrefix}_inbound_rpc_errors_total`)
|
|
54
|
+
errors: components.metrics?.registerCounterGroup(`${init.metricsPrefix}_inbound_rpc_errors_total`),
|
|
55
|
+
rpcTime: components.metrics?.registerMetricGroup(`${init.metricsPrefix}_inbound_rpc_time_seconds`, { label: 'operation' })
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
this.log = components.logger.forComponent(`${init.logPrefix}:rpc`)
|
|
55
|
-
this.
|
|
59
|
+
this.incomingMessageTimeout = init.incomingMessageTimeout ?? 10_000
|
|
56
60
|
this.handlers = {
|
|
57
61
|
[MessageType.GET_VALUE.toString()]: new GetValueHandler(components, init),
|
|
58
62
|
[MessageType.PUT_VALUE.toString()]: new PutValueHandler(components, init),
|
|
@@ -92,34 +96,61 @@ export class RPC {
|
|
|
92
96
|
* Handle incoming streams on the dht protocol
|
|
93
97
|
*/
|
|
94
98
|
onIncomingStream (data: IncomingStreamData): void {
|
|
95
|
-
|
|
99
|
+
const message = 'unknown'
|
|
96
100
|
|
|
97
101
|
Promise.resolve().then(async () => {
|
|
98
102
|
const { stream, connection } = data
|
|
99
|
-
const peerId = connection.remotePeer
|
|
100
103
|
|
|
101
|
-
const
|
|
104
|
+
const abortListener = (): void => {
|
|
105
|
+
stream.abort(new TimeoutError())
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let signal = AbortSignal.timeout(this.incomingMessageTimeout)
|
|
109
|
+
signal.addEventListener('abort', abortListener)
|
|
110
|
+
|
|
111
|
+
const messages = pbStream(stream).pb(Message)
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
while (true) {
|
|
115
|
+
const message = await messages.read({
|
|
116
|
+
signal
|
|
117
|
+
})
|
|
102
118
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
const stopSuccessTimer = this.metrics?.rpcTime?.timer(message.type.toString())
|
|
120
|
+
const stopErrorTimer = this.metrics?.rpcTime?.timer(message.type.toString())
|
|
121
|
+
let errored = false
|
|
122
|
+
|
|
123
|
+
try {
|
|
108
124
|
// handle the message
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
self.log('incoming %s from %p', desMessage.type, peerId)
|
|
112
|
-
const res = await self.handleMessage(peerId, desMessage)
|
|
125
|
+
this.log('incoming %s from %p', message.type, connection.remotePeer)
|
|
126
|
+
const res = await this.handleMessage(connection.remotePeer, message)
|
|
113
127
|
|
|
114
128
|
// Not all handlers will return a response
|
|
115
129
|
if (res != null) {
|
|
116
|
-
|
|
130
|
+
await messages.write(res, {
|
|
131
|
+
signal
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
errored = true
|
|
136
|
+
stopErrorTimer?.()
|
|
137
|
+
|
|
138
|
+
throw err
|
|
139
|
+
} finally {
|
|
140
|
+
if (!errored) {
|
|
141
|
+
stopSuccessTimer?.()
|
|
117
142
|
}
|
|
118
143
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
|
|
145
|
+
// we have received a message so reset the timeout controller to
|
|
146
|
+
// allow the remote to send another
|
|
147
|
+
signal.removeEventListener('abort', abortListener)
|
|
148
|
+
signal = AbortSignal.timeout(this.incomingMessageTimeout)
|
|
149
|
+
signal.addEventListener('abort', abortListener)
|
|
150
|
+
}
|
|
151
|
+
} catch (err: any) {
|
|
152
|
+
stream.abort(err)
|
|
153
|
+
}
|
|
123
154
|
})
|
|
124
155
|
.catch(err => {
|
|
125
156
|
this.log.error('error handling %s RPC message from %p - %e', message, data.connection.remotePeer, err)
|
package/src/topology-listener.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
|
|
|
11
11
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
12
12
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
13
13
|
import type { Operation, OperationMetrics } from './kad-dht.js'
|
|
14
|
-
import type { PeerId, PeerInfo } from '@libp2p/interface'
|
|
14
|
+
import type { AbortOptions, PeerId, PeerInfo } from '@libp2p/interface'
|
|
15
15
|
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
16
16
|
|
|
17
17
|
// const IPNS_PREFIX = uint8ArrayFromString('/ipns/')
|
|
@@ -90,8 +90,9 @@ export function passthroughMapper (info: PeerInfo): PeerInfo {
|
|
|
90
90
|
/**
|
|
91
91
|
* Creates a DHT ID by hashing a given Uint8Array
|
|
92
92
|
*/
|
|
93
|
-
export async function convertBuffer (buf: Uint8Array): Promise<Uint8Array> {
|
|
93
|
+
export async function convertBuffer (buf: Uint8Array, options?: AbortOptions): Promise<Uint8Array> {
|
|
94
94
|
const multihash = await sha256.digest(buf)
|
|
95
|
+
options?.signal?.throwIfAborted()
|
|
95
96
|
|
|
96
97
|
return multihash.digest
|
|
97
98
|
}
|
|
@@ -99,8 +100,8 @@ export async function convertBuffer (buf: Uint8Array): Promise<Uint8Array> {
|
|
|
99
100
|
/**
|
|
100
101
|
* Creates a DHT ID by hashing a Peer ID
|
|
101
102
|
*/
|
|
102
|
-
export async function convertPeerId (peerId: PeerId): Promise<Uint8Array> {
|
|
103
|
-
return convertBuffer(peerId.toMultihash().bytes)
|
|
103
|
+
export async function convertPeerId (peerId: PeerId, options?: AbortOptions): Promise<Uint8Array> {
|
|
104
|
+
return convertBuffer(peerId.toMultihash().bytes, options)
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
/**
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"codec": "https://libp2p.github.io/js-libp2p/functions/_libp2p_kad-dht.MessageType.codec.html",
|
|
3
|
-
"EventTypes": "https://libp2p.github.io/js-libp2p/enums/_libp2p_kad-dht.EventTypes.html",
|
|
4
|
-
".:EventTypes": "https://libp2p.github.io/js-libp2p/enums/_libp2p_kad-dht.EventTypes.html",
|
|
5
|
-
"MessageType": "https://libp2p.github.io/js-libp2p/enums/_libp2p_kad-dht.MessageType.html",
|
|
6
|
-
"AddPeerEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.AddPeerEvent.html",
|
|
7
|
-
".:AddPeerEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.AddPeerEvent.html",
|
|
8
|
-
"DHTRecord": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.DHTRecord.html",
|
|
9
|
-
".:DHTRecord": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.DHTRecord.html",
|
|
10
|
-
"DialPeerEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.DialPeerEvent.html",
|
|
11
|
-
".:DialPeerEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.DialPeerEvent.html",
|
|
12
|
-
"DisjointPath": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.DisjointPath.html",
|
|
13
|
-
".:DisjointPath": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.DisjointPath.html",
|
|
14
|
-
"FinalPeerEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.FinalPeerEvent.html",
|
|
15
|
-
".:FinalPeerEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.FinalPeerEvent.html",
|
|
16
|
-
"KadDHT": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.KadDHT.html",
|
|
17
|
-
".:KadDHT": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.KadDHT.html",
|
|
18
|
-
"KadDHTComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.KadDHTComponents.html",
|
|
19
|
-
".:KadDHTComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.KadDHTComponents.html",
|
|
20
|
-
"KadDHTInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.KadDHTInit.html",
|
|
21
|
-
".:KadDHTInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.KadDHTInit.html",
|
|
22
|
-
"PathEndedEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.PathEndedEvent.html",
|
|
23
|
-
".:PathEndedEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.PathEndedEvent.html",
|
|
24
|
-
"PeerInfoMapper": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.PeerInfoMapper.html",
|
|
25
|
-
".:PeerInfoMapper": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.PeerInfoMapper.html",
|
|
26
|
-
"PeerResponseEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.PeerResponseEvent.html",
|
|
27
|
-
".:PeerResponseEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.PeerResponseEvent.html",
|
|
28
|
-
"ProviderEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ProviderEvent.html",
|
|
29
|
-
".:ProviderEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ProviderEvent.html",
|
|
30
|
-
"ProvidersInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ProvidersInit.html",
|
|
31
|
-
".:ProvidersInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ProvidersInit.html",
|
|
32
|
-
"QueryErrorEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.QueryErrorEvent.html",
|
|
33
|
-
".:QueryErrorEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.QueryErrorEvent.html",
|
|
34
|
-
"ReProvideInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ReProvideInit.html",
|
|
35
|
-
".:ReProvideInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ReProvideInit.html",
|
|
36
|
-
"RoutingTable": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.RoutingTable.html",
|
|
37
|
-
".:RoutingTable": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.RoutingTable.html",
|
|
38
|
-
"SelectFn": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.SelectFn.html",
|
|
39
|
-
".:SelectFn": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.SelectFn.html",
|
|
40
|
-
"SendQueryEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.SendQueryEvent.html",
|
|
41
|
-
".:SendQueryEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.SendQueryEvent.html",
|
|
42
|
-
"SingleKadDHT": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.SingleKadDHT.html",
|
|
43
|
-
".:SingleKadDHT": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.SingleKadDHT.html",
|
|
44
|
-
"ValidateFn": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ValidateFn.html",
|
|
45
|
-
".:ValidateFn": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ValidateFn.html",
|
|
46
|
-
"ValueEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ValueEvent.html",
|
|
47
|
-
".:ValueEvent": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_kad-dht.ValueEvent.html",
|
|
48
|
-
"DHTProgressEvents": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.DHTProgressEvents.html",
|
|
49
|
-
".:DHTProgressEvents": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.DHTProgressEvents.html",
|
|
50
|
-
"MessageName": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.MessageName.html",
|
|
51
|
-
".:MessageName": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.MessageName.html",
|
|
52
|
-
"QueryEvent": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.QueryEvent.html",
|
|
53
|
-
".:QueryEvent": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.QueryEvent.html",
|
|
54
|
-
"Selectors": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.Selectors.html",
|
|
55
|
-
".:Selectors": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.Selectors.html",
|
|
56
|
-
"Validators": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.Validators.html",
|
|
57
|
-
".:Validators": "https://libp2p.github.io/js-libp2p/types/_libp2p_kad-dht.Validators.html",
|
|
58
|
-
"kadDHT": "https://libp2p.github.io/js-libp2p/functions/_libp2p_kad-dht.kadDHT.html",
|
|
59
|
-
".:kadDHT": "https://libp2p.github.io/js-libp2p/functions/_libp2p_kad-dht.kadDHT.html",
|
|
60
|
-
"passthroughMapper": "https://libp2p.github.io/js-libp2p/functions/_libp2p_kad-dht.passthroughMapper.html",
|
|
61
|
-
"removePrivateAddressesMapper": "https://libp2p.github.io/js-libp2p/functions/_libp2p_kad-dht.removePrivateAddressesMapper.html",
|
|
62
|
-
"removePublicAddressesMapper": "https://libp2p.github.io/js-libp2p/functions/_libp2p_kad-dht.removePublicAddressesMapper.html"
|
|
63
|
-
}
|