@libp2p/kad-dht 15.1.2 → 15.1.3-ae595d8db
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 +17 -17
- 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
package/src/providers.ts
CHANGED
|
@@ -3,13 +3,11 @@ import * as varint from 'uint8-varint'
|
|
|
3
3
|
import { parseProviderKey, readProviderTime, toProviderKey } from './utils.js'
|
|
4
4
|
import type { AbortOptions, ComponentLogger, Logger, Metrics, PeerId } from '@libp2p/interface'
|
|
5
5
|
import type { Datastore } from 'interface-datastore'
|
|
6
|
-
import type { Mortice } from 'mortice'
|
|
7
6
|
import type { CID } from 'multiformats'
|
|
8
7
|
|
|
9
8
|
export interface ProvidersInit {
|
|
10
9
|
logPrefix: string
|
|
11
10
|
datastorePrefix: string
|
|
12
|
-
lock: Mortice
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
export interface ProvidersComponents {
|
|
@@ -18,6 +16,10 @@ export interface ProvidersComponents {
|
|
|
18
16
|
metrics?: Metrics
|
|
19
17
|
}
|
|
20
18
|
|
|
19
|
+
interface WriteProviderEntryOptions extends AbortOptions {
|
|
20
|
+
time?: Date
|
|
21
|
+
}
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* Provides a mechanism to add and remove provider records from the datastore
|
|
23
25
|
*/
|
|
@@ -25,67 +27,47 @@ export class Providers {
|
|
|
25
27
|
private readonly log: Logger
|
|
26
28
|
private readonly datastore: Datastore
|
|
27
29
|
private readonly datastorePrefix: string
|
|
28
|
-
private readonly lock: Mortice
|
|
29
30
|
|
|
30
31
|
constructor (components: ProvidersComponents, init: ProvidersInit) {
|
|
31
32
|
this.log = components.logger.forComponent(`${init.logPrefix}:providers`)
|
|
32
33
|
this.datastorePrefix = `${init.datastorePrefix}/provider`
|
|
33
34
|
this.datastore = components.datastore
|
|
34
|
-
this.lock = init.lock
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Add a new provider for the given CID
|
|
39
39
|
*/
|
|
40
40
|
async addProvider (cid: CID, provider: PeerId, options?: AbortOptions): Promise<void> {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
this.log.trace('%p provides %s', provider, cid)
|
|
45
|
-
await this.writeProviderEntry(cid, provider)
|
|
46
|
-
} finally {
|
|
47
|
-
release()
|
|
48
|
-
}
|
|
41
|
+
this.log.trace('%p provides %s', provider, cid)
|
|
42
|
+
await this.writeProviderEntry(cid, provider, options)
|
|
49
43
|
}
|
|
50
44
|
|
|
51
45
|
/**
|
|
52
46
|
* Remove a provider for the given CID
|
|
53
47
|
*/
|
|
54
48
|
async removeProvider (cid: CID, provider: PeerId, options?: AbortOptions): Promise<void> {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const key = toProviderKey(this.datastorePrefix, cid, provider)
|
|
59
|
-
this.log.trace('%p no longer provides %s', provider, cid)
|
|
60
|
-
await this.datastore.delete(key)
|
|
61
|
-
} finally {
|
|
62
|
-
release()
|
|
63
|
-
}
|
|
49
|
+
const key = toProviderKey(this.datastorePrefix, cid, provider)
|
|
50
|
+
this.log.trace('%p no longer provides %s', provider, cid)
|
|
51
|
+
await this.datastore.delete(key, options)
|
|
64
52
|
}
|
|
65
53
|
|
|
66
54
|
/**
|
|
67
55
|
* Get a list of providers for the given CID
|
|
68
56
|
*/
|
|
69
57
|
async getProviders (cid: CID, options?: AbortOptions): Promise<PeerId[]> {
|
|
70
|
-
|
|
58
|
+
this.log.trace('get providers for %c', cid)
|
|
59
|
+
const provs = await this.loadProviders(cid, options)
|
|
60
|
+
this.log.trace('got %d providers for %c', provs.size, cid)
|
|
71
61
|
|
|
72
|
-
|
|
73
|
-
this.log.trace('get providers for %c', cid)
|
|
74
|
-
const provs = await this.loadProviders(cid, options)
|
|
75
|
-
this.log.trace('got %d providers for %c', provs.size, cid)
|
|
76
|
-
|
|
77
|
-
return [...provs.keys()]
|
|
78
|
-
} finally {
|
|
79
|
-
release()
|
|
80
|
-
}
|
|
62
|
+
return [...provs.keys()]
|
|
81
63
|
}
|
|
82
64
|
|
|
83
65
|
/**
|
|
84
66
|
* Write a provider into the given store
|
|
85
67
|
*/
|
|
86
|
-
private async writeProviderEntry (cid: CID, peerId: PeerId,
|
|
68
|
+
private async writeProviderEntry (cid: CID, peerId: PeerId, options?: WriteProviderEntryOptions): Promise<void> {
|
|
87
69
|
const key = toProviderKey(this.datastorePrefix, cid, peerId)
|
|
88
|
-
const buffer = varint.encode(time
|
|
70
|
+
const buffer = varint.encode(options?.time?.getTime() ?? Date.now())
|
|
89
71
|
|
|
90
72
|
await this.datastore.put(key, buffer, options)
|
|
91
73
|
}
|
package/src/query/manager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { setMaxListeners } from '@libp2p/interface'
|
|
2
1
|
import { createScalableCuckooFilter } from '@libp2p/utils/filters'
|
|
3
2
|
import { anySignal } from 'any-signal'
|
|
4
3
|
import merge from 'it-merge'
|
|
4
|
+
import { setMaxListeners } from 'main-event'
|
|
5
5
|
import { pEvent } from 'p-event'
|
|
6
6
|
import { raceSignal } from 'race-signal'
|
|
7
7
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
@@ -165,8 +165,12 @@ export class QueryManager implements Startable {
|
|
|
165
165
|
|
|
166
166
|
log('query:start')
|
|
167
167
|
|
|
168
|
-
const id = await convertBuffer(key
|
|
169
|
-
|
|
168
|
+
const id = await convertBuffer(key, {
|
|
169
|
+
signal
|
|
170
|
+
})
|
|
171
|
+
const peers = this.routingTable.closestPeers(id, {
|
|
172
|
+
count: this.routingTable.kBucketSize
|
|
173
|
+
})
|
|
170
174
|
|
|
171
175
|
// split peers into d buckets evenly(ish)
|
|
172
176
|
const peersToQuery = peers.sort(() => {
|
package/src/query/query-path.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { AbortError } from '@libp2p/interface'
|
|
1
2
|
import { Queue } from '@libp2p/utils/queue'
|
|
2
3
|
import { pushable } from 'it-pushable'
|
|
3
4
|
import { xor as uint8ArrayXor } from 'uint8arrays/xor'
|
|
4
5
|
import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare'
|
|
5
|
-
import { QueryAbortedError } from '../errors.js'
|
|
6
6
|
import { convertPeerId, convertBuffer } from '../utils.js'
|
|
7
7
|
import { pathEndedEvent, queryErrorEvent } from './events.js'
|
|
8
8
|
import type { QueryEvent } from '../index.js'
|
|
@@ -106,11 +106,13 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
|
|
|
106
106
|
|
|
107
107
|
signal.addEventListener('abort', () => {
|
|
108
108
|
queue.abort()
|
|
109
|
-
events.end(new
|
|
109
|
+
events.end(new AbortError())
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
// perform lookups on kadId, not the actual value
|
|
113
|
-
const kadId = await convertBuffer(key
|
|
113
|
+
const kadId = await convertBuffer(key, {
|
|
114
|
+
signal
|
|
115
|
+
})
|
|
114
116
|
|
|
115
117
|
/**
|
|
116
118
|
* Adds the passed peer to the query queue if it's not us and no other path
|
|
@@ -159,7 +161,9 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
|
|
|
159
161
|
continue
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
const closerPeerKadId = await convertPeerId(closerPeer.id
|
|
164
|
+
const closerPeerKadId = await convertPeerId(closerPeer.id, {
|
|
165
|
+
signal
|
|
166
|
+
})
|
|
163
167
|
const closerPeerXor = uint8ArrayXor(closerPeerKadId, kadId)
|
|
164
168
|
|
|
165
169
|
// only continue query if closer peer is actually closer
|
|
@@ -206,7 +210,9 @@ export async function * queryPath (options: QueryPathOptions): AsyncGenerator<Qu
|
|
|
206
210
|
// begin the query with the starting peers
|
|
207
211
|
await Promise.all(
|
|
208
212
|
startingPeers.map(async startingPeer => {
|
|
209
|
-
queryPeer({ id: startingPeer, multiaddrs: [] }, await convertPeerId(startingPeer
|
|
213
|
+
queryPeer({ id: startingPeer, multiaddrs: [] }, await convertPeerId(startingPeer, {
|
|
214
|
+
signal
|
|
215
|
+
}))
|
|
210
216
|
})
|
|
211
217
|
)
|
|
212
218
|
|
package/src/query-self.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { setMaxListeners } from '@libp2p/interface'
|
|
2
1
|
import { anySignal } from 'any-signal'
|
|
3
2
|
import length from 'it-length'
|
|
4
3
|
import { pipe } from 'it-pipe'
|
|
5
4
|
import take from 'it-take'
|
|
5
|
+
import { setMaxListeners } from 'main-event'
|
|
6
6
|
import pDefer from 'p-defer'
|
|
7
7
|
import { QUERY_SELF_INTERVAL, QUERY_SELF_TIMEOUT, K, QUERY_SELF_INITIAL_INTERVAL } from './constants.js'
|
|
8
8
|
import { timeOperationMethod } from './utils.js'
|
|
@@ -133,6 +133,8 @@ export class QuerySelf implements Startable {
|
|
|
133
133
|
async (source) => length(source)
|
|
134
134
|
)
|
|
135
135
|
|
|
136
|
+
signal?.throwIfAborted()
|
|
137
|
+
|
|
136
138
|
const duration = Date.now() - start
|
|
137
139
|
|
|
138
140
|
this.log('self-query found %d peers in %dms', peers, duration)
|
package/src/record/validators.ts
CHANGED
|
@@ -4,13 +4,14 @@ import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
|
4
4
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
5
5
|
import type { Validators } from '../index.js'
|
|
6
6
|
import type { Libp2pRecord } from '@libp2p/record'
|
|
7
|
+
import type { AbortOptions } from 'it-pushable'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Checks a record and ensures it is still valid.
|
|
10
11
|
* It runs the needed validators.
|
|
11
12
|
* If verification fails the returned Promise will reject with the error.
|
|
12
13
|
*/
|
|
13
|
-
export async function verifyRecord (validators: Validators, record: Libp2pRecord): Promise<void> {
|
|
14
|
+
export async function verifyRecord (validators: Validators, record: Libp2pRecord, options?: AbortOptions): Promise<void> {
|
|
14
15
|
const key = record.key
|
|
15
16
|
const keyString = uint8ArrayToString(key)
|
|
16
17
|
const parts = keyString.split('/')
|
|
@@ -26,7 +27,7 @@ export async function verifyRecord (validators: Validators, record: Libp2pRecord
|
|
|
26
27
|
throw new InvalidParametersError(`No validator available for key type "${parts[1]}"`)
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
await validator(key, record.value)
|
|
30
|
+
await validator(key, record.value, options)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -38,7 +39,7 @@ export async function verifyRecord (validators: Validators, record: Libp2pRecord
|
|
|
38
39
|
* @param {Uint8Array} key - A valid key is of the form `'/pk/<keymultihash>'`
|
|
39
40
|
* @param {Uint8Array} publicKey - The public key to validate against (protobuf encoded).
|
|
40
41
|
*/
|
|
41
|
-
const validatePublicKeyRecord = async (key: Uint8Array, publicKey: Uint8Array): Promise<void> => {
|
|
42
|
+
const validatePublicKeyRecord = async (key: Uint8Array, publicKey: Uint8Array, options?: AbortOptions): Promise<void> => {
|
|
42
43
|
if (!(key instanceof Uint8Array)) {
|
|
43
44
|
throw new InvalidParametersError('"key" must be a Uint8Array')
|
|
44
45
|
}
|
package/src/reprovider.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
|
2
1
|
import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout'
|
|
3
2
|
import { Queue } from '@libp2p/utils/queue'
|
|
4
3
|
import drain from 'it-drain'
|
|
5
|
-
import {
|
|
4
|
+
import { TypedEventEmitter, setMaxListeners } from 'main-event'
|
|
5
|
+
import { PROVIDERS_VALIDITY, REPROVIDE_CONCURRENCY, REPROVIDE_INTERVAL, REPROVIDE_MAX_QUEUE_SIZE, REPROVIDE_THRESHOLD, REPROVIDE_TIMEOUT } from './constants.js'
|
|
6
6
|
import { parseProviderKey, readProviderTime, timeOperationMethod } from './utils.js'
|
|
7
7
|
import type { ContentRouting } from './content-routing/index.js'
|
|
8
8
|
import type { OperationMetrics } from './kad-dht.js'
|
|
@@ -10,7 +10,6 @@ import type { AbortOptions, ComponentLogger, Logger, Metrics, PeerId } from '@li
|
|
|
10
10
|
import type { AddressManager } from '@libp2p/interface-internal'
|
|
11
11
|
import type { AdaptiveTimeoutInit } from '@libp2p/utils/adaptive-timeout'
|
|
12
12
|
import type { Datastore } from 'interface-datastore'
|
|
13
|
-
import type { Mortice } from 'mortice'
|
|
14
13
|
import type { CID } from 'multiformats/cid'
|
|
15
14
|
|
|
16
15
|
export interface ReproviderComponents {
|
|
@@ -26,7 +25,6 @@ export interface ReproviderInit {
|
|
|
26
25
|
metricsPrefix: string
|
|
27
26
|
datastorePrefix: string
|
|
28
27
|
contentRouting: ContentRouting
|
|
29
|
-
lock: Mortice
|
|
30
28
|
operationMetrics: OperationMetrics
|
|
31
29
|
concurrency?: number
|
|
32
30
|
maxQueueSize?: number
|
|
@@ -60,7 +58,6 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
60
58
|
private readonly addressManager: AddressManager
|
|
61
59
|
private readonly validity: number
|
|
62
60
|
private readonly interval: number
|
|
63
|
-
private readonly lock: Mortice
|
|
64
61
|
private readonly peerId: PeerId
|
|
65
62
|
|
|
66
63
|
constructor (components: ReproviderComponents, init: ReproviderInit) {
|
|
@@ -86,7 +83,6 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
86
83
|
this.validity = init.validity ?? PROVIDERS_VALIDITY
|
|
87
84
|
this.interval = init.interval ?? REPROVIDE_INTERVAL
|
|
88
85
|
this.contentRouting = init.contentRouting
|
|
89
|
-
this.lock = init.lock
|
|
90
86
|
this.running = false
|
|
91
87
|
|
|
92
88
|
this.reprovide = timeOperationMethod(this.reprovide.bind(this), init.operationMetrics, 'PROVIDE')
|
|
@@ -103,7 +99,9 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
103
99
|
setMaxListeners(Infinity, this.shutdownController.signal)
|
|
104
100
|
|
|
105
101
|
this.timeout = setTimeout(() => {
|
|
106
|
-
this.cleanUp(
|
|
102
|
+
this.cleanUp({
|
|
103
|
+
signal: AbortSignal.timeout(REPROVIDE_TIMEOUT)
|
|
104
|
+
}).catch(err => {
|
|
107
105
|
this.log.error('error running reprovide/cleanup - %e', err)
|
|
108
106
|
})
|
|
109
107
|
}, this.interval)
|
|
@@ -120,16 +118,14 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
120
118
|
* Check all provider records. Delete them if they have expired, reprovide
|
|
121
119
|
* them if the provider is us and the expiry is within the reprovide window.
|
|
122
120
|
*/
|
|
123
|
-
private async cleanUp (): Promise<void> {
|
|
124
|
-
const release = await this.lock.writeLock()
|
|
125
|
-
|
|
121
|
+
private async cleanUp (options?: AbortOptions): Promise<void> {
|
|
126
122
|
try {
|
|
127
123
|
this.safeDispatchEvent('reprovide:start')
|
|
128
124
|
|
|
129
125
|
// Get all provider entries from the datastore
|
|
130
126
|
for await (const entry of this.datastore.query({
|
|
131
127
|
prefix: this.datastorePrefix
|
|
132
|
-
})) {
|
|
128
|
+
}, options)) {
|
|
133
129
|
try {
|
|
134
130
|
// Add a delete to the batch for each expired entry
|
|
135
131
|
const { cid, peerId } = parseProviderKey(entry.key)
|
|
@@ -142,7 +138,7 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
142
138
|
|
|
143
139
|
// delete the record if it has expired
|
|
144
140
|
if (expired) {
|
|
145
|
-
await this.datastore.delete(entry.key)
|
|
141
|
+
await this.datastore.delete(entry.key, options)
|
|
146
142
|
}
|
|
147
143
|
|
|
148
144
|
// if the provider is us and we are within the reprovide threshold,
|
|
@@ -160,12 +156,13 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
160
156
|
|
|
161
157
|
this.log('reprovide/cleanup successful')
|
|
162
158
|
} finally {
|
|
163
|
-
release()
|
|
164
159
|
this.safeDispatchEvent('reprovide:end')
|
|
165
160
|
|
|
166
161
|
if (this.running) {
|
|
167
162
|
this.timeout = setTimeout(() => {
|
|
168
|
-
this.cleanUp(
|
|
163
|
+
this.cleanUp({
|
|
164
|
+
signal: AbortSignal.timeout(REPROVIDE_TIMEOUT)
|
|
165
|
+
}).catch(err => {
|
|
169
166
|
this.log.error('error running re-provide - %e', err)
|
|
170
167
|
})
|
|
171
168
|
}, this.interval)
|
|
@@ -173,13 +170,13 @@ export class Reprovider extends TypedEventEmitter<ReprovideEvents> {
|
|
|
173
170
|
}
|
|
174
171
|
}
|
|
175
172
|
|
|
176
|
-
private async queueReprovide (cid: CID): Promise<void> {
|
|
173
|
+
private async queueReprovide (cid: CID, options?: AbortOptions): Promise<void> {
|
|
177
174
|
if (!this.running) {
|
|
178
175
|
return
|
|
179
176
|
}
|
|
180
177
|
|
|
181
178
|
this.log.trace('waiting for queue capacity before adding %c to re-provide queue', cid)
|
|
182
|
-
await this.reprovideQueue.onSizeLessThan(this.maxQueueSize)
|
|
179
|
+
await this.reprovideQueue.onSizeLessThan(this.maxQueueSize, options)
|
|
183
180
|
|
|
184
181
|
const existingJob = this.reprovideQueue.queue.find(job => job.options.cid.equals(cid))
|
|
185
182
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { start, stop } from '@libp2p/interface'
|
|
2
2
|
import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout'
|
|
3
3
|
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
|
4
4
|
import { anySignal } from 'any-signal'
|
|
5
5
|
import parallel from 'it-parallel'
|
|
6
|
+
import { TypedEventEmitter, setMaxListeners } from 'main-event'
|
|
6
7
|
import * as utils from '../utils.js'
|
|
7
8
|
import { ClosestPeers } from './closest-peers.js'
|
|
8
9
|
import { KBucket, isLeafBucket } from './k-bucket.js'
|
|
9
|
-
import type { Bucket, LeafBucket, Peer } from './k-bucket.js'
|
|
10
|
+
import type { Bucket, GetClosestPeersOptions, LeafBucket, Peer } from './k-bucket.js'
|
|
10
11
|
import type { Network } from '../network.js'
|
|
11
12
|
import type { AbortOptions, ComponentLogger, CounterGroup, Logger, Metric, Metrics, PeerId, PeerStore, Startable, Stream } from '@libp2p/interface'
|
|
12
13
|
import type { Ping } from '@libp2p/ping'
|
|
@@ -97,6 +98,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
97
98
|
kadBucketEvents: CounterGroup<'ping_old_contact' | 'ping_old_contact_error' | 'ping_new_contact' | 'ping_new_contact_error' | 'peer_added' | 'peer_removed'>
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
private shutdownController: AbortController
|
|
102
|
+
|
|
100
103
|
constructor (components: RoutingTableComponents, init: RoutingTableInit) {
|
|
101
104
|
super()
|
|
102
105
|
|
|
@@ -114,6 +117,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
114
117
|
this.peerRemoved = this.peerRemoved.bind(this)
|
|
115
118
|
this.populateFromDatastoreOnStart = init.populateFromDatastoreOnStart ?? POPULATE_FROM_DATASTORE_ON_START
|
|
116
119
|
this.populateFromDatastoreLimit = init.populateFromDatastoreLimit ?? POPULATE_FROM_DATASTORE_LIMIT
|
|
120
|
+
this.shutdownController = new AbortController()
|
|
117
121
|
|
|
118
122
|
this.pingOldContactQueue = new PeerQueue({
|
|
119
123
|
concurrency: init.pingOldContactConcurrency ?? PING_OLD_CONTACT_CONCURRENCY,
|
|
@@ -139,7 +143,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
139
143
|
metricName: `${init.metricsPrefix}_routing_table_ping_new_contact_time_milliseconds`
|
|
140
144
|
})
|
|
141
145
|
|
|
142
|
-
this.kb = new KBucket({
|
|
146
|
+
this.kb = new KBucket(components, {
|
|
143
147
|
kBucketSize: init.kBucketSize,
|
|
144
148
|
prefixLength: init.prefixLength,
|
|
145
149
|
splitThreshold: init.splitThreshold,
|
|
@@ -148,7 +152,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
148
152
|
ping: this.pingOldContacts,
|
|
149
153
|
verify: this.verifyNewContact,
|
|
150
154
|
onAdd: this.peerAdded,
|
|
151
|
-
onRemove: this.peerRemoved
|
|
155
|
+
onRemove: this.peerRemoved,
|
|
156
|
+
metricsPrefix: init.metricsPrefix
|
|
152
157
|
})
|
|
153
158
|
|
|
154
159
|
this.closestPeerTagger = new ClosestPeers(this.components, {
|
|
@@ -184,11 +189,13 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
184
189
|
|
|
185
190
|
this.running = true
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
await this.
|
|
192
|
+
this.shutdownController = new AbortController()
|
|
193
|
+
await start(this.closestPeerTagger, this.kb)
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
async afterStart (): Promise<void> {
|
|
197
|
+
let peerStorePeers = 0
|
|
198
|
+
|
|
192
199
|
// do this async to not block startup but iterate serially to not overwhelm
|
|
193
200
|
// the ping queue
|
|
194
201
|
Promise.resolve().then(async () => {
|
|
@@ -196,48 +203,60 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
196
203
|
return
|
|
197
204
|
}
|
|
198
205
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
const signal = anySignal([
|
|
207
|
+
this.shutdownController.signal,
|
|
208
|
+
AbortSignal.timeout(20_000)
|
|
209
|
+
])
|
|
210
|
+
setMaxListeners(Infinity, signal)
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
// add existing peers from the peer store to routing table
|
|
214
|
+
for (const peer of await this.components.peerStore.all({
|
|
215
|
+
filters: [(peer) => {
|
|
216
|
+
return peer.protocols.includes(this.protocol) && peer.tags.has(KAD_PEER_TAG_NAME)
|
|
217
|
+
}],
|
|
218
|
+
limit: this.populateFromDatastoreLimit,
|
|
219
|
+
signal
|
|
220
|
+
})) {
|
|
221
|
+
if (!this.running) {
|
|
222
|
+
// bail if we've been shut down
|
|
223
|
+
return
|
|
224
|
+
}
|
|
212
225
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
tags
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
try {
|
|
227
|
+
await this.add(peer.id, {
|
|
228
|
+
signal
|
|
229
|
+
})
|
|
230
|
+
peerStorePeers++
|
|
231
|
+
} catch (err) {
|
|
232
|
+
this.log('failed to add peer %p to routing table, removing kad-dht peer tags - %e')
|
|
233
|
+
await this.components.peerStore.merge(peer.id, {
|
|
234
|
+
tags: {
|
|
235
|
+
[this.peerTagName]: undefined
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
}
|
|
223
239
|
}
|
|
240
|
+
} finally {
|
|
241
|
+
signal.clear()
|
|
224
242
|
}
|
|
225
243
|
|
|
226
244
|
this.log('added %d peer store peers to the routing table', peerStorePeers)
|
|
227
245
|
})
|
|
228
246
|
.catch(err => {
|
|
229
|
-
this.log.error('error adding peer store peers to the routing table %e', err)
|
|
247
|
+
this.log.error('error adding %d, peer store peers to the routing table - %e', peerStorePeers, err)
|
|
230
248
|
})
|
|
231
249
|
}
|
|
232
250
|
|
|
233
251
|
async stop (): Promise<void> {
|
|
234
252
|
this.running = false
|
|
235
|
-
await stop(this.closestPeerTagger)
|
|
253
|
+
await stop(this.closestPeerTagger, this.kb)
|
|
236
254
|
this.pingOldContactQueue.abort()
|
|
237
255
|
this.pingNewContactQueue.abort()
|
|
256
|
+
this.shutdownController.abort()
|
|
238
257
|
}
|
|
239
258
|
|
|
240
|
-
private async peerAdded (peer: Peer, bucket: LeafBucket): Promise<void> {
|
|
259
|
+
private async peerAdded (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void> {
|
|
241
260
|
if (!this.components.peerId.equals(peer.peerId)) {
|
|
242
261
|
await this.components.peerStore.merge(peer.peerId, {
|
|
243
262
|
tags: {
|
|
@@ -245,7 +264,7 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
245
264
|
value: this.peerTagValue
|
|
246
265
|
}
|
|
247
266
|
}
|
|
248
|
-
})
|
|
267
|
+
}, options)
|
|
249
268
|
}
|
|
250
269
|
|
|
251
270
|
this.updateMetrics()
|
|
@@ -253,13 +272,13 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
253
272
|
this.safeDispatchEvent('peer:add', { detail: peer.peerId })
|
|
254
273
|
}
|
|
255
274
|
|
|
256
|
-
private async peerRemoved (peer: Peer, bucket: LeafBucket): Promise<void> {
|
|
275
|
+
private async peerRemoved (peer: Peer, bucket: LeafBucket, options?: AbortOptions): Promise<void> {
|
|
257
276
|
if (!this.components.peerId.equals(peer.peerId)) {
|
|
258
277
|
await this.components.peerStore.merge(peer.peerId, {
|
|
259
278
|
tags: {
|
|
260
279
|
[this.peerTagName]: undefined
|
|
261
280
|
}
|
|
262
|
-
})
|
|
281
|
+
}, options)
|
|
263
282
|
}
|
|
264
283
|
|
|
265
284
|
this.updateMetrics()
|
|
@@ -309,7 +328,11 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
309
328
|
|
|
310
329
|
const result = await this.pingOldContactQueue.add(async (options) => {
|
|
311
330
|
const signal = this.pingOldContactTimeout.getTimeoutSignal()
|
|
312
|
-
const signals = anySignal([
|
|
331
|
+
const signals = anySignal([
|
|
332
|
+
signal,
|
|
333
|
+
this.shutdownController.signal,
|
|
334
|
+
options?.signal
|
|
335
|
+
])
|
|
313
336
|
setMaxListeners(Infinity, signal, signals)
|
|
314
337
|
|
|
315
338
|
try {
|
|
@@ -341,7 +364,11 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
341
364
|
|
|
342
365
|
async verifyNewContact (contact: Peer, options?: AbortOptions): Promise<boolean> {
|
|
343
366
|
const signal = this.pingNewContactTimeout.getTimeoutSignal()
|
|
344
|
-
const signals = anySignal([
|
|
367
|
+
const signals = anySignal([
|
|
368
|
+
signal,
|
|
369
|
+
this.shutdownController.signal,
|
|
370
|
+
options?.signal
|
|
371
|
+
])
|
|
345
372
|
setMaxListeners(Infinity, signal, signals)
|
|
346
373
|
|
|
347
374
|
try {
|
|
@@ -408,8 +435,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
408
435
|
/**
|
|
409
436
|
* Find a specific peer by id
|
|
410
437
|
*/
|
|
411
|
-
async find (peer: PeerId): Promise<PeerId | undefined> {
|
|
412
|
-
const kadId = await utils.convertPeerId(peer)
|
|
438
|
+
async find (peer: PeerId, options?: AbortOptions): Promise<PeerId | undefined> {
|
|
439
|
+
const kadId = await utils.convertPeerId(peer, options)
|
|
413
440
|
return this.kb.get(kadId)?.peerId
|
|
414
441
|
}
|
|
415
442
|
|
|
@@ -417,7 +444,9 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
417
444
|
* Retrieve the closest peers to the given kadId
|
|
418
445
|
*/
|
|
419
446
|
closestPeer (kadId: Uint8Array): PeerId | undefined {
|
|
420
|
-
const res = this.closestPeers(kadId,
|
|
447
|
+
const res = this.closestPeers(kadId, {
|
|
448
|
+
count: 1
|
|
449
|
+
})
|
|
421
450
|
|
|
422
451
|
if (res.length > 0) {
|
|
423
452
|
return res[0]
|
|
@@ -429,12 +458,12 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
429
458
|
/**
|
|
430
459
|
* Retrieve the `count`-closest peers to the given kadId
|
|
431
460
|
*/
|
|
432
|
-
closestPeers (kadId: Uint8Array,
|
|
461
|
+
closestPeers (kadId: Uint8Array, options?: GetClosestPeersOptions): PeerId[] {
|
|
433
462
|
if (this.kb == null) {
|
|
434
463
|
return []
|
|
435
464
|
}
|
|
436
465
|
|
|
437
|
-
return [...this.kb.closest(kadId,
|
|
466
|
+
return [...this.kb.closest(kadId, options)]
|
|
438
467
|
}
|
|
439
468
|
|
|
440
469
|
/**
|
|
@@ -451,14 +480,14 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
451
480
|
/**
|
|
452
481
|
* Remove a given peer from the table
|
|
453
482
|
*/
|
|
454
|
-
async remove (peer: PeerId): Promise<void> {
|
|
483
|
+
async remove (peer: PeerId, options?: AbortOptions): Promise<void> {
|
|
455
484
|
if (this.kb == null) {
|
|
456
485
|
throw new Error('RoutingTable is not started')
|
|
457
486
|
}
|
|
458
487
|
|
|
459
|
-
const kadId = await utils.convertPeerId(peer)
|
|
488
|
+
const kadId = await utils.convertPeerId(peer, options)
|
|
460
489
|
|
|
461
|
-
await this.kb.remove(kadId)
|
|
490
|
+
await this.kb.remove(kadId, options)
|
|
462
491
|
}
|
|
463
492
|
|
|
464
493
|
private updateMetrics (): void {
|