@helia/bitswap 0.0.0-329652a
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/LICENSE +4 -0
- package/README.md +64 -0
- package/dist/index.min.js +3 -0
- package/dist/src/bitswap.d.ts +50 -0
- package/dist/src/bitswap.d.ts.map +1 -0
- package/dist/src/bitswap.js +120 -0
- package/dist/src/bitswap.js.map +1 -0
- package/dist/src/constants.d.ts +12 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +12 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/index.d.ts +178 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/network.d.ts +84 -0
- package/dist/src/network.d.ts.map +1 -0
- package/dist/src/network.js +370 -0
- package/dist/src/network.js.map +1 -0
- package/dist/src/pb/message.d.ts +67 -0
- package/dist/src/pb/message.d.ts.map +1 -0
- package/dist/src/pb/message.js +359 -0
- package/dist/src/pb/message.js.map +1 -0
- package/dist/src/peer-want-lists/index.d.ts +44 -0
- package/dist/src/peer-want-lists/index.d.ts.map +1 -0
- package/dist/src/peer-want-lists/index.js +116 -0
- package/dist/src/peer-want-lists/index.js.map +1 -0
- package/dist/src/peer-want-lists/ledger.d.ts +54 -0
- package/dist/src/peer-want-lists/ledger.d.ts.map +1 -0
- package/dist/src/peer-want-lists/ledger.js +104 -0
- package/dist/src/peer-want-lists/ledger.js.map +1 -0
- package/dist/src/session.d.ts +20 -0
- package/dist/src/session.d.ts.map +1 -0
- package/dist/src/session.js +100 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/stats.d.ts +16 -0
- package/dist/src/stats.d.ts.map +1 -0
- package/dist/src/stats.js +49 -0
- package/dist/src/stats.js.map +1 -0
- package/dist/src/utils/cid-prefix.d.ts +3 -0
- package/dist/src/utils/cid-prefix.d.ts.map +1 -0
- package/dist/src/utils/cid-prefix.js +7 -0
- package/dist/src/utils/cid-prefix.js.map +1 -0
- package/dist/src/utils/varint-decoder.d.ts +3 -0
- package/dist/src/utils/varint-decoder.d.ts.map +1 -0
- package/dist/src/utils/varint-decoder.js +15 -0
- package/dist/src/utils/varint-decoder.js.map +1 -0
- package/dist/src/utils/varint-encoder.d.ts +3 -0
- package/dist/src/utils/varint-encoder.d.ts.map +1 -0
- package/dist/src/utils/varint-encoder.js +14 -0
- package/dist/src/utils/varint-encoder.js.map +1 -0
- package/dist/src/want-list.d.ts +120 -0
- package/dist/src/want-list.d.ts.map +1 -0
- package/dist/src/want-list.js +361 -0
- package/dist/src/want-list.js.map +1 -0
- package/package.json +200 -0
- package/src/bitswap.ts +152 -0
- package/src/constants.ts +11 -0
- package/src/index.ts +215 -0
- package/src/network.ts +506 -0
- package/src/pb/message.proto +42 -0
- package/src/pb/message.ts +450 -0
- package/src/peer-want-lists/index.ts +165 -0
- package/src/peer-want-lists/ledger.ts +161 -0
- package/src/session.ts +150 -0
- package/src/stats.ts +67 -0
- package/src/utils/cid-prefix.ts +8 -0
- package/src/utils/varint-decoder.ts +19 -0
- package/src/utils/varint-encoder.ts +18 -0
- package/src/want-list.ts +529 -0
package/src/want-list.ts
ADDED
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { AbortError } from '@libp2p/interface'
|
|
2
|
+
import { trackedPeerMap, PeerSet } from '@libp2p/peer-collections'
|
|
3
|
+
import { trackedMap } from '@libp2p/utils/tracked-map'
|
|
4
|
+
import all from 'it-all'
|
|
5
|
+
import filter from 'it-filter'
|
|
6
|
+
import map from 'it-map'
|
|
7
|
+
import { pipe } from 'it-pipe'
|
|
8
|
+
import { CID } from 'multiformats/cid'
|
|
9
|
+
import { sha256 } from 'multiformats/hashes/sha2'
|
|
10
|
+
import pDefer from 'p-defer'
|
|
11
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
12
|
+
import { DEFAULT_MESSAGE_SEND_DELAY } from './constants.js'
|
|
13
|
+
import { BlockPresenceType, WantType } from './pb/message.js'
|
|
14
|
+
import vd from './utils/varint-decoder.js'
|
|
15
|
+
import type { MultihashHasherLoader } from './index.js'
|
|
16
|
+
import type { BitswapNetworkWantProgressEvents, Network } from './network.js'
|
|
17
|
+
import type { BitswapMessage } from './pb/message.js'
|
|
18
|
+
import type { ComponentLogger, Metrics, PeerId, Startable, AbortOptions } from '@libp2p/interface'
|
|
19
|
+
import type { Logger } from '@libp2p/logger'
|
|
20
|
+
import type { PeerMap } from '@libp2p/peer-collections'
|
|
21
|
+
import type { DeferredPromise } from 'p-defer'
|
|
22
|
+
import type { ProgressOptions } from 'progress-events'
|
|
23
|
+
|
|
24
|
+
export interface WantListComponents {
|
|
25
|
+
network: Network
|
|
26
|
+
logger: ComponentLogger
|
|
27
|
+
metrics?: Metrics
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WantListInit {
|
|
31
|
+
sendMessagesDelay?: number
|
|
32
|
+
hashLoader?: MultihashHasherLoader
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WantListEntry {
|
|
36
|
+
/**
|
|
37
|
+
* The CID we send to the remote
|
|
38
|
+
*/
|
|
39
|
+
cid: CID
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The priority with which the remote should return the block
|
|
43
|
+
*/
|
|
44
|
+
priority: number
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* If we want the block or if we want the remote to tell us if they have the
|
|
48
|
+
* block - note if the block is small they'll send it to us anyway.
|
|
49
|
+
*/
|
|
50
|
+
wantType: WantType
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether we are cancelling the block want or not
|
|
54
|
+
*/
|
|
55
|
+
cancel: boolean
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Whether the remote should tell us if they have the block or not
|
|
59
|
+
*/
|
|
60
|
+
sendDontHave: boolean
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* If this set has members, the want will only be sent to these peers
|
|
64
|
+
*/
|
|
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>>
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface WantOptions extends AbortOptions, ProgressOptions<BitswapNetworkWantProgressEvents> {
|
|
79
|
+
/**
|
|
80
|
+
* If set, this WantList entry will only be sent to this peer
|
|
81
|
+
*/
|
|
82
|
+
peerId?: PeerId
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Allow prioritising blocks
|
|
86
|
+
*/
|
|
87
|
+
priority?: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface WantBlockResult {
|
|
91
|
+
sender: PeerId
|
|
92
|
+
cid: CID
|
|
93
|
+
block: Uint8Array
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface WantDontHaveResult {
|
|
97
|
+
sender: PeerId
|
|
98
|
+
cid: CID
|
|
99
|
+
has: false
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface WantHaveResult {
|
|
103
|
+
sender: PeerId
|
|
104
|
+
cid: CID
|
|
105
|
+
has: true
|
|
106
|
+
block?: Uint8Array
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type WantPresenceResult = WantDontHaveResult | WantHaveResult
|
|
110
|
+
|
|
111
|
+
export class WantList implements Startable {
|
|
112
|
+
/**
|
|
113
|
+
* Tracks what CIDs we've previously sent to which peers
|
|
114
|
+
*/
|
|
115
|
+
public readonly peers: PeerMap<Set<string>>
|
|
116
|
+
public readonly wants: Map<string, WantListEntry>
|
|
117
|
+
private readonly network: Network
|
|
118
|
+
private readonly log: Logger
|
|
119
|
+
private readonly sendMessagesDelay: number
|
|
120
|
+
private sendMessagesTimeout?: ReturnType<typeof setTimeout>
|
|
121
|
+
private readonly hashLoader?: MultihashHasherLoader
|
|
122
|
+
|
|
123
|
+
constructor (components: WantListComponents, init: WantListInit = {}) {
|
|
124
|
+
this.peers = trackedPeerMap({
|
|
125
|
+
name: 'ipfs_bitswap_peers',
|
|
126
|
+
metrics: components.metrics
|
|
127
|
+
})
|
|
128
|
+
this.wants = trackedMap({
|
|
129
|
+
name: 'ipfs_bitswap_wantlist',
|
|
130
|
+
metrics: components.metrics
|
|
131
|
+
})
|
|
132
|
+
this.network = components.network
|
|
133
|
+
this.sendMessagesDelay = init.sendMessagesDelay ?? DEFAULT_MESSAGE_SEND_DELAY
|
|
134
|
+
this.log = components.logger.forComponent('helia:bitswap:wantlist')
|
|
135
|
+
this.hashLoader = init.hashLoader
|
|
136
|
+
|
|
137
|
+
this.network.addEventListener('bitswap:message', (evt) => {
|
|
138
|
+
this.receiveMessage(evt.detail.peer, evt.detail.message)
|
|
139
|
+
.catch(err => {
|
|
140
|
+
this.log.error('error receiving bitswap message from %p', evt.detail.peer, err)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
this.network.addEventListener('peer:connected', evt => {
|
|
144
|
+
this.peerConnected(evt.detail)
|
|
145
|
+
.catch(err => {
|
|
146
|
+
this.log.error('error processing newly connected bitswap peer %p', evt.detail, err)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
this.network.addEventListener('peer:disconnected', evt => {
|
|
150
|
+
this.peerDisconnected(evt.detail)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async addEntry (cid: CID, options: WantOptions & { wantType: WantType.WantBlock }): Promise<WantBlockResult>
|
|
155
|
+
private async addEntry (cid: CID, options: WantOptions & { wantType: WantType.WantHave }): Promise<WantPresenceResult>
|
|
156
|
+
private async addEntry (cid: CID, options: WantOptions & { wantType: WantType }): Promise<WantBlockResult | WantPresenceResult> {
|
|
157
|
+
const cidStr = uint8ArrayToString(cid.multihash.bytes, 'base64')
|
|
158
|
+
let entry = this.wants.get(cidStr)
|
|
159
|
+
|
|
160
|
+
if (entry == null) {
|
|
161
|
+
entry = {
|
|
162
|
+
cid,
|
|
163
|
+
session: new PeerSet(),
|
|
164
|
+
priority: options.priority ?? 1,
|
|
165
|
+
wantType: options.wantType ?? WantType.WantBlock,
|
|
166
|
+
cancel: false,
|
|
167
|
+
sendDontHave: true,
|
|
168
|
+
blockWantListeners: [],
|
|
169
|
+
blockPresenceListeners: []
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (options.peerId != null) {
|
|
173
|
+
entry.session.add(options.peerId)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.wants.set(cidStr, entry)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// upgrade want-have to want-block if the new want is a WantBlock but the
|
|
180
|
+
// previous want was a WantHave
|
|
181
|
+
if (entry.wantType === WantType.WantHave && options.wantType === WantType.WantBlock) {
|
|
182
|
+
entry.wantType = WantType.WantBlock
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// if this want was part of a session..
|
|
186
|
+
if (entry.session.size > 0) {
|
|
187
|
+
// if the new want is also part of a session, expand the want session to
|
|
188
|
+
// include both sets of peers
|
|
189
|
+
if (options.peerId != null) {
|
|
190
|
+
entry.session.add(options.peerId)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// if the new want is not part of a session, make this want a non-session
|
|
194
|
+
// want - nb. this will cause this WantList entry to be sent to every peer
|
|
195
|
+
// instead of just the ones in the session
|
|
196
|
+
if (options.peerId == null) {
|
|
197
|
+
entry.session.clear()
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// add a promise that will be resolved or rejected when the response arrives
|
|
202
|
+
let deferred: DeferredPromise<WantBlockResult | WantPresenceResult>
|
|
203
|
+
|
|
204
|
+
if (options.wantType === WantType.WantBlock) {
|
|
205
|
+
const p = deferred = pDefer<WantBlockResult>()
|
|
206
|
+
|
|
207
|
+
entry.blockWantListeners.push(p)
|
|
208
|
+
} else {
|
|
209
|
+
const p = deferred = pDefer<WantPresenceResult>()
|
|
210
|
+
|
|
211
|
+
entry.blockPresenceListeners.push(p)
|
|
212
|
+
}
|
|
213
|
+
|
|
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) {
|
|
219
|
+
entry.cancel = true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
deferred.reject(new AbortError('Want was aborted'))
|
|
223
|
+
}
|
|
224
|
+
options.signal?.addEventListener('abort', abortListener)
|
|
225
|
+
|
|
226
|
+
// broadcast changes
|
|
227
|
+
clearTimeout(this.sendMessagesTimeout)
|
|
228
|
+
this.sendMessagesTimeout = setTimeout(() => {
|
|
229
|
+
void this.sendMessages()
|
|
230
|
+
.catch(err => {
|
|
231
|
+
this.log('error sending messages to peers', err)
|
|
232
|
+
})
|
|
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
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private async sendMessages (): Promise<void> {
|
|
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
|
+
)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (message.wantlist?.entries.length === 0) {
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// add message to send queue
|
|
294
|
+
try {
|
|
295
|
+
await this.network.sendMessage(peerId, message)
|
|
296
|
+
|
|
297
|
+
// update list of messages sent to remote
|
|
298
|
+
for (const key of sent) {
|
|
299
|
+
sentWants.add(key)
|
|
300
|
+
}
|
|
301
|
+
} catch (err: any) {
|
|
302
|
+
this.log.error('error sending full wantlist to new peer', err)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// queued all message sends, remove cancelled wants from wantlist and sent
|
|
307
|
+
// wants
|
|
308
|
+
for (const [key, entry] of this.wants) {
|
|
309
|
+
if (entry.cancel) {
|
|
310
|
+
this.wants.delete(key)
|
|
311
|
+
|
|
312
|
+
for (const sentWants of this.peers.values()) {
|
|
313
|
+
sentWants.delete(key)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
has (cid: CID): boolean {
|
|
320
|
+
const cidStr = uint8ArrayToString(cid.multihash.bytes, 'base64')
|
|
321
|
+
return this.wants.has(cidStr)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Add a CID to the wantlist
|
|
326
|
+
*/
|
|
327
|
+
async wantPresence (cid: CID, options: WantOptions = {}): Promise<WantPresenceResult> {
|
|
328
|
+
if (options.peerId != null && this.peers.get(options.peerId) == null) {
|
|
329
|
+
const cidStr = uint8ArrayToString(cid.multihash.bytes, 'base64')
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
// if we don't have them as a peer, add them
|
|
333
|
+
this.peers.set(options.peerId, new Set([cidStr]))
|
|
334
|
+
|
|
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)
|
|
350
|
+
|
|
351
|
+
throw err
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return this.addEntry(cid, {
|
|
356
|
+
...options,
|
|
357
|
+
wantType: WantType.WantHave
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Add a CID to the wantlist
|
|
363
|
+
*/
|
|
364
|
+
async wantBlock (cid: CID, options: WantOptions = {}): Promise<WantBlockResult> {
|
|
365
|
+
return this.addEntry(cid, {
|
|
366
|
+
...options,
|
|
367
|
+
wantType: WantType.WantBlock
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Invoked when a message is received from a bitswap peer
|
|
373
|
+
*/
|
|
374
|
+
private async receiveMessage (sender: PeerId, message: BitswapMessage): Promise<void> {
|
|
375
|
+
this.log('received message from %p', sender)
|
|
376
|
+
|
|
377
|
+
// blocks received
|
|
378
|
+
const blockResults: WantBlockResult[] = []
|
|
379
|
+
const presenceResults: WantPresenceResult[] = []
|
|
380
|
+
|
|
381
|
+
// process blocks
|
|
382
|
+
for (const block of message.blocks) {
|
|
383
|
+
if (block.prefix == null || block.data == null) {
|
|
384
|
+
continue
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
this.log('received block')
|
|
388
|
+
const values = vd(block.prefix)
|
|
389
|
+
const cidVersion = values[0]
|
|
390
|
+
const multicodec = values[1]
|
|
391
|
+
const hashAlg = values[2]
|
|
392
|
+
// const hashLen = values[3] // We haven't need to use this so far
|
|
393
|
+
|
|
394
|
+
const hasher = hashAlg === sha256.code ? sha256 : await this.hashLoader?.getHasher(hashAlg)
|
|
395
|
+
|
|
396
|
+
if (hasher == null) {
|
|
397
|
+
this.log.error('unknown hash algorithm', hashAlg)
|
|
398
|
+
continue
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const hash = await hasher.digest(block.data)
|
|
402
|
+
const cid = CID.create(cidVersion === 0 ? 0 : 1, multicodec, hash)
|
|
403
|
+
|
|
404
|
+
this.log('received block from %p for %c', sender, cid)
|
|
405
|
+
|
|
406
|
+
blockResults.push({
|
|
407
|
+
sender,
|
|
408
|
+
cid,
|
|
409
|
+
block: block.data
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
presenceResults.push({
|
|
413
|
+
sender,
|
|
414
|
+
cid,
|
|
415
|
+
has: true
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
|
|
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')
|
|
434
|
+
const entry = this.wants.get(cidStr)
|
|
435
|
+
|
|
436
|
+
if (entry == null) {
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const recipients = entry.blockWantListeners
|
|
441
|
+
entry.blockWantListeners = []
|
|
442
|
+
recipients.forEach((p) => {
|
|
443
|
+
p.resolve(result)
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
// since we received the block, flip the cancel flag to send cancels to
|
|
447
|
+
// any peers on the next message sending iteration, this will remove it
|
|
448
|
+
// from the internal want list
|
|
449
|
+
entry.cancel = true
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
for (const result of presenceResults) {
|
|
453
|
+
const cidStr = uint8ArrayToString(result.cid.multihash.bytes, 'base64')
|
|
454
|
+
const entry = this.wants.get(cidStr)
|
|
455
|
+
|
|
456
|
+
if (entry == null) {
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const recipients = entry.blockPresenceListeners
|
|
461
|
+
entry.blockPresenceListeners = []
|
|
462
|
+
recipients.forEach((p) => {
|
|
463
|
+
p.resolve(result)
|
|
464
|
+
})
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Invoked when the network topology notices a new peer that supports Bitswap
|
|
470
|
+
*/
|
|
471
|
+
async peerConnected (peerId: PeerId): Promise<void> {
|
|
472
|
+
const sentWants = new Set<string>()
|
|
473
|
+
|
|
474
|
+
// new peer, give them the full wantlist
|
|
475
|
+
const message: Partial<BitswapMessage> = {
|
|
476
|
+
wantlist: {
|
|
477
|
+
full: true,
|
|
478
|
+
entries: pipe(
|
|
479
|
+
this.wants.entries(),
|
|
480
|
+
(source) => filter(source, ([key, entry]) => !entry.cancel && (entry.session.size > 0 && !entry.session.has(peerId))),
|
|
481
|
+
(source) => filter(source, ([key, entry]) => !entry.cancel),
|
|
482
|
+
(source) => map(source, ([key, entry]) => {
|
|
483
|
+
sentWants.add(key)
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
cid: entry.cid.bytes,
|
|
487
|
+
priority: 1,
|
|
488
|
+
wantType: WantType.WantBlock,
|
|
489
|
+
cancel: false,
|
|
490
|
+
sendDontHave: false
|
|
491
|
+
}
|
|
492
|
+
}),
|
|
493
|
+
(source) => all(source)
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// only send the wantlist if we have something to send
|
|
499
|
+
if (message.wantlist?.entries.length === 0) {
|
|
500
|
+
this.peers.set(peerId, sentWants)
|
|
501
|
+
|
|
502
|
+
return
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
await this.network.sendMessage(peerId, message)
|
|
507
|
+
|
|
508
|
+
this.peers.set(peerId, sentWants)
|
|
509
|
+
} catch (err) {
|
|
510
|
+
this.log.error('error sending full wantlist to new peer %p', peerId, err)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Invoked when the network topology notices peer that supports Bitswap has
|
|
516
|
+
* disconnected
|
|
517
|
+
*/
|
|
518
|
+
peerDisconnected (peerId: PeerId): void {
|
|
519
|
+
this.peers.delete(peerId)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
start (): void {
|
|
523
|
+
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
stop (): void {
|
|
527
|
+
this.peers.clear()
|
|
528
|
+
}
|
|
529
|
+
}
|