@libp2p/kad-dht 13.1.2-b4f02a637 → 14.0.0-934a891f9
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.min.js +2 -2
- package/dist/src/index.d.ts +26 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/kad-dht.d.ts.map +1 -1
- package/dist/src/kad-dht.js +25 -13
- 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 +1 -3
- package/dist/src/network.js.map +1 -1
- package/dist/src/peer-distance-list.d.ts.map +1 -0
- package/dist/src/{peer-list/peer-distance-list.js → peer-distance-list.js} +1 -1
- package/dist/src/peer-distance-list.js.map +1 -0
- package/dist/src/peer-routing/index.js +1 -1
- package/dist/src/peer-routing/index.js.map +1 -1
- package/dist/src/query-self.d.ts.map +1 -1
- package/dist/src/query-self.js +13 -6
- package/dist/src/query-self.js.map +1 -1
- package/dist/src/routing-table/closest-peers.d.ts +43 -0
- package/dist/src/routing-table/closest-peers.d.ts.map +1 -0
- package/dist/src/routing-table/closest-peers.js +86 -0
- package/dist/src/routing-table/closest-peers.js.map +1 -0
- package/dist/src/routing-table/index.d.ts +54 -32
- package/dist/src/routing-table/index.d.ts.map +1 -1
- package/dist/src/routing-table/index.js +256 -172
- package/dist/src/routing-table/index.js.map +1 -1
- package/dist/src/routing-table/k-bucket.d.ts +65 -21
- package/dist/src/routing-table/k-bucket.d.ts.map +1 -1
- package/dist/src/routing-table/k-bucket.js +122 -66
- package/dist/src/routing-table/k-bucket.js.map +1 -1
- package/dist/src/routing-table/refresh.d.ts.map +1 -1
- package/dist/src/routing-table/refresh.js +4 -1
- package/dist/src/routing-table/refresh.js.map +1 -1
- package/dist/src/rpc/index.d.ts.map +1 -1
- package/dist/src/rpc/index.js +3 -7
- package/dist/src/rpc/index.js.map +1 -1
- package/package.json +11 -12
- package/src/index.ts +30 -2
- package/src/kad-dht.ts +29 -15
- package/src/network.ts +1 -4
- package/src/{peer-list/peer-distance-list.ts → peer-distance-list.ts} +1 -1
- package/src/peer-routing/index.ts +1 -1
- package/src/query-self.ts +14 -6
- package/src/routing-table/closest-peers.ts +113 -0
- package/src/routing-table/index.ts +296 -205
- package/src/routing-table/k-bucket.ts +194 -81
- package/src/routing-table/refresh.ts +5 -1
- package/src/rpc/index.ts +4 -7
- package/dist/src/peer-list/index.d.ts +0 -29
- package/dist/src/peer-list/index.d.ts.map +0 -1
- package/dist/src/peer-list/index.js +0 -45
- package/dist/src/peer-list/index.js.map +0 -1
- package/dist/src/peer-list/peer-distance-list.d.ts.map +0 -1
- package/dist/src/peer-list/peer-distance-list.js.map +0 -1
- package/src/peer-list/index.ts +0 -54
- /package/dist/src/{peer-list/peer-distance-list.d.ts → peer-distance-list.d.ts} +0 -0
@@ -1,21 +1,32 @@
|
|
1
|
-
import {
|
2
|
-
import { PeerSet } from '@libp2p/peer-collections'
|
1
|
+
import { TypedEventEmitter, setMaxListeners, start, stop } from '@libp2p/interface'
|
3
2
|
import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout'
|
4
3
|
import { PeerQueue } from '@libp2p/utils/peer-queue'
|
5
|
-
import {
|
6
|
-
import
|
4
|
+
import { anySignal } from 'any-signal'
|
5
|
+
import parallel from 'it-parallel'
|
6
|
+
import { EventTypes } from '../index.js'
|
7
|
+
import { MessageType } from '../message/dht.js'
|
7
8
|
import * as utils from '../utils.js'
|
8
|
-
import {
|
9
|
-
import
|
10
|
-
import type {
|
9
|
+
import { ClosestPeers } from './closest-peers.js'
|
10
|
+
import { KBucket, isLeafBucket } from './k-bucket.js'
|
11
|
+
import type { Bucket, LeafBucket, Peer } from './k-bucket.js'
|
12
|
+
import type { Network } from '../network.js'
|
13
|
+
import type { AbortOptions, ComponentLogger, CounterGroup, Logger, Metric, Metrics, PeerId, PeerStore, Startable, Stream } from '@libp2p/interface'
|
11
14
|
import type { AdaptiveTimeoutInit } from '@libp2p/utils/adaptive-timeout'
|
12
15
|
|
13
|
-
export const KAD_CLOSE_TAG_NAME = 'kad-close'
|
14
|
-
export const KAD_CLOSE_TAG_VALUE = 50
|
15
16
|
export const KBUCKET_SIZE = 20
|
16
|
-
export const PREFIX_LENGTH =
|
17
|
-
export const
|
18
|
-
export const
|
17
|
+
export const PREFIX_LENGTH = 8
|
18
|
+
export const PING_NEW_CONTACT_TIMEOUT = 2000
|
19
|
+
export const PING_NEW_CONTACT_CONCURRENCY = 20
|
20
|
+
export const PING_NEW_CONTACT_MAX_QUEUE_SIZE = 100
|
21
|
+
export const PING_OLD_CONTACT_COUNT = 3
|
22
|
+
export const PING_OLD_CONTACT_TIMEOUT = 2000
|
23
|
+
export const PING_OLD_CONTACT_CONCURRENCY = 20
|
24
|
+
export const PING_OLD_CONTACT_MAX_QUEUE_SIZE = 100
|
25
|
+
export const KAD_PEER_TAG_NAME = 'kad-peer'
|
26
|
+
export const KAD_PEER_TAG_VALUE = 1
|
27
|
+
export const LAST_PING_THRESHOLD = 600000
|
28
|
+
export const POPULATE_FROM_DATASTORE_ON_START = true
|
29
|
+
export const POPULATE_FROM_DATASTORE_LIMIT = 1000
|
19
30
|
|
20
31
|
export interface RoutingTableInit {
|
21
32
|
logPrefix: string
|
@@ -23,16 +34,28 @@ export interface RoutingTableInit {
|
|
23
34
|
prefixLength?: number
|
24
35
|
splitThreshold?: number
|
25
36
|
kBucketSize?: number
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
pingNewContactTimeout?: AdaptiveTimeoutInit
|
38
|
+
pingNewContactConcurrency?: number
|
39
|
+
pingNewContactMaxQueueSize?: number
|
40
|
+
pingOldContactTimeout?: AdaptiveTimeoutInit
|
41
|
+
pingOldContactConcurrency?: number
|
42
|
+
pingOldContactMaxQueueSize?: number
|
43
|
+
numberOfOldContactsToPing?: number
|
44
|
+
peerTagName?: string
|
45
|
+
peerTagValue?: number
|
46
|
+
closeTagName?: string
|
47
|
+
closeTagValue?: number
|
48
|
+
network: Network
|
49
|
+
populateFromDatastoreOnStart?: boolean
|
50
|
+
populateFromDatastoreLimit?: number
|
51
|
+
lastPingThreshold?: number
|
52
|
+
closestPeerSetSize?: number
|
53
|
+
closestPeerSetRefreshInterval?: number
|
30
54
|
}
|
31
55
|
|
32
56
|
export interface RoutingTableComponents {
|
33
57
|
peerId: PeerId
|
34
58
|
peerStore: PeerStore
|
35
|
-
connectionManager: ConnectionManager
|
36
59
|
metrics?: Metrics
|
37
60
|
logger: ComponentLogger
|
38
61
|
}
|
@@ -40,33 +63,37 @@ export interface RoutingTableComponents {
|
|
40
63
|
export interface RoutingTableEvents {
|
41
64
|
'peer:add': CustomEvent<PeerId>
|
42
65
|
'peer:remove': CustomEvent<PeerId>
|
66
|
+
'peer:ping': CustomEvent<PeerId>
|
43
67
|
}
|
44
68
|
|
45
69
|
/**
|
46
|
-
* A wrapper around `k-bucket`, to provide easy store and
|
47
|
-
* retrieval for peers.
|
70
|
+
* A wrapper around `k-bucket`, to provide easy store and retrieval for peers.
|
48
71
|
*/
|
49
72
|
export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implements Startable {
|
50
73
|
public kBucketSize: number
|
51
|
-
public kb
|
52
|
-
public
|
53
|
-
|
74
|
+
public kb: KBucket
|
75
|
+
public network: Network
|
76
|
+
private readonly closestPeerTagger: ClosestPeers
|
54
77
|
private readonly log: Logger
|
55
78
|
private readonly components: RoutingTableComponents
|
56
|
-
private readonly prefixLength: number
|
57
|
-
private readonly splitThreshold: number
|
58
|
-
private readonly pingTimeout: AdaptiveTimeout
|
59
|
-
private readonly pingConcurrency: number
|
60
79
|
private running: boolean
|
80
|
+
private readonly pingNewContactTimeout: AdaptiveTimeout
|
81
|
+
private readonly pingNewContactQueue: PeerQueue<boolean>
|
82
|
+
private readonly pingOldContactTimeout: AdaptiveTimeout
|
83
|
+
private readonly pingOldContactQueue: PeerQueue<boolean>
|
84
|
+
private readonly populateFromDatastoreOnStart: boolean
|
85
|
+
private readonly populateFromDatastoreLimit: number
|
61
86
|
private readonly protocol: string
|
62
|
-
private readonly
|
63
|
-
private readonly
|
87
|
+
private readonly peerTagName: string
|
88
|
+
private readonly peerTagValue: number
|
64
89
|
private readonly metrics?: {
|
65
90
|
routingTableSize: Metric
|
66
91
|
routingTableKadBucketTotal: Metric
|
67
92
|
routingTableKadBucketAverageOccupancy: Metric
|
68
93
|
routingTableKadBucketMaxDepth: Metric
|
69
|
-
|
94
|
+
routingTableKadBucketMinOccupancy: Metric
|
95
|
+
routingTableKadBucketMaxOccupancy: Metric
|
96
|
+
kadBucketEvents: CounterGroup<'ping_old_contact' | 'ping_old_contact_error' | 'ping_new_contact' | 'ping_new_contact_error' | 'peer_added' | 'peer_removed'>
|
70
97
|
}
|
71
98
|
|
72
99
|
constructor (components: RoutingTableComponents, init: RoutingTableInit) {
|
@@ -75,26 +102,61 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
75
102
|
this.components = components
|
76
103
|
this.log = components.logger.forComponent(`${init.logPrefix}:routing-table`)
|
77
104
|
this.kBucketSize = init.kBucketSize ?? KBUCKET_SIZE
|
78
|
-
this.pingConcurrency = init.pingConcurrency ?? PING_CONCURRENCY
|
79
105
|
this.running = false
|
80
106
|
this.protocol = init.protocol
|
81
|
-
this.
|
82
|
-
this.
|
83
|
-
this.
|
84
|
-
this.
|
85
|
-
|
86
|
-
this.
|
87
|
-
|
88
|
-
|
89
|
-
|
107
|
+
this.network = init.network
|
108
|
+
this.peerTagName = init.peerTagName ?? KAD_PEER_TAG_NAME
|
109
|
+
this.peerTagValue = init.peerTagValue ?? KAD_PEER_TAG_VALUE
|
110
|
+
this.pingOldContacts = this.pingOldContacts.bind(this)
|
111
|
+
this.verifyNewContact = this.verifyNewContact.bind(this)
|
112
|
+
this.peerAdded = this.peerAdded.bind(this)
|
113
|
+
this.peerRemoved = this.peerRemoved.bind(this)
|
114
|
+
this.populateFromDatastoreOnStart = init.populateFromDatastoreOnStart ?? POPULATE_FROM_DATASTORE_ON_START
|
115
|
+
this.populateFromDatastoreLimit = init.populateFromDatastoreLimit ?? POPULATE_FROM_DATASTORE_LIMIT
|
116
|
+
|
117
|
+
this.pingOldContactQueue = new PeerQueue({
|
118
|
+
concurrency: init.pingOldContactConcurrency ?? PING_OLD_CONTACT_CONCURRENCY,
|
119
|
+
metricName: `${init.logPrefix.replaceAll(':', '_')}_ping_old_contact_queue`,
|
120
|
+
metrics: this.components.metrics,
|
121
|
+
maxSize: init.pingOldContactMaxQueueSize ?? PING_OLD_CONTACT_MAX_QUEUE_SIZE
|
90
122
|
})
|
91
|
-
this.
|
92
|
-
|
123
|
+
this.pingOldContactTimeout = new AdaptiveTimeout({
|
124
|
+
...(init.pingOldContactTimeout ?? {}),
|
125
|
+
metrics: this.components.metrics,
|
126
|
+
metricName: `${init.logPrefix.replaceAll(':', '_')}_routing_table_ping_old_contact_time_milliseconds`
|
93
127
|
})
|
94
|
-
|
95
|
-
|
128
|
+
|
129
|
+
this.pingNewContactQueue = new PeerQueue({
|
130
|
+
concurrency: init.pingNewContactConcurrency ?? PING_NEW_CONTACT_CONCURRENCY,
|
131
|
+
metricName: `${init.logPrefix.replaceAll(':', '_')}_ping_new_contact_queue`,
|
96
132
|
metrics: this.components.metrics,
|
97
|
-
|
133
|
+
maxSize: init.pingNewContactMaxQueueSize ?? PING_NEW_CONTACT_MAX_QUEUE_SIZE
|
134
|
+
})
|
135
|
+
this.pingNewContactTimeout = new AdaptiveTimeout({
|
136
|
+
...(init.pingNewContactTimeout ?? {}),
|
137
|
+
metrics: this.components.metrics,
|
138
|
+
metricName: `${init.logPrefix.replaceAll(':', '_')}_routing_table_ping_new_contact_time_milliseconds`
|
139
|
+
})
|
140
|
+
|
141
|
+
this.kb = new KBucket({
|
142
|
+
kBucketSize: init.kBucketSize,
|
143
|
+
prefixLength: init.prefixLength,
|
144
|
+
splitThreshold: init.splitThreshold,
|
145
|
+
numberOfOldContactsToPing: init.numberOfOldContactsToPing,
|
146
|
+
lastPingThreshold: init.lastPingThreshold,
|
147
|
+
ping: this.pingOldContacts,
|
148
|
+
verify: this.verifyNewContact,
|
149
|
+
onAdd: this.peerAdded,
|
150
|
+
onRemove: this.peerRemoved
|
151
|
+
})
|
152
|
+
|
153
|
+
this.closestPeerTagger = new ClosestPeers(this.components, {
|
154
|
+
logPrefix: init.logPrefix,
|
155
|
+
routingTable: this,
|
156
|
+
peerSetSize: init.closestPeerSetSize,
|
157
|
+
refreshInterval: init.closestPeerSetRefreshInterval,
|
158
|
+
closeTagName: init.closeTagName,
|
159
|
+
closeTagValue: init.closeTagValue
|
98
160
|
})
|
99
161
|
|
100
162
|
if (this.components.metrics != null) {
|
@@ -102,6 +164,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
102
164
|
routingTableSize: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_size`),
|
103
165
|
routingTableKadBucketTotal: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_total`),
|
104
166
|
routingTableKadBucketAverageOccupancy: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_average_occupancy`),
|
167
|
+
routingTableKadBucketMinOccupancy: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_min_occupancy`),
|
168
|
+
routingTableKadBucketMaxOccupancy: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_max_occupancy`),
|
105
169
|
routingTableKadBucketMaxDepth: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_max_depth`),
|
106
170
|
kadBucketEvents: this.components.metrics.registerCounterGroup(`${init.logPrefix.replaceAll(':', '_')}_kad_bucket_events_total`)
|
107
171
|
}
|
@@ -115,110 +179,87 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
115
179
|
async start (): Promise<void> {
|
116
180
|
this.running = true
|
117
181
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
peerId: this.components.peerId
|
122
|
-
},
|
123
|
-
kBucketSize: this.kBucketSize,
|
124
|
-
prefixLength: this.prefixLength,
|
125
|
-
splitThreshold: this.splitThreshold,
|
126
|
-
numberOfNodesToPing: 1
|
127
|
-
})
|
128
|
-
this.kb = kBuck
|
129
|
-
|
130
|
-
// test whether to evict peers
|
131
|
-
kBuck.addEventListener('ping', (evt) => {
|
132
|
-
this.metrics?.kadBucketEvents.increment({ ping: true })
|
133
|
-
|
134
|
-
this._onPing(evt).catch(err => {
|
135
|
-
this.metrics?.kadBucketEvents.increment({ ping_error: true })
|
136
|
-
this.log.error('could not process k-bucket ping event', err)
|
137
|
-
})
|
138
|
-
})
|
182
|
+
await start(this.closestPeerTagger)
|
183
|
+
await this.kb.addSelfPeer(this.components.peerId)
|
184
|
+
}
|
139
185
|
|
140
|
-
|
186
|
+
async afterStart (): Promise<void> {
|
187
|
+
// do this async to not block startup but iterate serially to not overwhelm
|
188
|
+
// the ping queue
|
189
|
+
Promise.resolve().then(async () => {
|
190
|
+
if (!this.populateFromDatastoreOnStart) {
|
191
|
+
return
|
192
|
+
}
|
141
193
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
194
|
+
let peerStorePeers = 0
|
195
|
+
|
196
|
+
// add existing peers from the peer store to routing table
|
197
|
+
for (const peer of await this.components.peerStore.all({
|
198
|
+
filters: [(peer) => {
|
199
|
+
return peer.protocols.includes(this.protocol) && peer.tags.has(KAD_PEER_TAG_NAME)
|
200
|
+
}],
|
201
|
+
limit: this.populateFromDatastoreLimit
|
202
|
+
})) {
|
203
|
+
if (!this.running) {
|
204
|
+
// bail if we've been shut down
|
205
|
+
return
|
206
|
+
}
|
146
207
|
|
147
|
-
|
148
|
-
|
208
|
+
try {
|
209
|
+
await this.add(peer.id)
|
210
|
+
peerStorePeers++
|
211
|
+
} catch (err) {
|
212
|
+
this.log('failed to add peer %p to routing table, removing kad-dht peer tags - %e')
|
213
|
+
await this.components.peerStore.merge(peer.id, {
|
214
|
+
tags: {
|
215
|
+
[this.peerTagName]: undefined
|
216
|
+
}
|
217
|
+
})
|
218
|
+
}
|
149
219
|
}
|
150
|
-
}
|
151
220
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
221
|
+
this.log('added %d peer store peers to the routing table', peerStorePeers)
|
222
|
+
})
|
223
|
+
.catch(err => {
|
224
|
+
this.log.error('error adding peer store peers to the routing table %e', err)
|
225
|
+
})
|
156
226
|
}
|
157
227
|
|
158
228
|
async stop (): Promise<void> {
|
159
229
|
this.running = false
|
160
|
-
this.
|
161
|
-
this.
|
230
|
+
await stop(this.closestPeerTagger)
|
231
|
+
this.pingOldContactQueue.abort()
|
232
|
+
this.pingNewContactQueue.abort()
|
162
233
|
}
|
163
234
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
let kClosest = new PeerSet()
|
171
|
-
|
172
|
-
const updatePeerTags = utils.debounce(() => {
|
173
|
-
const newClosest = new PeerSet(
|
174
|
-
kBuck.closest(kBuck.localPeer.kadId, KBUCKET_SIZE)
|
175
|
-
)
|
176
|
-
const addedPeers = newClosest.difference(kClosest)
|
177
|
-
const removedPeers = kClosest.difference(newClosest)
|
178
|
-
|
179
|
-
Promise.resolve()
|
180
|
-
.then(async () => {
|
181
|
-
for (const peer of addedPeers) {
|
182
|
-
await this.components.peerStore.merge(peer, {
|
183
|
-
tags: {
|
184
|
-
[this.tagName]: {
|
185
|
-
value: this.tagValue
|
186
|
-
},
|
187
|
-
[KEEP_ALIVE]: {
|
188
|
-
value: 1
|
189
|
-
}
|
190
|
-
}
|
191
|
-
})
|
192
|
-
}
|
193
|
-
|
194
|
-
for (const peer of removedPeers) {
|
195
|
-
await this.components.peerStore.merge(peer, {
|
196
|
-
tags: {
|
197
|
-
[this.tagName]: undefined,
|
198
|
-
[KEEP_ALIVE]: undefined
|
199
|
-
}
|
200
|
-
})
|
235
|
+
private async peerAdded (peer: Peer, bucket: LeafBucket): Promise<void> {
|
236
|
+
if (!this.components.peerId.equals(peer.peerId)) {
|
237
|
+
await this.components.peerStore.merge(peer.peerId, {
|
238
|
+
tags: {
|
239
|
+
[this.peerTagName]: {
|
240
|
+
value: this.peerTagValue
|
201
241
|
}
|
202
|
-
}
|
203
|
-
|
204
|
-
|
205
|
-
})
|
206
|
-
|
207
|
-
kClosest = newClosest
|
208
|
-
})
|
242
|
+
}
|
243
|
+
})
|
244
|
+
}
|
209
245
|
|
210
|
-
|
211
|
-
|
246
|
+
this.updateMetrics()
|
247
|
+
this.metrics?.kadBucketEvents.increment({ peer_added: true })
|
248
|
+
this.safeDispatchEvent('peer:add', { detail: peer.peerId })
|
249
|
+
}
|
212
250
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
251
|
+
private async peerRemoved (peer: Peer, bucket: LeafBucket): Promise<void> {
|
252
|
+
if (!this.components.peerId.equals(peer.peerId)) {
|
253
|
+
await this.components.peerStore.merge(peer.peerId, {
|
254
|
+
tags: {
|
255
|
+
[this.peerTagName]: undefined
|
256
|
+
}
|
257
|
+
})
|
258
|
+
}
|
218
259
|
|
219
|
-
|
220
|
-
|
221
|
-
})
|
260
|
+
this.updateMetrics()
|
261
|
+
this.metrics?.kadBucketEvents.increment({ peer_removed: true })
|
262
|
+
this.safeDispatchEvent('peer:remove', { detail: peer.peerId })
|
222
263
|
}
|
223
264
|
|
224
265
|
/**
|
@@ -231,87 +272,132 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
231
272
|
* `oldContacts` will not be empty and is the list of contacts that
|
232
273
|
* have not been contacted for the longest.
|
233
274
|
*/
|
234
|
-
async
|
275
|
+
async * pingOldContacts (oldContacts: Peer[], options?: AbortOptions): AsyncGenerator<Peer> {
|
235
276
|
if (!this.running) {
|
236
277
|
return
|
237
278
|
}
|
238
279
|
|
239
|
-
const
|
240
|
-
oldContacts,
|
241
|
-
newContact
|
242
|
-
} = evt.detail
|
280
|
+
const jobs: Array<() => Promise<Peer | undefined>> = []
|
243
281
|
|
244
|
-
const
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
if (pingJob != null) {
|
250
|
-
return pingJob.join()
|
251
|
-
}
|
282
|
+
for (const oldContact of oldContacts) {
|
283
|
+
if (this.kb.get(oldContact.kadId) == null) {
|
284
|
+
this.log('asked to ping contact %p that was not in routing table', oldContact.peerId)
|
285
|
+
continue
|
286
|
+
}
|
252
287
|
|
253
|
-
|
254
|
-
let stream: Stream | undefined
|
255
|
-
const signal = this.pingTimeout.getTimeoutSignal()
|
288
|
+
this.metrics?.kadBucketEvents.increment({ ping_old_contact: true })
|
256
289
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
}
|
290
|
+
jobs.push(async () => {
|
291
|
+
// if a previous ping wants us to ping this contact, re-use the result
|
292
|
+
const existingJob = this.pingOldContactQueue.find(oldContact.peerId)
|
261
293
|
|
262
|
-
|
263
|
-
|
264
|
-
|
294
|
+
if (existingJob != null) {
|
295
|
+
this.log('asked to ping contact %p was already being pinged', oldContact.peerId)
|
296
|
+
const result = await existingJob.join(options)
|
265
297
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
closer: [],
|
270
|
-
providers: []
|
271
|
-
}, options)
|
272
|
-
const response = await pb.read(options)
|
298
|
+
if (!result) {
|
299
|
+
return oldContact
|
300
|
+
}
|
273
301
|
|
274
|
-
|
302
|
+
return
|
303
|
+
}
|
275
304
|
|
276
|
-
|
277
|
-
|
278
|
-
|
305
|
+
const result = await this.pingOldContactQueue.add(async (options) => {
|
306
|
+
const signal = this.pingOldContactTimeout.getTimeoutSignal()
|
307
|
+
const signals = anySignal([signal, options?.signal])
|
308
|
+
setMaxListeners(Infinity, signal, signals)
|
279
309
|
|
280
|
-
|
310
|
+
try {
|
311
|
+
return await this.pingContact(oldContact, options)
|
312
|
+
} catch {
|
313
|
+
this.metrics?.kadBucketEvents.increment({ ping_old_contact_error: true })
|
281
314
|
return true
|
282
|
-
} catch (err: any) {
|
283
|
-
if (this.running) {
|
284
|
-
// only evict peers if we are still running, otherwise we evict
|
285
|
-
// when dialing is cancelled due to shutdown in progress
|
286
|
-
this.log.error('could not ping peer %p - %e', oldContact.peerId, err)
|
287
|
-
this.log('evicting old contact after ping failed %p', oldContact.peerId)
|
288
|
-
this.kb?.remove(oldContact.kadId)
|
289
|
-
}
|
290
|
-
|
291
|
-
stream?.abort(err)
|
292
|
-
|
293
|
-
return false
|
294
315
|
} finally {
|
295
|
-
this.
|
296
|
-
|
316
|
+
this.pingOldContactTimeout.cleanUp(signal)
|
317
|
+
signals.clear()
|
297
318
|
}
|
298
319
|
}, {
|
299
|
-
peerId: oldContact.peerId
|
320
|
+
peerId: oldContact.peerId,
|
321
|
+
signal: options?.signal
|
300
322
|
})
|
323
|
+
|
324
|
+
if (!result) {
|
325
|
+
return oldContact
|
326
|
+
}
|
301
327
|
})
|
302
|
-
|
328
|
+
}
|
329
|
+
|
330
|
+
for await (const peer of parallel(jobs)) {
|
331
|
+
if (peer != null) {
|
332
|
+
yield peer
|
333
|
+
}
|
334
|
+
}
|
335
|
+
}
|
303
336
|
|
304
|
-
|
305
|
-
|
306
|
-
|
337
|
+
async verifyNewContact (contact: Peer, options?: AbortOptions): Promise<boolean> {
|
338
|
+
const signal = this.pingNewContactTimeout.getTimeoutSignal()
|
339
|
+
const signals = anySignal([signal, options?.signal])
|
340
|
+
setMaxListeners(Infinity, signal, signals)
|
307
341
|
|
308
|
-
|
309
|
-
this.
|
310
|
-
|
342
|
+
try {
|
343
|
+
const job = this.pingNewContactQueue.find(contact.peerId)
|
344
|
+
|
345
|
+
if (job != null) {
|
346
|
+
this.log('joining existing ping to add new peer %p to routing table', contact.peerId)
|
347
|
+
return await job.join({
|
348
|
+
signal: signals
|
349
|
+
})
|
350
|
+
} else {
|
351
|
+
return await this.pingNewContactQueue.add(async (options) => {
|
352
|
+
this.metrics?.kadBucketEvents.increment({ ping_new_contact: true })
|
353
|
+
|
354
|
+
this.log('pinging new peer %p before adding to routing table', contact.peerId)
|
355
|
+
return this.pingContact(contact, options)
|
356
|
+
}, {
|
357
|
+
peerId: contact.peerId,
|
358
|
+
signal: signals
|
359
|
+
})
|
360
|
+
}
|
361
|
+
} catch (err) {
|
362
|
+
this.log.trace('tried to add peer %p but they were not online', contact.peerId)
|
363
|
+
this.metrics?.kadBucketEvents.increment({ ping_new_contact_error: true })
|
364
|
+
|
365
|
+
return false
|
366
|
+
} finally {
|
367
|
+
this.pingNewContactTimeout.cleanUp(signal)
|
368
|
+
signals.clear()
|
311
369
|
}
|
312
370
|
}
|
313
371
|
|
314
|
-
|
372
|
+
async pingContact (contact: Peer, options?: AbortOptions): Promise<boolean> {
|
373
|
+
let stream: Stream | undefined
|
374
|
+
|
375
|
+
try {
|
376
|
+
this.log('pinging contact %p', contact.peerId)
|
377
|
+
|
378
|
+
for await (const event of this.network.sendRequest(contact.peerId, { type: MessageType.PING }, options)) {
|
379
|
+
if (event.type === EventTypes.PEER_RESPONSE) {
|
380
|
+
if (event.messageType === MessageType.PING) {
|
381
|
+
this.log('contact %p ping ok', contact.peerId)
|
382
|
+
|
383
|
+
this.safeDispatchEvent('peer:ping', {
|
384
|
+
detail: contact.peerId
|
385
|
+
})
|
386
|
+
|
387
|
+
return true
|
388
|
+
}
|
389
|
+
|
390
|
+
return false
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
return false
|
395
|
+
} catch (err: any) {
|
396
|
+
this.log('error pinging old contact %p - %e', contact.peerId, err)
|
397
|
+
stream?.abort(err)
|
398
|
+
return false
|
399
|
+
}
|
400
|
+
}
|
315
401
|
|
316
402
|
/**
|
317
403
|
* Amount of currently stored peers
|
@@ -328,8 +414,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
328
414
|
* Find a specific peer by id
|
329
415
|
*/
|
330
416
|
async find (peer: PeerId): Promise<PeerId | undefined> {
|
331
|
-
const
|
332
|
-
return this.kb
|
417
|
+
const kadId = await utils.convertPeerId(peer)
|
418
|
+
return this.kb.get(kadId)?.peerId
|
333
419
|
}
|
334
420
|
|
335
421
|
/**
|
@@ -359,18 +445,12 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
359
445
|
/**
|
360
446
|
* Add or update the routing table with the given peer
|
361
447
|
*/
|
362
|
-
async add (peerId: PeerId): Promise<void> {
|
448
|
+
async add (peerId: PeerId, options?: AbortOptions): Promise<void> {
|
363
449
|
if (this.kb == null) {
|
364
450
|
throw new Error('RoutingTable is not started')
|
365
451
|
}
|
366
452
|
|
367
|
-
|
368
|
-
|
369
|
-
this.kb.add({ kadId, peerId })
|
370
|
-
|
371
|
-
this.log.trace('added %p with kad id %b', peerId, kadId)
|
372
|
-
|
373
|
-
this.updateMetrics()
|
453
|
+
await this.kb.add(peerId, options)
|
374
454
|
}
|
375
455
|
|
376
456
|
/**
|
@@ -381,11 +461,9 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
381
461
|
throw new Error('RoutingTable is not started')
|
382
462
|
}
|
383
463
|
|
384
|
-
const
|
385
|
-
|
386
|
-
this.kb.remove(id)
|
464
|
+
const kadId = await utils.convertPeerId(peer)
|
387
465
|
|
388
|
-
this.
|
466
|
+
await this.kb.remove(kadId)
|
389
467
|
}
|
390
468
|
|
391
469
|
private updateMetrics (): void {
|
@@ -396,6 +474,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
396
474
|
let size = 0
|
397
475
|
let buckets = 0
|
398
476
|
let maxDepth = 0
|
477
|
+
let minOccupancy = 20
|
478
|
+
let maxOccupancy = 0
|
399
479
|
|
400
480
|
function count (bucket: Bucket): void {
|
401
481
|
if (isLeafBucket(bucket)) {
|
@@ -405,6 +485,15 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
405
485
|
|
406
486
|
buckets++
|
407
487
|
size += bucket.peers.length
|
488
|
+
|
489
|
+
if (bucket.peers.length < minOccupancy) {
|
490
|
+
minOccupancy = bucket.peers.length
|
491
|
+
}
|
492
|
+
|
493
|
+
if (bucket.peers.length > maxOccupancy) {
|
494
|
+
maxOccupancy = bucket.peers.length
|
495
|
+
}
|
496
|
+
|
408
497
|
return
|
409
498
|
}
|
410
499
|
|
@@ -417,6 +506,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
417
506
|
this.metrics.routingTableSize.update(size)
|
418
507
|
this.metrics.routingTableKadBucketTotal.update(buckets)
|
419
508
|
this.metrics.routingTableKadBucketAverageOccupancy.update(Math.round(size / buckets))
|
509
|
+
this.metrics.routingTableKadBucketMinOccupancy.update(minOccupancy)
|
510
|
+
this.metrics.routingTableKadBucketMaxOccupancy.update(maxOccupancy)
|
420
511
|
this.metrics.routingTableKadBucketMaxDepth.update(maxDepth)
|
421
512
|
}
|
422
513
|
}
|