@libp2p/kad-dht 12.0.1 → 12.0.2
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 +17 -17
- package/dist/src/kad-dht.d.ts.map +1 -1
- package/dist/src/kad-dht.js +11 -70
- package/dist/src/kad-dht.js.map +1 -1
- package/dist/src/query/manager.d.ts.map +1 -1
- package/dist/src/query/manager.js +20 -12
- package/dist/src/query/manager.js.map +1 -1
- package/dist/src/routing-table/index.d.ts +2 -2
- package/dist/src/routing-table/index.d.ts.map +1 -1
- package/dist/src/routing-table/index.js +12 -15
- package/dist/src/routing-table/index.js.map +1 -1
- package/package.json +16 -15
- package/src/kad-dht.ts +12 -80
- package/src/query/manager.ts +24 -13
- package/src/routing-table/index.ts +14 -19
package/src/kad-dht.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { CodeError, CustomEvent, TypedEventEmitter, contentRoutingSymbol, peerDiscoverySymbol, peerRoutingSymbol } from '@libp2p/interface'
|
|
2
2
|
import drain from 'it-drain'
|
|
3
|
-
import map from 'it-map'
|
|
4
|
-
import parallel from 'it-parallel'
|
|
5
3
|
import pDefer from 'p-defer'
|
|
6
4
|
import { PROTOCOL } from './constants.js'
|
|
7
5
|
import { ContentFetching } from './content-fetching/index.js'
|
|
@@ -22,42 +20,17 @@ import {
|
|
|
22
20
|
removePrivateAddressesMapper
|
|
23
21
|
} from './utils.js'
|
|
24
22
|
import type { KadDHTComponents, KadDHTInit, Validators, Selectors, KadDHT as KadDHTInterface, QueryEvent, PeerInfoMapper } from './index.js'
|
|
25
|
-
import type {
|
|
23
|
+
import type { ContentRouting, Logger, PeerDiscovery, PeerDiscoveryEvents, PeerId, PeerInfo, PeerRouting, RoutingOptions, Startable } from '@libp2p/interface'
|
|
26
24
|
import type { CID } from 'multiformats/cid'
|
|
27
25
|
|
|
28
|
-
async function * ensurePeerInfoHasMultiaddrs (source: AsyncGenerator<PeerInfo>, peerRouting: PeerRouting, log: Logger, options: AbortOptions = {}): AsyncGenerator<() => Promise<PeerInfo | undefined>, void, undefined> {
|
|
29
|
-
yield * map(source, prov => {
|
|
30
|
-
return async () => {
|
|
31
|
-
if (prov.multiaddrs.length > 0) {
|
|
32
|
-
return prov
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
return await peerRouting.findPeer(prov.id, {
|
|
37
|
-
...options,
|
|
38
|
-
useCache: false
|
|
39
|
-
})
|
|
40
|
-
} catch (err) {
|
|
41
|
-
log.error('could not find peer', err)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
26
|
/**
|
|
48
27
|
* Wrapper class to convert events into returned values
|
|
49
28
|
*/
|
|
50
29
|
class DHTContentRouting implements ContentRouting {
|
|
51
30
|
private readonly dht: KadDHTInterface
|
|
52
|
-
private readonly peerInfoMapper: PeerInfoMapper
|
|
53
|
-
private readonly peerRouting: PeerRouting
|
|
54
|
-
private readonly log: Logger
|
|
55
31
|
|
|
56
|
-
constructor (dht: KadDHTInterface
|
|
32
|
+
constructor (dht: KadDHTInterface) {
|
|
57
33
|
this.dht = dht
|
|
58
|
-
this.peerInfoMapper = peerInfoMapper
|
|
59
|
-
this.peerRouting = peerRouting
|
|
60
|
-
this.log = log
|
|
61
34
|
}
|
|
62
35
|
|
|
63
36
|
async provide (cid: CID, options: RoutingOptions = {}): Promise<void> {
|
|
@@ -65,27 +38,11 @@ class DHTContentRouting implements ContentRouting {
|
|
|
65
38
|
}
|
|
66
39
|
|
|
67
40
|
async * findProviders (cid: CID, options: RoutingOptions = {}): AsyncGenerator<PeerInfo, void, undefined> {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (event.name === 'PROVIDER') {
|
|
72
|
-
yield * event.providers
|
|
73
|
-
}
|
|
41
|
+
for await (const event of this.dht.findProviders(cid, options)) {
|
|
42
|
+
if (event.name === 'PROVIDER') {
|
|
43
|
+
yield * event.providers
|
|
74
44
|
}
|
|
75
45
|
}
|
|
76
|
-
for await (let peerInfo of parallel(ensurePeerInfoHasMultiaddrs(source(), this.peerRouting, this.log, options))) {
|
|
77
|
-
if (peerInfo == null) {
|
|
78
|
-
continue
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
peerInfo = this.peerInfoMapper(peerInfo)
|
|
82
|
-
|
|
83
|
-
if (peerInfo.multiaddrs.length === 0) {
|
|
84
|
-
continue
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
yield peerInfo
|
|
88
|
-
}
|
|
89
46
|
}
|
|
90
47
|
|
|
91
48
|
async put (key: Uint8Array, value: Uint8Array, options?: RoutingOptions): Promise<void> {
|
|
@@ -108,23 +65,15 @@ class DHTContentRouting implements ContentRouting {
|
|
|
108
65
|
*/
|
|
109
66
|
class DHTPeerRouting implements PeerRouting {
|
|
110
67
|
private readonly dht: KadDHTInterface
|
|
111
|
-
private readonly peerInfoMapper: PeerInfoMapper
|
|
112
|
-
private readonly log: Logger
|
|
113
68
|
|
|
114
|
-
constructor (dht: KadDHTInterface
|
|
69
|
+
constructor (dht: KadDHTInterface) {
|
|
115
70
|
this.dht = dht
|
|
116
|
-
this.peerInfoMapper = peerInfoMapper
|
|
117
|
-
this.log = log
|
|
118
71
|
}
|
|
119
72
|
|
|
120
73
|
async findPeer (peerId: PeerId, options: RoutingOptions = {}): Promise<PeerInfo> {
|
|
121
74
|
for await (const event of this.dht.findPeer(peerId, options)) {
|
|
122
75
|
if (event.name === 'FINAL_PEER') {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (peer.multiaddrs.length > 0) {
|
|
126
|
-
return event.peer
|
|
127
|
-
}
|
|
76
|
+
return event.peer
|
|
128
77
|
}
|
|
129
78
|
}
|
|
130
79
|
|
|
@@ -132,27 +81,10 @@ class DHTPeerRouting implements PeerRouting {
|
|
|
132
81
|
}
|
|
133
82
|
|
|
134
83
|
async * getClosestPeers (key: Uint8Array, options: RoutingOptions = {}): AsyncIterable<PeerInfo> {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (event.name === 'FINAL_PEER') {
|
|
139
|
-
yield event.peer
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
for await (let peerInfo of parallel(ensurePeerInfoHasMultiaddrs(source(), this, this.log, options))) {
|
|
145
|
-
if (peerInfo == null) {
|
|
146
|
-
continue
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
peerInfo = this.peerInfoMapper(peerInfo)
|
|
150
|
-
|
|
151
|
-
if (peerInfo.multiaddrs.length === 0) {
|
|
152
|
-
continue
|
|
84
|
+
for await (const event of this.dht.getClosestPeers(key, options)) {
|
|
85
|
+
if (event.name === 'FINAL_PEER') {
|
|
86
|
+
yield event.peer
|
|
153
87
|
}
|
|
154
|
-
|
|
155
|
-
yield peerInfo
|
|
156
88
|
}
|
|
157
89
|
}
|
|
158
90
|
}
|
|
@@ -347,8 +279,8 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
347
279
|
})
|
|
348
280
|
})
|
|
349
281
|
|
|
350
|
-
this.dhtPeerRouting = new DHTPeerRouting(this
|
|
351
|
-
this.dhtContentRouting = new DHTContentRouting(this
|
|
282
|
+
this.dhtPeerRouting = new DHTPeerRouting(this)
|
|
283
|
+
this.dhtContentRouting = new DHTContentRouting(this)
|
|
352
284
|
|
|
353
285
|
// if client mode has not been explicitly specified, auto-switch to server
|
|
354
286
|
// mode when the node's peer data is updated with publicly dialable
|
package/src/query/manager.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TypedEventEmitter, CustomEvent, setMaxListeners } from '@libp2p/interface'
|
|
2
2
|
import { PeerSet } from '@libp2p/peer-collections'
|
|
3
3
|
import { anySignal } from 'any-signal'
|
|
4
4
|
import merge from 'it-merge'
|
|
5
|
+
import { raceSignal } from 'race-signal'
|
|
5
6
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
6
7
|
import {
|
|
7
8
|
ALPHA, K, DEFAULT_QUERY_TIMEOUT
|
|
@@ -127,7 +128,16 @@ export class QueryManager implements Startable {
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
// if the user breaks out of a for..await of loop iterating over query
|
|
132
|
+
// results we need to cancel any in-flight network requests
|
|
133
|
+
const queryEarlyExitController = new AbortController()
|
|
134
|
+
setMaxListeners(Infinity, queryEarlyExitController.signal)
|
|
135
|
+
|
|
136
|
+
const signal = anySignal([
|
|
137
|
+
this.shutDownController.signal,
|
|
138
|
+
queryEarlyExitController.signal,
|
|
139
|
+
options.signal
|
|
140
|
+
])
|
|
131
141
|
|
|
132
142
|
// this signal will get listened to for every invocation of queryFunc
|
|
133
143
|
// so make sure we don't make a lot of noise in the logs
|
|
@@ -138,19 +148,13 @@ export class QueryManager implements Startable {
|
|
|
138
148
|
// query a subset of peers up to `kBucketSize / 2` in length
|
|
139
149
|
const startTime = Date.now()
|
|
140
150
|
const cleanUp = new TypedEventEmitter<CleanUpEvents>()
|
|
151
|
+
let queryFinished = false
|
|
141
152
|
|
|
142
153
|
try {
|
|
143
154
|
if (options.isSelfQuery !== true && this.initialQuerySelfHasRun != null) {
|
|
144
155
|
log('waiting for initial query-self query before continuing')
|
|
145
156
|
|
|
146
|
-
await
|
|
147
|
-
new Promise((resolve, reject) => {
|
|
148
|
-
signal.addEventListener('abort', () => {
|
|
149
|
-
reject(new AbortError('Query was aborted before self-query ran'))
|
|
150
|
-
})
|
|
151
|
-
}),
|
|
152
|
-
this.initialQuerySelfHasRun.promise
|
|
153
|
-
])
|
|
157
|
+
await raceSignal(this.initialQuerySelfHasRun.promise, signal)
|
|
154
158
|
|
|
155
159
|
this.initialQuerySelfHasRun = undefined
|
|
156
160
|
}
|
|
@@ -192,12 +196,14 @@ export class QueryManager implements Startable {
|
|
|
192
196
|
|
|
193
197
|
// Execute the query along each disjoint path and yield their results as they become available
|
|
194
198
|
for await (const event of merge(...paths)) {
|
|
195
|
-
yield event
|
|
196
|
-
|
|
197
199
|
if (event.name === 'QUERY_ERROR') {
|
|
198
|
-
log('error', event.error)
|
|
200
|
+
log.error('query error', event.error)
|
|
199
201
|
}
|
|
202
|
+
|
|
203
|
+
yield event
|
|
200
204
|
}
|
|
205
|
+
|
|
206
|
+
queryFinished = true
|
|
201
207
|
} catch (err: any) {
|
|
202
208
|
if (!this.running && err.code === 'ERR_QUERY_ABORTED') {
|
|
203
209
|
// ignore query aborted errors that were thrown during query manager shutdown
|
|
@@ -205,6 +211,11 @@ export class QueryManager implements Startable {
|
|
|
205
211
|
throw err
|
|
206
212
|
}
|
|
207
213
|
} finally {
|
|
214
|
+
if (!queryFinished) {
|
|
215
|
+
log('query exited early')
|
|
216
|
+
queryEarlyExitController.abort()
|
|
217
|
+
}
|
|
218
|
+
|
|
208
219
|
signal.clear()
|
|
209
220
|
|
|
210
221
|
this.queries--
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CodeError, TypedEventEmitter } from '@libp2p/interface'
|
|
2
2
|
import { PeerSet } from '@libp2p/peer-collections'
|
|
3
|
-
import {
|
|
3
|
+
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
|
4
4
|
import { pbStream } from 'it-protobuf-stream'
|
|
5
5
|
import { Message, MessageType } from '../message/dht.js'
|
|
6
6
|
import * as utils from '../utils.js'
|
|
@@ -44,7 +44,7 @@ export interface RoutingTableEvents {
|
|
|
44
44
|
export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implements Startable {
|
|
45
45
|
public kBucketSize: number
|
|
46
46
|
public kb?: KBucket
|
|
47
|
-
public pingQueue:
|
|
47
|
+
public pingQueue: PeerQueue<boolean>
|
|
48
48
|
|
|
49
49
|
private readonly log: Logger
|
|
50
50
|
private readonly components: RoutingTableComponents
|
|
@@ -56,8 +56,6 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
56
56
|
private readonly tagValue: number
|
|
57
57
|
private readonly metrics?: {
|
|
58
58
|
routingTableSize: Metric
|
|
59
|
-
pingQueueSize: Metric
|
|
60
|
-
pingRunning: Metric
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
constructor (components: RoutingTableComponents, init: RoutingTableInit) {
|
|
@@ -75,23 +73,18 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
75
73
|
this.tagName = tagName ?? KAD_CLOSE_TAG_NAME
|
|
76
74
|
this.tagValue = tagValue ?? KAD_CLOSE_TAG_VALUE
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
this.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.pingQueue
|
|
84
|
-
|
|
85
|
-
this.pingQueue.addListener('next', updatePingQueueSizeMetric)
|
|
86
|
-
this.pingQueue.addListener('error', err => {
|
|
87
|
-
this.log.error('error pinging peer', err)
|
|
76
|
+
this.pingQueue = new PeerQueue({
|
|
77
|
+
concurrency: this.pingConcurrency,
|
|
78
|
+
metricName: `${logPrefix.replaceAll(':', '_')}_ping_queue`,
|
|
79
|
+
metrics: this.components.metrics
|
|
80
|
+
})
|
|
81
|
+
this.pingQueue.addEventListener('error', evt => {
|
|
82
|
+
this.log.error('error pinging peer', evt.detail)
|
|
88
83
|
})
|
|
89
84
|
|
|
90
85
|
if (this.components.metrics != null) {
|
|
91
86
|
this.metrics = {
|
|
92
|
-
routingTableSize: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_routing_table_size`)
|
|
93
|
-
pingQueueSize: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_ping_queue_size`),
|
|
94
|
-
pingRunning: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_ping_running`)
|
|
87
|
+
routingTableSize: this.components.metrics.registerMetric(`${logPrefix.replaceAll(':', '_')}_routing_table_size`)
|
|
95
88
|
}
|
|
96
89
|
}
|
|
97
90
|
}
|
|
@@ -204,8 +197,10 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
204
197
|
const results = await Promise.all(
|
|
205
198
|
oldContacts.map(async oldContact => {
|
|
206
199
|
// if a previous ping wants us to ping this contact, re-use the result
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
const pingJob = this.pingQueue.find(oldContact.peer)
|
|
201
|
+
|
|
202
|
+
if (pingJob != null) {
|
|
203
|
+
return pingJob.join()
|
|
209
204
|
}
|
|
210
205
|
|
|
211
206
|
return this.pingQueue.add(async () => {
|