@libp2p/kad-dht 15.0.2 → 15.1.0-aa25d38ab
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 -15
- package/dist/index.min.js.map +7 -0
- package/dist/src/constants.d.ts +1 -1
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +1 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/content-fetching/index.d.ts +4 -4
- package/dist/src/content-fetching/index.d.ts.map +1 -1
- package/dist/src/content-fetching/index.js +40 -13
- package/dist/src/content-fetching/index.js.map +1 -1
- package/dist/src/content-routing/index.d.ts.map +1 -1
- package/dist/src/content-routing/index.js +88 -38
- package/dist/src/content-routing/index.js.map +1 -1
- package/dist/src/index.d.ts +58 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/kad-dht.d.ts +3 -1
- package/dist/src/kad-dht.d.ts.map +1 -1
- package/dist/src/kad-dht.js +14 -10
- package/dist/src/kad-dht.js.map +1 -1
- package/dist/src/message/dht.d.ts +1 -1
- package/dist/src/message/dht.d.ts.map +1 -1
- package/dist/src/message/dht.js +0 -5
- package/dist/src/message/dht.js.map +1 -1
- package/dist/src/network.d.ts +14 -8
- package/dist/src/network.d.ts.map +1 -1
- package/dist/src/network.js +17 -16
- package/dist/src/network.js.map +1 -1
- package/dist/src/peer-distance-list.d.ts +10 -3
- package/dist/src/peer-distance-list.d.ts.map +1 -1
- package/dist/src/peer-distance-list.js +13 -5
- package/dist/src/peer-distance-list.js.map +1 -1
- package/dist/src/peer-routing/index.d.ts +10 -8
- package/dist/src/peer-routing/index.d.ts.map +1 -1
- package/dist/src/peer-routing/index.js +79 -49
- package/dist/src/peer-routing/index.js.map +1 -1
- package/dist/src/providers.d.ts +4 -4
- package/dist/src/providers.d.ts.map +1 -1
- package/dist/src/providers.js +12 -12
- package/dist/src/providers.js.map +1 -1
- package/dist/src/query/events.d.ts +15 -3
- package/dist/src/query/events.d.ts.map +1 -1
- package/dist/src/query/events.js +9 -0
- package/dist/src/query/events.js.map +1 -1
- package/dist/src/query/manager.d.ts +3 -5
- package/dist/src/query/manager.d.ts.map +1 -1
- package/dist/src/query/manager.js +41 -16
- package/dist/src/query/manager.js.map +1 -1
- package/dist/src/query/query-path.d.ts +10 -14
- package/dist/src/query/query-path.d.ts.map +1 -1
- package/dist/src/query/query-path.js +67 -53
- package/dist/src/query/query-path.js.map +1 -1
- package/dist/src/query/types.d.ts +6 -5
- package/dist/src/query/types.d.ts.map +1 -1
- package/dist/src/query-self.d.ts +2 -3
- package/dist/src/query-self.d.ts.map +1 -1
- package/dist/src/query-self.js +11 -14
- package/dist/src/query-self.js.map +1 -1
- package/dist/src/routing-table/closest-peers.js +1 -1
- package/dist/src/routing-table/closest-peers.js.map +1 -1
- package/dist/src/routing-table/k-bucket.js +1 -1
- package/dist/src/routing-table/k-bucket.js.map +1 -1
- package/dist/src/rpc/handlers/add-provider.d.ts +1 -1
- package/dist/src/rpc/handlers/add-provider.d.ts.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/index.d.ts +7 -7
- package/dist/src/rpc/index.d.ts.map +1 -1
- package/dist/src/rpc/index.js +1 -1
- package/dist/src/rpc/index.js.map +1 -1
- package/dist/src/topology-listener.d.ts +2 -2
- package/dist/src/topology-listener.d.ts.map +1 -1
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +4 -0
- package/dist/src/utils.js.map +1 -1
- package/package.json +30 -39
- package/src/constants.ts +1 -1
- package/src/content-fetching/index.ts +41 -14
- package/src/content-routing/index.ts +93 -45
- package/src/index.ts +65 -6
- package/src/kad-dht.ts +14 -10
- package/src/message/dht.ts +2 -7
- package/src/network.ts +36 -22
- package/src/peer-distance-list.ts +19 -7
- package/src/peer-routing/index.ts +84 -53
- package/src/providers.ts +13 -13
- package/src/query/events.ts +27 -3
- package/src/query/manager.ts +48 -22
- package/src/query/query-path.ts +86 -76
- package/src/query/types.ts +7 -5
- package/src/query-self.ts +15 -17
- package/src/routing-table/closest-peers.ts +1 -1
- package/src/routing-table/k-bucket.ts +1 -1
- package/src/rpc/handlers/add-provider.ts +1 -1
- package/src/rpc/handlers/get-providers.ts +1 -1
- package/src/rpc/index.ts +12 -8
- package/src/topology-listener.ts +2 -2
- package/src/utils.ts +9 -0
- package/dist/typedoc-urls.json +0 -59
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { PeerSet } from '@libp2p/peer-collections'
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import { pipe } from 'it-pipe'
|
|
2
|
+
import { Queue } from '@libp2p/utils/queue'
|
|
3
|
+
import { pushable } from 'it-pushable'
|
|
5
4
|
import { ALPHA } from '../constants.js'
|
|
6
5
|
import { MessageType } from '../message/dht.js'
|
|
7
6
|
import { toPbPeerInfo } from '../message/utils.js'
|
|
@@ -10,7 +9,7 @@ import {
|
|
|
10
9
|
peerResponseEvent,
|
|
11
10
|
providerEvent
|
|
12
11
|
} from '../query/events.js'
|
|
13
|
-
import type { KadDHTComponents, PeerResponseEvent, ProviderEvent, QueryEvent } from '../index.js'
|
|
12
|
+
import type { FinalPeerEvent, KadDHTComponents, PeerResponseEvent, ProviderEvent, QueryEvent } from '../index.js'
|
|
14
13
|
import type { Message } from '../message/dht.js'
|
|
15
14
|
import type { Network } from '../network.js'
|
|
16
15
|
import type { PeerRouting } from '../peer-routing/index.js'
|
|
@@ -98,51 +97,76 @@ export class ContentRouting {
|
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
let sent = 0
|
|
100
|
+
const self = this
|
|
101
|
+
|
|
102
|
+
async function * publishProviderRecord (event: FinalPeerEvent): AsyncGenerator<QueryEvent, void, undefined> {
|
|
103
|
+
try {
|
|
104
|
+
self.log('sending provider record for %s to %p', key, event.peer.id)
|
|
105
|
+
|
|
106
|
+
for await (const addProviderEvent of self.network.sendMessage(event.peer.id, msg, {
|
|
107
|
+
...options,
|
|
108
|
+
path: event.path
|
|
109
|
+
})) {
|
|
110
|
+
if (addProviderEvent.name === 'PEER_RESPONSE') {
|
|
111
|
+
self.log('sent provider record for %s to %p', key, event.peer.id)
|
|
112
|
+
sent++
|
|
113
|
+
}
|
|
101
114
|
|
|
102
|
-
|
|
103
|
-
return async () => {
|
|
104
|
-
if (event.name !== 'FINAL_PEER') {
|
|
105
|
-
return [event]
|
|
115
|
+
yield addProviderEvent
|
|
106
116
|
}
|
|
117
|
+
} catch (err: any) {
|
|
118
|
+
self.log.error('error sending provide record to peer %p', event.peer.id, err)
|
|
119
|
+
yield queryErrorEvent({
|
|
120
|
+
from: event.peer.id,
|
|
121
|
+
error: err,
|
|
122
|
+
path: event.path
|
|
123
|
+
}, options)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
107
126
|
|
|
108
|
-
|
|
127
|
+
const events = pushable<QueryEvent>({
|
|
128
|
+
objectMode: true
|
|
129
|
+
})
|
|
109
130
|
|
|
110
|
-
|
|
131
|
+
const queue = new Queue({
|
|
132
|
+
concurrency: ALPHA
|
|
133
|
+
})
|
|
134
|
+
queue.addEventListener('idle', () => {
|
|
135
|
+
events.end()
|
|
136
|
+
})
|
|
137
|
+
queue.addEventListener('error', (err) => {
|
|
138
|
+
this.log.error('error publishing provider record to peer - %e', err)
|
|
139
|
+
})
|
|
111
140
|
|
|
112
|
-
|
|
113
|
-
|
|
141
|
+
queue.add(async () => {
|
|
142
|
+
const finalPeerEvents: FinalPeerEvent[] = []
|
|
114
143
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.log('sent provider record for %s to %p', key, event.peer.id)
|
|
118
|
-
sent++
|
|
119
|
-
}
|
|
144
|
+
for await (const event of this.peerRouting.getClosestPeers(target, options)) {
|
|
145
|
+
events.push(event)
|
|
120
146
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
} catch (err: any) {
|
|
124
|
-
this.log.error('error sending provide record to peer %p', event.peer.id, err)
|
|
125
|
-
events.push(queryErrorEvent({ from: event.peer.id, error: err }, options))
|
|
147
|
+
if (event.name !== 'FINAL_PEER') {
|
|
148
|
+
continue
|
|
126
149
|
}
|
|
127
150
|
|
|
128
|
-
|
|
151
|
+
finalPeerEvents.push(event)
|
|
129
152
|
}
|
|
130
|
-
}
|
|
131
153
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
finalPeerEvents.forEach(event => {
|
|
155
|
+
queue.add(async () => {
|
|
156
|
+
for await (const notifyEvent of publishProviderRecord(event)) {
|
|
157
|
+
events.push(notifyEvent)
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
.catch(err => {
|
|
161
|
+
this.log.error('error publishing provider record to peer - %e', err)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
.catch(err => {
|
|
166
|
+
events.end(err)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
yield * events
|
|
146
170
|
|
|
147
171
|
this.log('sent provider records to %d peers', sent)
|
|
148
172
|
}
|
|
@@ -154,7 +178,7 @@ export class ContentRouting {
|
|
|
154
178
|
const toFind = this.routingTable.kBucketSize
|
|
155
179
|
let found = 0
|
|
156
180
|
const target = key.multihash.bytes
|
|
157
|
-
const self = this
|
|
181
|
+
const self = this
|
|
158
182
|
|
|
159
183
|
this.log('findProviders %c', key)
|
|
160
184
|
|
|
@@ -181,8 +205,27 @@ export class ContentRouting {
|
|
|
181
205
|
}
|
|
182
206
|
}
|
|
183
207
|
|
|
184
|
-
yield peerResponseEvent({
|
|
185
|
-
|
|
208
|
+
yield peerResponseEvent({
|
|
209
|
+
from: this.components.peerId,
|
|
210
|
+
messageType: MessageType.GET_PROVIDERS,
|
|
211
|
+
providers,
|
|
212
|
+
path: {
|
|
213
|
+
index: -1,
|
|
214
|
+
queued: 0,
|
|
215
|
+
running: 0,
|
|
216
|
+
total: 0
|
|
217
|
+
}
|
|
218
|
+
}, options)
|
|
219
|
+
yield providerEvent({
|
|
220
|
+
from: this.components.peerId,
|
|
221
|
+
providers,
|
|
222
|
+
path: {
|
|
223
|
+
index: -1,
|
|
224
|
+
queued: 0,
|
|
225
|
+
running: 0,
|
|
226
|
+
total: 0
|
|
227
|
+
}
|
|
228
|
+
}, options)
|
|
186
229
|
|
|
187
230
|
found += providers.length
|
|
188
231
|
|
|
@@ -194,15 +237,16 @@ export class ContentRouting {
|
|
|
194
237
|
/**
|
|
195
238
|
* The query function to use on this particular disjoint path
|
|
196
239
|
*/
|
|
197
|
-
const findProvidersQuery: QueryFunc = async function * ({ peer, signal }) {
|
|
240
|
+
const findProvidersQuery: QueryFunc = async function * ({ peer, signal, path }) {
|
|
198
241
|
const request = {
|
|
199
242
|
type: MessageType.GET_PROVIDERS,
|
|
200
243
|
key: target
|
|
201
244
|
}
|
|
202
245
|
|
|
203
|
-
yield * self.network.sendRequest(peer, request, {
|
|
246
|
+
yield * self.network.sendRequest(peer.id, request, {
|
|
204
247
|
...options,
|
|
205
|
-
signal
|
|
248
|
+
signal,
|
|
249
|
+
path
|
|
206
250
|
})
|
|
207
251
|
}
|
|
208
252
|
|
|
@@ -226,7 +270,11 @@ export class ContentRouting {
|
|
|
226
270
|
}
|
|
227
271
|
|
|
228
272
|
if (newProviders.length > 0) {
|
|
229
|
-
yield providerEvent({
|
|
273
|
+
yield providerEvent({
|
|
274
|
+
from: event.from,
|
|
275
|
+
providers: newProviders,
|
|
276
|
+
path: event.path
|
|
277
|
+
}, options)
|
|
230
278
|
|
|
231
279
|
found += newProviders.length
|
|
232
280
|
|
package/src/index.ts
CHANGED
|
@@ -138,7 +138,7 @@ import { removePrivateAddressesMapper, removePublicAddressesMapper, passthroughM
|
|
|
138
138
|
import type { Libp2pEvents, ComponentLogger, TypedEventTarget, Metrics, PeerId, PeerInfo, PeerStore, RoutingOptions, PrivateKey } from '@libp2p/interface'
|
|
139
139
|
import type { AddressManager, ConnectionManager, Registrar } from '@libp2p/interface-internal'
|
|
140
140
|
import type { Ping } from '@libp2p/ping'
|
|
141
|
-
import type { AdaptiveTimeoutInit } from '@libp2p/utils/
|
|
141
|
+
import type { AdaptiveTimeoutInit } from '@libp2p/utils/adaptive-timeout'
|
|
142
142
|
import type { Datastore } from 'interface-datastore'
|
|
143
143
|
import type { CID } from 'multiformats/cid'
|
|
144
144
|
import type { ProgressEvent } from 'progress-events'
|
|
@@ -157,7 +157,8 @@ export enum EventTypes {
|
|
|
157
157
|
PROVIDER,
|
|
158
158
|
VALUE,
|
|
159
159
|
ADD_PEER,
|
|
160
|
-
DIAL_PEER
|
|
160
|
+
DIAL_PEER,
|
|
161
|
+
PATH_ENDED
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
/**
|
|
@@ -183,6 +184,13 @@ export type DHTProgressEvents =
|
|
|
183
184
|
ProgressEvent<'kad-dht:query:add-peer', AddPeerEvent> |
|
|
184
185
|
ProgressEvent<'kad-dht:query:dial-peer', DialPeerEvent>
|
|
185
186
|
|
|
187
|
+
export interface DisjointPath {
|
|
188
|
+
index: number
|
|
189
|
+
queued: number
|
|
190
|
+
running: number
|
|
191
|
+
total: number
|
|
192
|
+
}
|
|
193
|
+
|
|
186
194
|
/**
|
|
187
195
|
* Emitted when sending queries to remote peers
|
|
188
196
|
*/
|
|
@@ -192,6 +200,7 @@ export interface SendQueryEvent {
|
|
|
192
200
|
name: 'SEND_QUERY'
|
|
193
201
|
messageName: keyof typeof MessageType
|
|
194
202
|
messageType: MessageType
|
|
203
|
+
path: DisjointPath
|
|
195
204
|
}
|
|
196
205
|
|
|
197
206
|
/**
|
|
@@ -207,6 +216,7 @@ export interface PeerResponseEvent {
|
|
|
207
216
|
closer: PeerInfo[]
|
|
208
217
|
providers: PeerInfo[]
|
|
209
218
|
record?: DHTRecord
|
|
219
|
+
path: DisjointPath
|
|
210
220
|
}
|
|
211
221
|
|
|
212
222
|
/**
|
|
@@ -217,6 +227,7 @@ export interface FinalPeerEvent {
|
|
|
217
227
|
peer: PeerInfo
|
|
218
228
|
type: EventTypes.FINAL_PEER
|
|
219
229
|
name: 'FINAL_PEER'
|
|
230
|
+
path: DisjointPath
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
/**
|
|
@@ -227,6 +238,7 @@ export interface QueryErrorEvent {
|
|
|
227
238
|
type: EventTypes.QUERY_ERROR
|
|
228
239
|
name: 'QUERY_ERROR'
|
|
229
240
|
error: Error
|
|
241
|
+
path: DisjointPath
|
|
230
242
|
}
|
|
231
243
|
|
|
232
244
|
/**
|
|
@@ -237,6 +249,7 @@ export interface ProviderEvent {
|
|
|
237
249
|
type: EventTypes.PROVIDER
|
|
238
250
|
name: 'PROVIDER'
|
|
239
251
|
providers: PeerInfo[]
|
|
252
|
+
path: DisjointPath
|
|
240
253
|
}
|
|
241
254
|
|
|
242
255
|
/**
|
|
@@ -247,6 +260,7 @@ export interface ValueEvent {
|
|
|
247
260
|
type: EventTypes.VALUE
|
|
248
261
|
name: 'VALUE'
|
|
249
262
|
value: Uint8Array
|
|
263
|
+
path: DisjointPath
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
/**
|
|
@@ -256,18 +270,29 @@ export interface AddPeerEvent {
|
|
|
256
270
|
type: EventTypes.ADD_PEER
|
|
257
271
|
name: 'ADD_PEER'
|
|
258
272
|
peer: PeerId
|
|
273
|
+
path: DisjointPath
|
|
259
274
|
}
|
|
260
275
|
|
|
261
276
|
/**
|
|
262
|
-
* Emitted when peers are dialled as part of a query
|
|
277
|
+
* Emitted when peers are dialled and a new stream is opened as part of a query
|
|
263
278
|
*/
|
|
264
279
|
export interface DialPeerEvent {
|
|
265
280
|
peer: PeerId
|
|
266
281
|
type: EventTypes.DIAL_PEER
|
|
267
282
|
name: 'DIAL_PEER'
|
|
283
|
+
path: DisjointPath
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Emitted when sending queries to remote peers
|
|
288
|
+
*/
|
|
289
|
+
export interface PathEndedEvent {
|
|
290
|
+
type: EventTypes.PATH_ENDED
|
|
291
|
+
name: 'PATH_ENDED'
|
|
292
|
+
path: DisjointPath
|
|
268
293
|
}
|
|
269
294
|
|
|
270
|
-
export type QueryEvent = SendQueryEvent | PeerResponseEvent | FinalPeerEvent | QueryErrorEvent | ProviderEvent | ValueEvent | AddPeerEvent | DialPeerEvent
|
|
295
|
+
export type QueryEvent = SendQueryEvent | PeerResponseEvent | FinalPeerEvent | QueryErrorEvent | ProviderEvent | ValueEvent | AddPeerEvent | DialPeerEvent | PathEndedEvent
|
|
271
296
|
|
|
272
297
|
export interface RoutingTable {
|
|
273
298
|
size: number
|
|
@@ -278,6 +303,24 @@ export interface PeerInfoMapper {
|
|
|
278
303
|
}
|
|
279
304
|
|
|
280
305
|
export interface KadDHT {
|
|
306
|
+
/**
|
|
307
|
+
* This is the maximum size of the k-buckets and how many peers are looked up
|
|
308
|
+
* when searching for peers close to a key.
|
|
309
|
+
*/
|
|
310
|
+
readonly k: number
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Query concurrency factor - this controls how many peers we contact in
|
|
314
|
+
* parallel during a query.
|
|
315
|
+
*/
|
|
316
|
+
readonly a: number
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* From section 4.4 of the S/Kademlia paper - this is how many disjoint paths
|
|
320
|
+
* are used during a query.
|
|
321
|
+
*/
|
|
322
|
+
readonly d: number
|
|
323
|
+
|
|
281
324
|
/**
|
|
282
325
|
* Get a value from the DHT, the final ValueEvent will be the best value
|
|
283
326
|
*/
|
|
@@ -436,6 +479,22 @@ export interface KadDHTInit {
|
|
|
436
479
|
*/
|
|
437
480
|
kBucketSplitThreshold?: number
|
|
438
481
|
|
|
482
|
+
/**
|
|
483
|
+
* How many peers are queried in parallel during a query.
|
|
484
|
+
*
|
|
485
|
+
* @default 3
|
|
486
|
+
*/
|
|
487
|
+
alpha?: number
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* How many disjoint paths are used during a query
|
|
491
|
+
*
|
|
492
|
+
* @see https://telematics.tm.kit.edu/publications/Files/267/SKademlia_2007.pdf - section 4.4
|
|
493
|
+
*
|
|
494
|
+
* @default alpha
|
|
495
|
+
*/
|
|
496
|
+
disjointPaths?: number
|
|
497
|
+
|
|
439
498
|
/**
|
|
440
499
|
* How many bits of the KAD-ID of peers to use when creating the routing
|
|
441
500
|
* table.
|
|
@@ -506,8 +565,8 @@ export interface KadDHTInit {
|
|
|
506
565
|
initialQuerySelfInterval?: number
|
|
507
566
|
|
|
508
567
|
/**
|
|
509
|
-
* After startup by default all queries will be paused until
|
|
510
|
-
*
|
|
568
|
+
* After startup by default all queries will be paused until there is at least
|
|
569
|
+
* one peer in the routing table.
|
|
511
570
|
*
|
|
512
571
|
* Pass true here to disable this behavior.
|
|
513
572
|
*
|
package/src/kad-dht.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { NotFoundError, TypedEventEmitter, contentRoutingSymbol, peerDiscoverySy
|
|
|
2
2
|
import drain from 'it-drain'
|
|
3
3
|
import createMortice from 'mortice'
|
|
4
4
|
import pDefer from 'p-defer'
|
|
5
|
-
import { PROTOCOL } from './constants.js'
|
|
5
|
+
import { ALPHA, PROTOCOL } from './constants.js'
|
|
6
6
|
import { ContentFetching } from './content-fetching/index.js'
|
|
7
7
|
import { ContentRouting as KADDHTContentRouting } from './content-routing/index.js'
|
|
8
8
|
import { Network } from './network.js'
|
|
@@ -13,7 +13,7 @@ import { QuerySelf } from './query-self.js'
|
|
|
13
13
|
import { selectors as recordSelectors } from './record/selectors.js'
|
|
14
14
|
import { validators as recordValidators } from './record/validators.js'
|
|
15
15
|
import { Reprovider } from './reprovider.js'
|
|
16
|
-
import { RoutingTable } from './routing-table/index.js'
|
|
16
|
+
import { KBUCKET_SIZE, RoutingTable } from './routing-table/index.js'
|
|
17
17
|
import { RoutingTableRefresh } from './routing-table/refresh.js'
|
|
18
18
|
import { RPC } from './rpc/index.js'
|
|
19
19
|
import { TopologyListener } from './topology-listener.js'
|
|
@@ -113,6 +113,9 @@ export interface OperationMetrics {
|
|
|
113
113
|
* Original implementation in go: https://github.com/libp2p/go-libp2p-kad-dht.
|
|
114
114
|
*/
|
|
115
115
|
export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements KadDHTInterface, Startable {
|
|
116
|
+
public readonly k: number
|
|
117
|
+
public readonly a: number
|
|
118
|
+
public readonly d: number
|
|
116
119
|
public protocol: string
|
|
117
120
|
public routingTable: RoutingTable
|
|
118
121
|
public providers: Providers
|
|
@@ -122,7 +125,6 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
122
125
|
public readonly components: KadDHTComponents
|
|
123
126
|
private readonly log: Logger
|
|
124
127
|
private running: boolean
|
|
125
|
-
private readonly kBucketSize: number
|
|
126
128
|
private clientMode: boolean
|
|
127
129
|
private readonly validators: Validators
|
|
128
130
|
private readonly selectors: Selectors
|
|
@@ -161,8 +163,10 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
161
163
|
this.running = false
|
|
162
164
|
this.components = components
|
|
163
165
|
this.log = components.logger.forComponent(logPrefix)
|
|
166
|
+
this.k = init.kBucketSize ?? KBUCKET_SIZE
|
|
167
|
+
this.a = init.alpha ?? ALPHA
|
|
168
|
+
this.d = init.disjointPaths ?? this.a
|
|
164
169
|
this.protocol = init.protocol ?? PROTOCOL
|
|
165
|
-
this.kBucketSize = init.kBucketSize ?? 20
|
|
166
170
|
this.clientMode = init.clientMode ?? true
|
|
167
171
|
this.maxInboundStreams = init.maxInboundStreams ?? DEFAULT_MAX_INBOUND_STREAMS
|
|
168
172
|
this.maxOutboundStreams = init.maxOutboundStreams ?? DEFAULT_MAX_OUTBOUND_STREAMS
|
|
@@ -192,7 +196,7 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
192
196
|
})
|
|
193
197
|
|
|
194
198
|
this.routingTable = new RoutingTable(components, {
|
|
195
|
-
kBucketSize:
|
|
199
|
+
kBucketSize: this.k,
|
|
196
200
|
pingOldContactTimeout: init.pingOldContactTimeout,
|
|
197
201
|
pingOldContactConcurrency: init.pingOldContactConcurrency,
|
|
198
202
|
pingOldContactMaxQueueSize: init.pingOldContactMaxQueueSize,
|
|
@@ -218,12 +222,13 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
this.queryManager = new QueryManager(components, {
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
disjointPaths: this.d,
|
|
226
|
+
alpha: this.a,
|
|
223
227
|
logPrefix,
|
|
224
228
|
metricsPrefix,
|
|
225
229
|
initialQuerySelfHasRun,
|
|
226
|
-
routingTable: this.routingTable
|
|
230
|
+
routingTable: this.routingTable,
|
|
231
|
+
allowQueryWithZeroPeers: init.allowQueryWithZeroPeers
|
|
227
232
|
})
|
|
228
233
|
|
|
229
234
|
// DHT components
|
|
@@ -276,7 +281,6 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
276
281
|
initialInterval: init.initialQuerySelfInterval,
|
|
277
282
|
logPrefix,
|
|
278
283
|
initialQuerySelfHasRun,
|
|
279
|
-
routingTable: this.routingTable,
|
|
280
284
|
operationMetrics
|
|
281
285
|
})
|
|
282
286
|
this.reprovider = new Reprovider(components, {
|
|
@@ -317,7 +321,7 @@ export class KadDHT extends TypedEventEmitter<PeerDiscoveryEvents> implements Ka
|
|
|
317
321
|
|
|
318
322
|
await this.onPeerConnect(peerData)
|
|
319
323
|
}).catch(err => {
|
|
320
|
-
this.log.error('could not add %p to routing table - %e', peerId, err)
|
|
324
|
+
this.log.error('could not add %p to routing table - %e - %e', peerId, err)
|
|
321
325
|
})
|
|
322
326
|
})
|
|
323
327
|
|
package/src/message/dht.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
/* eslint-disable complexity */
|
|
3
|
-
/* eslint-disable @typescript-eslint/no-namespace */
|
|
4
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
|
|
5
|
-
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
6
|
-
|
|
7
|
-
import { type Codec, decodeMessage, type DecodeOptions, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime'
|
|
1
|
+
import { decodeMessage, encodeMessage, enumeration, MaxLengthError, message } from 'protons-runtime'
|
|
8
2
|
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
|
|
3
|
+
import type { Codec, DecodeOptions } from 'protons-runtime'
|
|
9
4
|
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
10
5
|
|
|
11
6
|
export interface Record {
|
package/src/network.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { InvalidParametersError, TypedEventEmitter } from '@libp2p/interface'
|
|
2
2
|
import { Libp2pRecord } from '@libp2p/record'
|
|
3
|
-
import { AdaptiveTimeout
|
|
3
|
+
import { AdaptiveTimeout } from '@libp2p/utils/adaptive-timeout'
|
|
4
4
|
import { pbStream } from 'it-protobuf-stream'
|
|
5
5
|
import { Message } from './message/dht.js'
|
|
6
6
|
import { fromPbPeerInfo } from './message/utils.js'
|
|
7
7
|
import {
|
|
8
|
-
dialPeerEvent,
|
|
9
8
|
sendQueryEvent,
|
|
10
9
|
peerResponseEvent,
|
|
11
|
-
queryErrorEvent
|
|
10
|
+
queryErrorEvent,
|
|
11
|
+
dialPeerEvent
|
|
12
12
|
} from './query/events.js'
|
|
13
|
-
import type { KadDHTComponents, QueryEvent } from './index.js'
|
|
13
|
+
import type { DisjointPath, KadDHTComponents, QueryEvent } from './index.js'
|
|
14
14
|
import type { AbortOptions, Logger, Stream, PeerId, PeerInfo, Startable, RoutingOptions, CounterGroup } from '@libp2p/interface'
|
|
15
|
+
import type { AdaptiveTimeoutInit } from '@libp2p/utils/adaptive-timeout'
|
|
15
16
|
|
|
16
17
|
export interface NetworkInit {
|
|
17
18
|
protocol: string
|
|
@@ -21,7 +22,16 @@ export interface NetworkInit {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
interface NetworkEvents {
|
|
24
|
-
|
|
25
|
+
peer: CustomEvent<PeerInfo>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SendMessageOptions extends RoutingOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Queries involve following up to `k` disjoint paths through the network -
|
|
31
|
+
* this option is which index within `k` this message is for, and it
|
|
32
|
+
* allows observers to collate events together on a per-path basis
|
|
33
|
+
*/
|
|
34
|
+
path: DisjointPath
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
/**
|
|
@@ -142,7 +152,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
142
152
|
/**
|
|
143
153
|
* Send a request and read a response
|
|
144
154
|
*/
|
|
145
|
-
async * sendRequest (to: PeerId, msg: Partial<Message>, options:
|
|
155
|
+
async * sendRequest (to: PeerId, msg: Partial<Message>, options: SendMessageOptions): AsyncGenerator<QueryEvent> {
|
|
146
156
|
if (!this.running) {
|
|
147
157
|
return
|
|
148
158
|
}
|
|
@@ -153,10 +163,6 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
153
163
|
throw new InvalidParametersError('Message type was missing')
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
this.log('sending %s to %p', msg.type, to)
|
|
157
|
-
yield dialPeerEvent({ peer: to }, options)
|
|
158
|
-
yield sendQueryEvent({ to, type }, options)
|
|
159
|
-
|
|
160
166
|
let stream: Stream | undefined
|
|
161
167
|
const signal = this.timeout.getTimeoutSignal(options)
|
|
162
168
|
|
|
@@ -168,8 +174,15 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
168
174
|
try {
|
|
169
175
|
this.metrics.operations?.increment({ [type]: true })
|
|
170
176
|
|
|
177
|
+
this.log('dialling %p', to)
|
|
178
|
+
yield dialPeerEvent({ peer: to, path: options.path }, options)
|
|
179
|
+
|
|
171
180
|
const connection = await this.components.connectionManager.openConnection(to, options)
|
|
172
181
|
stream = await connection.newStream(this.protocol, options)
|
|
182
|
+
|
|
183
|
+
this.log('sending %s to %p', msg.type, to)
|
|
184
|
+
yield sendQueryEvent({ to, type, path: options.path }, options)
|
|
185
|
+
|
|
173
186
|
const response = await this._writeReadMessage(stream, msg, options)
|
|
174
187
|
|
|
175
188
|
stream.close(options)
|
|
@@ -183,7 +196,8 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
183
196
|
messageType: response.type,
|
|
184
197
|
closer: response.closer.map(fromPbPeerInfo),
|
|
185
198
|
providers: response.providers.map(fromPbPeerInfo),
|
|
186
|
-
record: response.record == null ? undefined : Libp2pRecord.deserialize(response.record)
|
|
199
|
+
record: response.record == null ? undefined : Libp2pRecord.deserialize(response.record),
|
|
200
|
+
path: options.path
|
|
187
201
|
}, options)
|
|
188
202
|
} catch (err: any) {
|
|
189
203
|
this.metrics.errors?.increment({ [type]: true })
|
|
@@ -196,7 +210,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
196
210
|
this.log.error('could not send %s to %p - %e', msg.type, to, err)
|
|
197
211
|
}
|
|
198
212
|
|
|
199
|
-
yield queryErrorEvent({ from: to, error: err }, options)
|
|
213
|
+
yield queryErrorEvent({ from: to, error: err, path: options.path }, options)
|
|
200
214
|
} finally {
|
|
201
215
|
this.timeout.cleanUp(signal)
|
|
202
216
|
}
|
|
@@ -205,7 +219,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
205
219
|
/**
|
|
206
220
|
* Sends a message without expecting an answer
|
|
207
221
|
*/
|
|
208
|
-
async * sendMessage (to: PeerId, msg: Partial<Message>, options:
|
|
222
|
+
async * sendMessage (to: PeerId, msg: Partial<Message>, options: SendMessageOptions): AsyncGenerator<QueryEvent> {
|
|
209
223
|
if (!this.running) {
|
|
210
224
|
return
|
|
211
225
|
}
|
|
@@ -216,10 +230,6 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
216
230
|
throw new InvalidParametersError('Message type was missing')
|
|
217
231
|
}
|
|
218
232
|
|
|
219
|
-
this.log('sending %s to %p', msg.type, to)
|
|
220
|
-
yield dialPeerEvent({ peer: to }, options)
|
|
221
|
-
yield sendQueryEvent({ to, type }, options)
|
|
222
|
-
|
|
223
233
|
let stream: Stream | undefined
|
|
224
234
|
const signal = this.timeout.getTimeoutSignal(options)
|
|
225
235
|
|
|
@@ -231,9 +241,15 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
231
241
|
try {
|
|
232
242
|
this.metrics.operations?.increment({ [type]: true })
|
|
233
243
|
|
|
244
|
+
this.log('dialling %p', to)
|
|
245
|
+
yield dialPeerEvent({ peer: to, path: options.path }, options)
|
|
246
|
+
|
|
234
247
|
const connection = await this.components.connectionManager.openConnection(to, options)
|
|
235
248
|
stream = await connection.newStream(this.protocol, options)
|
|
236
249
|
|
|
250
|
+
this.log('sending %s to %p', msg.type, to)
|
|
251
|
+
yield sendQueryEvent({ to, type, path: options.path }, options)
|
|
252
|
+
|
|
237
253
|
await this._writeMessage(stream, msg, options)
|
|
238
254
|
|
|
239
255
|
stream.close(options)
|
|
@@ -242,12 +258,12 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
242
258
|
stream?.abort(err)
|
|
243
259
|
})
|
|
244
260
|
|
|
245
|
-
yield peerResponseEvent({ from: to, messageType: type }, options)
|
|
261
|
+
yield peerResponseEvent({ from: to, messageType: type, path: options.path }, options)
|
|
246
262
|
} catch (err: any) {
|
|
247
263
|
this.metrics.errors?.increment({ [type]: true })
|
|
248
264
|
|
|
249
265
|
stream?.abort(err)
|
|
250
|
-
yield queryErrorEvent({ from: to, error: err }, options)
|
|
266
|
+
yield queryErrorEvent({ from: to, error: err, path: options.path }, options)
|
|
251
267
|
} finally {
|
|
252
268
|
this.timeout.cleanUp(signal)
|
|
253
269
|
}
|
|
@@ -262,9 +278,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> implements Startab
|
|
|
262
278
|
}
|
|
263
279
|
|
|
264
280
|
/**
|
|
265
|
-
* Write a message and read
|
|
266
|
-
* If no response is received after the specified timeout
|
|
267
|
-
* this will error out.
|
|
281
|
+
* Write a message and read a response
|
|
268
282
|
*/
|
|
269
283
|
async _writeReadMessage (stream: Stream, msg: Partial<Message>, options: AbortOptions): Promise<Message> {
|
|
270
284
|
const pb = pbStream(stream)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { xor as uint8ArrayXor } from 'uint8arrays/xor'
|
|
2
2
|
import { xorCompare as uint8ArrayXorCompare } from 'uint8arrays/xor-compare'
|
|
3
3
|
import { convertPeerId } from './utils.js'
|
|
4
|
+
import type { DisjointPath } from './index.js'
|
|
4
5
|
import type { PeerId, PeerInfo } from '@libp2p/interface'
|
|
5
6
|
|
|
6
7
|
interface PeerDistance {
|
|
7
8
|
peer: PeerInfo
|
|
8
9
|
distance: Uint8Array
|
|
10
|
+
path: DisjointPath
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -40,30 +42,40 @@ export class PeerDistanceList {
|
|
|
40
42
|
/**
|
|
41
43
|
* The peers in the list, in order of distance from the origin key
|
|
42
44
|
*/
|
|
43
|
-
get peers ():
|
|
44
|
-
return this.peerDistances
|
|
45
|
+
get peers (): PeerDistance[] {
|
|
46
|
+
return [...this.peerDistances]
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* Add a peerId to the list.
|
|
49
51
|
*/
|
|
50
|
-
async add (peer: PeerInfo): Promise<void> {
|
|
52
|
+
async add (peer: PeerInfo, path: DisjointPath = { index: -1, queued: 0, running: 0, total: 0 }): Promise<void> {
|
|
51
53
|
const dhtKey = await convertPeerId(peer.id)
|
|
52
54
|
|
|
53
|
-
this.addWithKadId(peer, dhtKey)
|
|
55
|
+
this.addWithKadId(peer, dhtKey, path)
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
59
|
* Add a peerId to the list.
|
|
58
60
|
*/
|
|
59
|
-
addWithKadId (peer: PeerInfo, kadId: Uint8Array): void {
|
|
61
|
+
addWithKadId (peer: PeerInfo, kadId: Uint8Array, path: DisjointPath = { index: -1, queued: 0, running: 0, total: 0 }): void {
|
|
60
62
|
if (this.peerDistances.find(pd => pd.peer.id.equals(peer.id)) != null) {
|
|
61
63
|
return
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
const el = {
|
|
66
|
+
const el: PeerDistance = {
|
|
65
67
|
peer,
|
|
66
|
-
distance: uint8ArrayXor(this.originDhtKey, kadId)
|
|
68
|
+
distance: uint8ArrayXor(this.originDhtKey, kadId),
|
|
69
|
+
path
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.peerDistances.length === this.capacity) {
|
|
73
|
+
const lastPeer = this.peerDistances[this.peerDistances.length - 1]
|
|
74
|
+
|
|
75
|
+
// only add if it's closer than our furthest peer
|
|
76
|
+
if (lastPeer != null && uint8ArrayXorCompare(el.distance, lastPeer.distance) !== -1) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
67
79
|
}
|
|
68
80
|
|
|
69
81
|
let added = false
|