@libp2p/kad-dht 13.1.1 → 13.1.2-27b2fa6b6
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/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 +2 -1
- package/dist/src/network.d.ts.map +1 -1
- package/dist/src/network.js +10 -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/manager.d.ts +1 -2
- package/dist/src/query/manager.d.ts.map +1 -1
- package/dist/src/query/manager.js +11 -16
- package/dist/src/query/manager.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 +261 -158
- 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 +32 -6
- package/src/kad-dht.ts +29 -13
- package/src/network.ts +18 -5
- 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/manager.ts +16 -21
- package/src/query-self.ts +14 -6
- package/src/routing-table/closest-peers.ts +113 -0
- package/src/routing-table/index.ts +302 -189
- 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,32 +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
|
|
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'>
|
|
67
97
|
}
|
|
68
98
|
|
|
69
99
|
constructor (components: RoutingTableComponents, init: RoutingTableInit) {
|
|
@@ -72,22 +102,61 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
72
102
|
this.components = components
|
|
73
103
|
this.log = components.logger.forComponent(`${init.logPrefix}:routing-table`)
|
|
74
104
|
this.kBucketSize = init.kBucketSize ?? KBUCKET_SIZE
|
|
75
|
-
this.pingTimeout = init.pingTimeout ?? PING_TIMEOUT
|
|
76
|
-
this.pingConcurrency = init.pingConcurrency ?? PING_CONCURRENCY
|
|
77
105
|
this.running = false
|
|
78
106
|
this.protocol = init.protocol
|
|
79
|
-
this.
|
|
80
|
-
this.
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
83
|
-
|
|
84
|
-
this.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
88
151
|
})
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
91
160
|
})
|
|
92
161
|
|
|
93
162
|
if (this.components.metrics != null) {
|
|
@@ -95,7 +164,10 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
95
164
|
routingTableSize: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_size`),
|
|
96
165
|
routingTableKadBucketTotal: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_total`),
|
|
97
166
|
routingTableKadBucketAverageOccupancy: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_average_occupancy`),
|
|
98
|
-
|
|
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`),
|
|
169
|
+
routingTableKadBucketMaxDepth: this.components.metrics.registerMetric(`${init.logPrefix.replaceAll(':', '_')}_routing_table_kad_bucket_max_depth`),
|
|
170
|
+
kadBucketEvents: this.components.metrics.registerCounterGroup(`${init.logPrefix.replaceAll(':', '_')}_kad_bucket_events_total`)
|
|
99
171
|
}
|
|
100
172
|
}
|
|
101
173
|
}
|
|
@@ -107,101 +179,87 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
107
179
|
async start (): Promise<void> {
|
|
108
180
|
this.running = true
|
|
109
181
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
peerId: this.components.peerId
|
|
114
|
-
},
|
|
115
|
-
kBucketSize: this.kBucketSize,
|
|
116
|
-
prefixLength: this.prefixLength,
|
|
117
|
-
splitThreshold: this.splitThreshold,
|
|
118
|
-
numberOfNodesToPing: 1
|
|
119
|
-
})
|
|
120
|
-
this.kb = kBuck
|
|
121
|
-
|
|
122
|
-
// test whether to evict peers
|
|
123
|
-
kBuck.addEventListener('ping', (evt) => {
|
|
124
|
-
this._onPing(evt).catch(err => {
|
|
125
|
-
this.log.error('could not process k-bucket ping event', err)
|
|
126
|
-
})
|
|
127
|
-
})
|
|
182
|
+
await start(this.closestPeerTagger)
|
|
183
|
+
await this.kb.addSelfPeer(this.components.peerId)
|
|
184
|
+
}
|
|
128
185
|
|
|
129
|
-
|
|
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
|
+
}
|
|
130
193
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
+
}
|
|
135
207
|
|
|
136
|
-
|
|
137
|
-
|
|
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
|
+
}
|
|
138
219
|
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
this.log('added %d peer store peers to the routing table', peerStorePeers)
|
|
142
220
|
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
})
|
|
145
226
|
}
|
|
146
227
|
|
|
147
228
|
async stop (): Promise<void> {
|
|
148
229
|
this.running = false
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
230
|
+
await stop(this.closestPeerTagger)
|
|
231
|
+
this.pingOldContactQueue.abort()
|
|
232
|
+
this.pingNewContactQueue.abort()
|
|
151
233
|
}
|
|
152
234
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
let kClosest = new PeerSet()
|
|
160
|
-
|
|
161
|
-
const updatePeerTags = utils.debounce(() => {
|
|
162
|
-
const newClosest = new PeerSet(
|
|
163
|
-
kBuck.closest(kBuck.localPeer.kadId, KBUCKET_SIZE)
|
|
164
|
-
)
|
|
165
|
-
const addedPeers = newClosest.difference(kClosest)
|
|
166
|
-
const removedPeers = kClosest.difference(newClosest)
|
|
167
|
-
|
|
168
|
-
Promise.resolve()
|
|
169
|
-
.then(async () => {
|
|
170
|
-
for (const peer of addedPeers) {
|
|
171
|
-
await this.components.peerStore.merge(peer, {
|
|
172
|
-
tags: {
|
|
173
|
-
[this.tagName]: {
|
|
174
|
-
value: this.tagValue
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
for (const peer of removedPeers) {
|
|
181
|
-
await this.components.peerStore.merge(peer, {
|
|
182
|
-
tags: {
|
|
183
|
-
[this.tagName]: undefined
|
|
184
|
-
}
|
|
185
|
-
})
|
|
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
|
|
186
241
|
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
kClosest = newClosest
|
|
193
|
-
})
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
}
|
|
194
245
|
|
|
195
|
-
|
|
196
|
-
|
|
246
|
+
this.updateMetrics()
|
|
247
|
+
this.metrics?.kadBucketEvents.increment({ peer_added: true })
|
|
248
|
+
this.safeDispatchEvent('peer:add', { detail: peer.peerId })
|
|
249
|
+
}
|
|
197
250
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
}
|
|
202
259
|
|
|
203
|
-
|
|
204
|
-
})
|
|
260
|
+
this.updateMetrics()
|
|
261
|
+
this.metrics?.kadBucketEvents.increment({ peer_removed: true })
|
|
262
|
+
this.safeDispatchEvent('peer:remove', { detail: peer.peerId })
|
|
205
263
|
}
|
|
206
264
|
|
|
207
265
|
/**
|
|
@@ -214,82 +272,132 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
214
272
|
* `oldContacts` will not be empty and is the list of contacts that
|
|
215
273
|
* have not been contacted for the longest.
|
|
216
274
|
*/
|
|
217
|
-
async
|
|
275
|
+
async * pingOldContacts (oldContacts: Peer[], options?: AbortOptions): AsyncGenerator<Peer> {
|
|
218
276
|
if (!this.running) {
|
|
219
277
|
return
|
|
220
278
|
}
|
|
221
279
|
|
|
222
|
-
const
|
|
223
|
-
oldContacts,
|
|
224
|
-
newContact
|
|
225
|
-
} = evt.detail
|
|
226
|
-
|
|
227
|
-
const results = await Promise.all(
|
|
228
|
-
oldContacts.map(async oldContact => {
|
|
229
|
-
// if a previous ping wants us to ping this contact, re-use the result
|
|
230
|
-
const pingJob = this.pingQueue.find(oldContact.peerId)
|
|
280
|
+
const jobs: Array<() => Promise<Peer | undefined>> = []
|
|
231
281
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
+
}
|
|
235
287
|
|
|
236
|
-
|
|
237
|
-
let stream: Stream | undefined
|
|
288
|
+
this.metrics?.kadBucketEvents.increment({ ping_old_contact: true })
|
|
238
289
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
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)
|
|
243
293
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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)
|
|
247
297
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}, Message, options)
|
|
252
|
-
const response = await pb.read(Message, options)
|
|
298
|
+
if (!result) {
|
|
299
|
+
return oldContact
|
|
300
|
+
}
|
|
253
301
|
|
|
254
|
-
|
|
302
|
+
return
|
|
303
|
+
}
|
|
255
304
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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)
|
|
259
309
|
|
|
310
|
+
try {
|
|
311
|
+
return await this.pingContact(oldContact, options)
|
|
312
|
+
} catch {
|
|
313
|
+
this.metrics?.kadBucketEvents.increment({ ping_old_contact_error: true })
|
|
260
314
|
return true
|
|
261
|
-
} catch (err: any) {
|
|
262
|
-
if (this.running && this.kb != null) {
|
|
263
|
-
// only evict peers if we are still running, otherwise we evict
|
|
264
|
-
// when dialing is cancelled due to shutdown in progress
|
|
265
|
-
this.log.error('could not ping peer %p', oldContact.peerId, err)
|
|
266
|
-
this.log('evicting old contact after ping failed %p', oldContact.peerId)
|
|
267
|
-
this.kb.remove(oldContact.kadId)
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
stream?.abort(err)
|
|
271
|
-
|
|
272
|
-
return false
|
|
273
315
|
} finally {
|
|
274
|
-
this.
|
|
316
|
+
this.pingOldContactTimeout.cleanUp(signal)
|
|
317
|
+
signals.clear()
|
|
275
318
|
}
|
|
276
319
|
}, {
|
|
277
|
-
peerId: oldContact.peerId
|
|
320
|
+
peerId: oldContact.peerId,
|
|
321
|
+
signal: options?.signal
|
|
278
322
|
})
|
|
323
|
+
|
|
324
|
+
if (!result) {
|
|
325
|
+
return oldContact
|
|
326
|
+
}
|
|
279
327
|
})
|
|
280
|
-
|
|
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)
|
|
281
341
|
|
|
282
|
-
|
|
283
|
-
.
|
|
284
|
-
.length
|
|
342
|
+
try {
|
|
343
|
+
const job = this.pingNewContactQueue.find(contact.peerId)
|
|
285
344
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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()
|
|
289
369
|
}
|
|
290
370
|
}
|
|
291
371
|
|
|
292
|
-
|
|
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
|
+
}
|
|
293
401
|
|
|
294
402
|
/**
|
|
295
403
|
* Amount of currently stored peers
|
|
@@ -306,8 +414,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
306
414
|
* Find a specific peer by id
|
|
307
415
|
*/
|
|
308
416
|
async find (peer: PeerId): Promise<PeerId | undefined> {
|
|
309
|
-
const
|
|
310
|
-
return this.kb
|
|
417
|
+
const kadId = await utils.convertPeerId(peer)
|
|
418
|
+
return this.kb.get(kadId)?.peerId
|
|
311
419
|
}
|
|
312
420
|
|
|
313
421
|
/**
|
|
@@ -337,18 +445,12 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
337
445
|
/**
|
|
338
446
|
* Add or update the routing table with the given peer
|
|
339
447
|
*/
|
|
340
|
-
async add (peerId: PeerId): Promise<void> {
|
|
448
|
+
async add (peerId: PeerId, options?: AbortOptions): Promise<void> {
|
|
341
449
|
if (this.kb == null) {
|
|
342
450
|
throw new Error('RoutingTable is not started')
|
|
343
451
|
}
|
|
344
452
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
this.kb.add({ kadId, peerId })
|
|
348
|
-
|
|
349
|
-
this.log.trace('added %p with kad id %b', peerId, kadId)
|
|
350
|
-
|
|
351
|
-
this.updateMetrics()
|
|
453
|
+
await this.kb.add(peerId, options)
|
|
352
454
|
}
|
|
353
455
|
|
|
354
456
|
/**
|
|
@@ -359,11 +461,9 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
359
461
|
throw new Error('RoutingTable is not started')
|
|
360
462
|
}
|
|
361
463
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
this.kb.remove(id)
|
|
464
|
+
const kadId = await utils.convertPeerId(peer)
|
|
365
465
|
|
|
366
|
-
this.
|
|
466
|
+
await this.kb.remove(kadId)
|
|
367
467
|
}
|
|
368
468
|
|
|
369
469
|
private updateMetrics (): void {
|
|
@@ -374,6 +474,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
374
474
|
let size = 0
|
|
375
475
|
let buckets = 0
|
|
376
476
|
let maxDepth = 0
|
|
477
|
+
let minOccupancy = 20
|
|
478
|
+
let maxOccupancy = 0
|
|
377
479
|
|
|
378
480
|
function count (bucket: Bucket): void {
|
|
379
481
|
if (isLeafBucket(bucket)) {
|
|
@@ -383,6 +485,15 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
383
485
|
|
|
384
486
|
buckets++
|
|
385
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
|
+
|
|
386
497
|
return
|
|
387
498
|
}
|
|
388
499
|
|
|
@@ -395,6 +506,8 @@ export class RoutingTable extends TypedEventEmitter<RoutingTableEvents> implemen
|
|
|
395
506
|
this.metrics.routingTableSize.update(size)
|
|
396
507
|
this.metrics.routingTableKadBucketTotal.update(buckets)
|
|
397
508
|
this.metrics.routingTableKadBucketAverageOccupancy.update(Math.round(size / buckets))
|
|
509
|
+
this.metrics.routingTableKadBucketMinOccupancy.update(minOccupancy)
|
|
510
|
+
this.metrics.routingTableKadBucketMaxOccupancy.update(maxOccupancy)
|
|
398
511
|
this.metrics.routingTableKadBucketMaxDepth.update(maxDepth)
|
|
399
512
|
}
|
|
400
513
|
}
|