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