@helia/bitswap 0.0.0-b67ac5f → 0.0.0-ba4b7ba
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 +1 -1
- package/dist/src/bitswap.d.ts.map +1 -1
- package/dist/src/bitswap.js +1 -4
- package/dist/src/bitswap.js.map +1 -1
- package/dist/src/network.d.ts +4 -3
- package/dist/src/network.d.ts.map +1 -1
- package/dist/src/network.js +27 -19
- package/dist/src/network.js.map +1 -1
- package/dist/src/peer-want-lists/index.d.ts +2 -2
- package/dist/src/peer-want-lists/index.d.ts.map +1 -1
- package/dist/src/peer-want-lists/index.js +1 -1
- package/dist/src/peer-want-lists/index.js.map +1 -1
- package/dist/src/session.d.ts.map +1 -1
- package/dist/src/session.js +0 -4
- package/dist/src/session.js.map +1 -1
- package/dist/src/stats.d.ts +2 -2
- package/dist/src/stats.d.ts.map +1 -1
- package/dist/src/stats.js +4 -4
- package/dist/src/stats.js.map +1 -1
- package/dist/src/want-list.d.ts +13 -15
- package/dist/src/want-list.d.ts.map +1 -1
- package/dist/src/want-list.js +102 -106
- package/dist/src/want-list.js.map +1 -1
- package/package.json +2 -2
- package/src/bitswap.ts +1 -4
- package/src/network.ts +33 -23
- package/src/peer-want-lists/index.ts +3 -3
- package/src/session.ts +0 -5
- package/src/stats.ts +6 -6
- package/src/want-list.ts +167 -173
package/src/want-list.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbortError } from '@libp2p/interface'
|
|
2
2
|
import { trackedPeerMap, PeerSet } from '@libp2p/peer-collections'
|
|
3
3
|
import { trackedMap } from '@libp2p/utils/tracked-map'
|
|
4
4
|
import all from 'it-all'
|
|
@@ -8,16 +8,14 @@ import { pipe } from 'it-pipe'
|
|
|
8
8
|
import { CID } from 'multiformats/cid'
|
|
9
9
|
import { sha256 } from 'multiformats/hashes/sha2'
|
|
10
10
|
import pDefer from 'p-defer'
|
|
11
|
-
import { raceEvent } from 'race-event'
|
|
12
|
-
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
13
11
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
14
12
|
import { DEFAULT_MESSAGE_SEND_DELAY } from './constants.js'
|
|
15
13
|
import { BlockPresenceType, WantType } from './pb/message.js'
|
|
16
14
|
import vd from './utils/varint-decoder.js'
|
|
17
|
-
import type {
|
|
15
|
+
import type { MultihashHasherLoader } from './index.js'
|
|
18
16
|
import type { BitswapNetworkWantProgressEvents, Network } from './network.js'
|
|
19
17
|
import type { BitswapMessage } from './pb/message.js'
|
|
20
|
-
import type { ComponentLogger, PeerId, Startable, AbortOptions
|
|
18
|
+
import type { ComponentLogger, Metrics, PeerId, Startable, AbortOptions } from '@libp2p/interface'
|
|
21
19
|
import type { Logger } from '@libp2p/logger'
|
|
22
20
|
import type { PeerMap } from '@libp2p/peer-collections'
|
|
23
21
|
import type { DeferredPromise } from 'p-defer'
|
|
@@ -26,7 +24,7 @@ import type { ProgressOptions } from 'progress-events'
|
|
|
26
24
|
export interface WantListComponents {
|
|
27
25
|
network: Network
|
|
28
26
|
logger: ComponentLogger
|
|
29
|
-
|
|
27
|
+
metrics?: Metrics
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
export interface WantListInit {
|
|
@@ -65,6 +63,16 @@ export interface WantListEntry {
|
|
|
65
63
|
* If this set has members, the want will only be sent to these peers
|
|
66
64
|
*/
|
|
67
65
|
session: PeerSet
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Promises returned from `.wantBlock` for this block
|
|
69
|
+
*/
|
|
70
|
+
blockWantListeners: Array<DeferredPromise<WantBlockResult>>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Promises returned from `.wantPresence` for this block
|
|
74
|
+
*/
|
|
75
|
+
blockPresenceListeners: Array<DeferredPromise<WantPresenceResult>>
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
export interface WantOptions extends AbortOptions, ProgressOptions<BitswapNetworkWantProgressEvents> {
|
|
@@ -100,12 +108,7 @@ export interface WantHaveResult {
|
|
|
100
108
|
|
|
101
109
|
export type WantPresenceResult = WantDontHaveResult | WantHaveResult
|
|
102
110
|
|
|
103
|
-
export
|
|
104
|
-
block: CustomEvent<WantBlockResult>
|
|
105
|
-
presence: CustomEvent<WantPresenceResult>
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export class WantList extends TypedEventEmitter<WantListEvents> implements Startable, TypedEventTarget<WantListEvents> {
|
|
111
|
+
export class WantList implements Startable {
|
|
109
112
|
/**
|
|
110
113
|
* Tracks what CIDs we've previously sent to which peers
|
|
111
114
|
*/
|
|
@@ -116,19 +119,15 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
116
119
|
private readonly sendMessagesDelay: number
|
|
117
120
|
private sendMessagesTimeout?: ReturnType<typeof setTimeout>
|
|
118
121
|
private readonly hashLoader?: MultihashHasherLoader
|
|
119
|
-
private sendingMessages?: DeferredPromise<void>
|
|
120
122
|
|
|
121
123
|
constructor (components: WantListComponents, init: WantListInit = {}) {
|
|
122
|
-
super()
|
|
123
|
-
|
|
124
|
-
setMaxListeners(Infinity, this)
|
|
125
124
|
this.peers = trackedPeerMap({
|
|
126
125
|
name: 'ipfs_bitswap_peers',
|
|
127
|
-
metrics: components.
|
|
126
|
+
metrics: components.metrics
|
|
128
127
|
})
|
|
129
128
|
this.wants = trackedMap({
|
|
130
129
|
name: 'ipfs_bitswap_wantlist',
|
|
131
|
-
metrics: components.
|
|
130
|
+
metrics: components.metrics
|
|
132
131
|
})
|
|
133
132
|
this.network = components.network
|
|
134
133
|
this.sendMessagesDelay = init.sendMessagesDelay ?? DEFAULT_MESSAGE_SEND_DELAY
|
|
@@ -165,7 +164,9 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
165
164
|
priority: options.priority ?? 1,
|
|
166
165
|
wantType: options.wantType ?? WantType.WantBlock,
|
|
167
166
|
cancel: false,
|
|
168
|
-
sendDontHave: true
|
|
167
|
+
sendDontHave: true,
|
|
168
|
+
blockWantListeners: [],
|
|
169
|
+
blockPresenceListeners: []
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
if (options.peerId != null) {
|
|
@@ -197,41 +198,30 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
200
|
|
|
200
|
-
//
|
|
201
|
-
|
|
201
|
+
// add a promise that will be resolved or rejected when the response arrives
|
|
202
|
+
let deferred: DeferredPromise<WantBlockResult | WantPresenceResult>
|
|
202
203
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const event = await raceEvent<CustomEvent<WantBlockResult>>(this, 'block', options?.signal, {
|
|
206
|
-
filter: (event) => {
|
|
207
|
-
return uint8ArrayEquals(cid.multihash.digest, event.detail.cid.multihash.digest)
|
|
208
|
-
},
|
|
209
|
-
errorMessage: 'Want was aborted'
|
|
210
|
-
})
|
|
204
|
+
if (options.wantType === WantType.WantBlock) {
|
|
205
|
+
const p = deferred = pDefer<WantBlockResult>()
|
|
211
206
|
|
|
212
|
-
|
|
213
|
-
|
|
207
|
+
entry.blockWantListeners.push(p)
|
|
208
|
+
} else {
|
|
209
|
+
const p = deferred = pDefer<WantPresenceResult>()
|
|
214
210
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return uint8ArrayEquals(cid.multihash.digest, event.detail.cid.multihash.digest)
|
|
218
|
-
},
|
|
219
|
-
errorMessage: 'Want was aborted'
|
|
220
|
-
})
|
|
211
|
+
entry.blockPresenceListeners.push(p)
|
|
212
|
+
}
|
|
221
213
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
214
|
+
// reject the promise if the want is rejected
|
|
215
|
+
const abortListener = (): void => {
|
|
216
|
+
this.log('want for %c was aborted, cancelling want', cid)
|
|
217
|
+
|
|
218
|
+
if (entry != null) {
|
|
226
219
|
entry.cancel = true
|
|
227
|
-
// broadcast changes
|
|
228
|
-
await this.sendMessagesDebounced()
|
|
229
220
|
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
221
|
|
|
233
|
-
|
|
234
|
-
|
|
222
|
+
deferred.reject(new AbortError('Want was aborted'))
|
|
223
|
+
}
|
|
224
|
+
options.signal?.addEventListener('abort', abortListener)
|
|
235
225
|
|
|
236
226
|
// broadcast changes
|
|
237
227
|
clearTimeout(this.sendMessagesTimeout)
|
|
@@ -241,70 +231,77 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
241
231
|
this.log('error sending messages to peers', err)
|
|
242
232
|
})
|
|
243
233
|
}, this.sendMessagesDelay)
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
return await deferred.promise
|
|
237
|
+
} finally {
|
|
238
|
+
// remove listener
|
|
239
|
+
options.signal?.removeEventListener('abort', abortListener)
|
|
240
|
+
// remove deferred promise
|
|
241
|
+
if (options.wantType === WantType.WantBlock) {
|
|
242
|
+
entry.blockWantListeners = entry.blockWantListeners.filter(recipient => recipient !== deferred)
|
|
243
|
+
} else {
|
|
244
|
+
entry.blockPresenceListeners = entry.blockPresenceListeners.filter(recipient => recipient !== deferred)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
244
247
|
}
|
|
245
248
|
|
|
246
249
|
private async sendMessages (): Promise<void> {
|
|
247
|
-
this.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}),
|
|
284
|
-
(source) => all(source)
|
|
285
|
-
)
|
|
286
|
-
}
|
|
250
|
+
for (const [peerId, sentWants] of this.peers) {
|
|
251
|
+
const sent = new Set<string>()
|
|
252
|
+
const message: Partial<BitswapMessage> = {
|
|
253
|
+
wantlist: {
|
|
254
|
+
full: false,
|
|
255
|
+
entries: pipe(
|
|
256
|
+
this.wants.entries(),
|
|
257
|
+
(source) => filter(source, ([key, entry]) => {
|
|
258
|
+
// skip session-only wants
|
|
259
|
+
if (entry.session.size > 0 && !entry.session.has(peerId)) {
|
|
260
|
+
return false
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const sentPreviously = sentWants.has(key)
|
|
264
|
+
|
|
265
|
+
// don't cancel if we've not sent it to them before
|
|
266
|
+
if (entry.cancel) {
|
|
267
|
+
return sentPreviously
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// only send if we've not sent it to them before
|
|
271
|
+
return !sentPreviously
|
|
272
|
+
}),
|
|
273
|
+
(source) => map(source, ([key, entry]) => {
|
|
274
|
+
sent.add(key)
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
cid: entry.cid.bytes,
|
|
278
|
+
priority: entry.priority,
|
|
279
|
+
wantType: entry.wantType,
|
|
280
|
+
cancel: entry.cancel,
|
|
281
|
+
sendDontHave: entry.sendDontHave
|
|
282
|
+
}
|
|
283
|
+
}),
|
|
284
|
+
(source) => all(source)
|
|
285
|
+
)
|
|
287
286
|
}
|
|
287
|
+
}
|
|
288
288
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
289
|
+
if (message.wantlist?.entries.length === 0) {
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
292
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
293
|
+
// add message to send queue
|
|
294
|
+
try {
|
|
295
|
+
await this.network.sendMessage(peerId, message)
|
|
296
296
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
} catch (err: any) {
|
|
302
|
-
this.log.error('error sending full wantlist to new peer', err)
|
|
297
|
+
// update list of messages sent to remote
|
|
298
|
+
for (const key of sent) {
|
|
299
|
+
sentWants.add(key)
|
|
303
300
|
}
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
301
|
+
} catch (err: any) {
|
|
302
|
+
this.log.error('error sending full wantlist to new peer', err)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
308
305
|
|
|
309
306
|
// queued all message sends, remove cancelled wants from wantlist and sent
|
|
310
307
|
// wants
|
|
@@ -317,8 +314,6 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
317
314
|
}
|
|
318
315
|
}
|
|
319
316
|
}
|
|
320
|
-
|
|
321
|
-
this.sendingMessages.resolve()
|
|
322
317
|
}
|
|
323
318
|
|
|
324
319
|
has (cid: CID): boolean {
|
|
@@ -330,30 +325,31 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
330
325
|
* Add a CID to the wantlist
|
|
331
326
|
*/
|
|
332
327
|
async wantPresence (cid: CID, options: WantOptions = {}): Promise<WantPresenceResult> {
|
|
333
|
-
if (options.peerId != null) {
|
|
334
|
-
const
|
|
328
|
+
if (options.peerId != null && this.peers.get(options.peerId) == null) {
|
|
329
|
+
const cidStr = uint8ArrayToString(cid.multihash.bytes, 'base64')
|
|
335
330
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
full: false,
|
|
340
|
-
entries: [{
|
|
341
|
-
cid: cid.bytes,
|
|
342
|
-
sendDontHave: true,
|
|
343
|
-
wantType: WantType.WantHave,
|
|
344
|
-
priority: 1
|
|
345
|
-
}]
|
|
346
|
-
}
|
|
347
|
-
})
|
|
331
|
+
try {
|
|
332
|
+
// if we don't have them as a peer, add them
|
|
333
|
+
this.peers.set(options.peerId, new Set([cidStr]))
|
|
348
334
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
335
|
+
// sending WantHave directly to peer
|
|
336
|
+
await this.network.sendMessage(options.peerId, {
|
|
337
|
+
wantlist: {
|
|
338
|
+
full: false,
|
|
339
|
+
entries: [{
|
|
340
|
+
cid: cid.bytes,
|
|
341
|
+
sendDontHave: true,
|
|
342
|
+
wantType: WantType.WantHave,
|
|
343
|
+
priority: 1
|
|
344
|
+
}]
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
} catch (err) {
|
|
348
|
+
// sending failed, remove them as a peer
|
|
349
|
+
this.peers.delete(options.peerId)
|
|
355
350
|
|
|
356
|
-
|
|
351
|
+
throw err
|
|
352
|
+
}
|
|
357
353
|
}
|
|
358
354
|
|
|
359
355
|
return this.addEntry(cid, {
|
|
@@ -372,29 +368,15 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
372
368
|
})
|
|
373
369
|
}
|
|
374
370
|
|
|
375
|
-
/**
|
|
376
|
-
* Invoked when a block has been received from an external source
|
|
377
|
-
*/
|
|
378
|
-
async receivedBlock (cid: CID, options: ProgressOptions<BitswapNotifyProgressEvents> & AbortOptions): Promise<void> {
|
|
379
|
-
const cidStr = uint8ArrayToString(cid.multihash.bytes, 'base64')
|
|
380
|
-
|
|
381
|
-
const entry = this.wants.get(cidStr)
|
|
382
|
-
|
|
383
|
-
if (entry == null) {
|
|
384
|
-
return
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
entry.cancel = true
|
|
388
|
-
|
|
389
|
-
await this.sendMessagesDebounced()
|
|
390
|
-
}
|
|
391
|
-
|
|
392
371
|
/**
|
|
393
372
|
* Invoked when a message is received from a bitswap peer
|
|
394
373
|
*/
|
|
395
374
|
private async receiveMessage (sender: PeerId, message: BitswapMessage): Promise<void> {
|
|
396
375
|
this.log('received message from %p', sender)
|
|
397
|
-
|
|
376
|
+
|
|
377
|
+
// blocks received
|
|
378
|
+
const blockResults: WantBlockResult[] = []
|
|
379
|
+
const presenceResults: WantPresenceResult[] = []
|
|
398
380
|
|
|
399
381
|
// process blocks
|
|
400
382
|
for (const block of message.blocks) {
|
|
@@ -402,6 +384,7 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
402
384
|
continue
|
|
403
385
|
}
|
|
404
386
|
|
|
387
|
+
this.log('received block')
|
|
405
388
|
const values = vd(block.prefix)
|
|
406
389
|
const cidVersion = values[0]
|
|
407
390
|
const multicodec = values[1]
|
|
@@ -420,55 +403,66 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
|
|
|
420
403
|
|
|
421
404
|
this.log('received block from %p for %c', sender, cid)
|
|
422
405
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
block: block.data
|
|
428
|
-
}
|
|
406
|
+
blockResults.push({
|
|
407
|
+
sender,
|
|
408
|
+
cid,
|
|
409
|
+
block: block.data
|
|
429
410
|
})
|
|
430
411
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
has: true,
|
|
436
|
-
block: block.data
|
|
437
|
-
}
|
|
412
|
+
presenceResults.push({
|
|
413
|
+
sender,
|
|
414
|
+
cid,
|
|
415
|
+
has: true
|
|
438
416
|
})
|
|
417
|
+
}
|
|
439
418
|
|
|
440
|
-
|
|
419
|
+
// process block presences
|
|
420
|
+
for (const { cid: cidBytes, type } of message.blockPresences) {
|
|
421
|
+
const cid = CID.decode(cidBytes)
|
|
422
|
+
|
|
423
|
+
this.log('received %s from %p for %c', type, sender, cid)
|
|
424
|
+
|
|
425
|
+
presenceResults.push({
|
|
426
|
+
sender,
|
|
427
|
+
cid,
|
|
428
|
+
has: type === BlockPresenceType.HaveBlock
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
for (const result of blockResults) {
|
|
433
|
+
const cidStr = uint8ArrayToString(result.cid.multihash.bytes, 'base64')
|
|
441
434
|
const entry = this.wants.get(cidStr)
|
|
442
435
|
|
|
443
436
|
if (entry == null) {
|
|
444
437
|
return
|
|
445
438
|
}
|
|
446
439
|
|
|
440
|
+
const recipients = entry.blockWantListeners
|
|
441
|
+
entry.blockWantListeners = []
|
|
442
|
+
recipients.forEach((p) => {
|
|
443
|
+
p.resolve(result)
|
|
444
|
+
})
|
|
445
|
+
|
|
447
446
|
// since we received the block, flip the cancel flag to send cancels to
|
|
448
447
|
// any peers on the next message sending iteration, this will remove it
|
|
449
448
|
// from the internal want list
|
|
450
449
|
entry.cancel = true
|
|
451
|
-
blocksCancelled = true
|
|
452
450
|
}
|
|
453
451
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const
|
|
452
|
+
for (const result of presenceResults) {
|
|
453
|
+
const cidStr = uint8ArrayToString(result.cid.multihash.bytes, 'base64')
|
|
454
|
+
const entry = this.wants.get(cidStr)
|
|
457
455
|
|
|
458
|
-
|
|
456
|
+
if (entry == null) {
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
459
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
has: type === BlockPresenceType.HaveBlock
|
|
465
|
-
}
|
|
460
|
+
const recipients = entry.blockPresenceListeners
|
|
461
|
+
entry.blockPresenceListeners = []
|
|
462
|
+
recipients.forEach((p) => {
|
|
463
|
+
p.resolve(result)
|
|
466
464
|
})
|
|
467
465
|
}
|
|
468
|
-
|
|
469
|
-
if (blocksCancelled) {
|
|
470
|
-
await this.sendMessagesDebounced()
|
|
471
|
-
}
|
|
472
466
|
}
|
|
473
467
|
|
|
474
468
|
/**
|