@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.
Files changed (70) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +64 -0
  3. package/dist/index.min.js +3 -0
  4. package/dist/src/bitswap.d.ts +50 -0
  5. package/dist/src/bitswap.d.ts.map +1 -0
  6. package/dist/src/bitswap.js +120 -0
  7. package/dist/src/bitswap.js.map +1 -0
  8. package/dist/src/constants.d.ts +12 -0
  9. package/dist/src/constants.d.ts.map +1 -0
  10. package/dist/src/constants.js +12 -0
  11. package/dist/src/constants.js.map +1 -0
  12. package/dist/src/index.d.ts +178 -0
  13. package/dist/src/index.d.ts.map +1 -0
  14. package/dist/src/index.js +12 -0
  15. package/dist/src/index.js.map +1 -0
  16. package/dist/src/network.d.ts +84 -0
  17. package/dist/src/network.d.ts.map +1 -0
  18. package/dist/src/network.js +370 -0
  19. package/dist/src/network.js.map +1 -0
  20. package/dist/src/pb/message.d.ts +67 -0
  21. package/dist/src/pb/message.d.ts.map +1 -0
  22. package/dist/src/pb/message.js +359 -0
  23. package/dist/src/pb/message.js.map +1 -0
  24. package/dist/src/peer-want-lists/index.d.ts +44 -0
  25. package/dist/src/peer-want-lists/index.d.ts.map +1 -0
  26. package/dist/src/peer-want-lists/index.js +116 -0
  27. package/dist/src/peer-want-lists/index.js.map +1 -0
  28. package/dist/src/peer-want-lists/ledger.d.ts +54 -0
  29. package/dist/src/peer-want-lists/ledger.d.ts.map +1 -0
  30. package/dist/src/peer-want-lists/ledger.js +104 -0
  31. package/dist/src/peer-want-lists/ledger.js.map +1 -0
  32. package/dist/src/session.d.ts +20 -0
  33. package/dist/src/session.d.ts.map +1 -0
  34. package/dist/src/session.js +100 -0
  35. package/dist/src/session.js.map +1 -0
  36. package/dist/src/stats.d.ts +16 -0
  37. package/dist/src/stats.d.ts.map +1 -0
  38. package/dist/src/stats.js +49 -0
  39. package/dist/src/stats.js.map +1 -0
  40. package/dist/src/utils/cid-prefix.d.ts +3 -0
  41. package/dist/src/utils/cid-prefix.d.ts.map +1 -0
  42. package/dist/src/utils/cid-prefix.js +7 -0
  43. package/dist/src/utils/cid-prefix.js.map +1 -0
  44. package/dist/src/utils/varint-decoder.d.ts +3 -0
  45. package/dist/src/utils/varint-decoder.d.ts.map +1 -0
  46. package/dist/src/utils/varint-decoder.js +15 -0
  47. package/dist/src/utils/varint-decoder.js.map +1 -0
  48. package/dist/src/utils/varint-encoder.d.ts +3 -0
  49. package/dist/src/utils/varint-encoder.d.ts.map +1 -0
  50. package/dist/src/utils/varint-encoder.js +14 -0
  51. package/dist/src/utils/varint-encoder.js.map +1 -0
  52. package/dist/src/want-list.d.ts +120 -0
  53. package/dist/src/want-list.d.ts.map +1 -0
  54. package/dist/src/want-list.js +361 -0
  55. package/dist/src/want-list.js.map +1 -0
  56. package/package.json +200 -0
  57. package/src/bitswap.ts +152 -0
  58. package/src/constants.ts +11 -0
  59. package/src/index.ts +215 -0
  60. package/src/network.ts +506 -0
  61. package/src/pb/message.proto +42 -0
  62. package/src/pb/message.ts +450 -0
  63. package/src/peer-want-lists/index.ts +165 -0
  64. package/src/peer-want-lists/ledger.ts +161 -0
  65. package/src/session.ts +150 -0
  66. package/src/stats.ts +67 -0
  67. package/src/utils/cid-prefix.ts +8 -0
  68. package/src/utils/varint-decoder.ts +19 -0
  69. package/src/utils/varint-encoder.ts +18 -0
  70. package/src/want-list.ts +529 -0
package/src/network.ts ADDED
@@ -0,0 +1,506 @@
1
+ import { CodeError, TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
2
+ import { PeerQueue, type PeerQueueJobOptions } from '@libp2p/utils/peer-queue'
3
+ import { Circuit } from '@multiformats/multiaddr-matcher'
4
+ import { anySignal } from 'any-signal'
5
+ import debug from 'debug'
6
+ import drain from 'it-drain'
7
+ import * as lp from 'it-length-prefixed'
8
+ import { lpStream } from 'it-length-prefixed-stream'
9
+ import map from 'it-map'
10
+ import { pipe } from 'it-pipe'
11
+ import take from 'it-take'
12
+ import { base64 } from 'multiformats/bases/base64'
13
+ import { CID } from 'multiformats/cid'
14
+ import { CustomProgressEvent } from 'progress-events'
15
+ import { raceEvent } from 'race-event'
16
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
17
+ import { BITSWAP_120, DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS, DEFAULT_MAX_PROVIDERS_PER_REQUEST, DEFAULT_MESSAGE_RECEIVE_TIMEOUT, DEFAULT_MESSAGE_SEND_TIMEOUT, DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS } from './constants.js'
18
+ import { BitswapMessage } from './pb/message.js'
19
+ import type { WantOptions } from './bitswap.js'
20
+ import type { MultihashHasherLoader } from './index.js'
21
+ import type { Block, BlockPresence, WantlistEntry } from './pb/message.js'
22
+ import type { Provider, Routing } from '@helia/interface/routing'
23
+ import type { Libp2p, AbortOptions, Connection, PeerId, IncomingStreamData, Topology, MetricGroup, ComponentLogger, Metrics, IdentifyResult } from '@libp2p/interface'
24
+ import type { Logger } from '@libp2p/logger'
25
+ import type { ProgressEvent, ProgressOptions } from 'progress-events'
26
+
27
+ // Add a formatter for a bitswap message
28
+ debug.formatters.B = (b?: BitswapMessage): string => {
29
+ if (b == null) {
30
+ return 'undefined'
31
+ }
32
+
33
+ return JSON.stringify({
34
+ blocks: b.blocks?.map(b => ({
35
+ data: `${uint8ArrayToString(b.data, 'base64').substring(0, 10)}...`,
36
+ prefix: uint8ArrayToString(b.prefix, 'base64')
37
+ })),
38
+ blockPresences: b.blockPresences?.map(p => ({
39
+ ...p,
40
+ cid: CID.decode(p.cid).toString()
41
+ })),
42
+ wantlist: b.wantlist == null
43
+ ? undefined
44
+ : {
45
+ full: b.wantlist.full,
46
+ entries: b.wantlist.entries.map(e => ({
47
+ ...e,
48
+ cid: CID.decode(e.cid).toString()
49
+ }))
50
+ }
51
+ }, null, 2)
52
+ }
53
+
54
+ export type BitswapNetworkProgressEvents =
55
+ ProgressEvent<'bitswap:network:dial', PeerId>
56
+
57
+ export type BitswapNetworkWantProgressEvents =
58
+ ProgressEvent<'bitswap:network:send-wantlist', PeerId> |
59
+ ProgressEvent<'bitswap:network:send-wantlist:error', { peer: PeerId, error: Error }> |
60
+ ProgressEvent<'bitswap:network:find-providers', CID> |
61
+ BitswapNetworkProgressEvents
62
+
63
+ export type BitswapNetworkNotifyProgressEvents =
64
+ BitswapNetworkProgressEvents |
65
+ ProgressEvent<'bitswap:network:send-block', PeerId>
66
+
67
+ export interface NetworkInit {
68
+ hashLoader?: MultihashHasherLoader
69
+ maxInboundStreams?: number
70
+ maxOutboundStreams?: number
71
+ messageReceiveTimeout?: number
72
+ messageSendTimeout?: number
73
+ messageSendConcurrency?: number
74
+ protocols?: string[]
75
+ runOnTransientConnections?: boolean
76
+ }
77
+
78
+ export interface NetworkComponents {
79
+ routing: Routing
80
+ logger: ComponentLogger
81
+ libp2p: Libp2p
82
+ metrics?: Metrics
83
+ }
84
+
85
+ export interface BitswapMessageEventDetail {
86
+ peer: PeerId
87
+ message: BitswapMessage
88
+ }
89
+
90
+ export interface NetworkEvents {
91
+ 'bitswap:message': CustomEvent<{ peer: PeerId, message: BitswapMessage }>
92
+ 'peer:connected': CustomEvent<PeerId>
93
+ 'peer:disconnected': CustomEvent<PeerId>
94
+ }
95
+
96
+ interface SendMessageJobOptions extends AbortOptions, ProgressOptions, PeerQueueJobOptions {
97
+ message: BitswapMessage
98
+ }
99
+
100
+ export class Network extends TypedEventEmitter<NetworkEvents> {
101
+ private readonly log: Logger
102
+ private readonly libp2p: Libp2p
103
+ private readonly routing: Routing
104
+ private readonly protocols: string[]
105
+ private running: boolean
106
+ private readonly maxInboundStreams: number
107
+ private readonly maxOutboundStreams: number
108
+ private readonly messageReceiveTimeout: number
109
+ private registrarIds: string[]
110
+ private readonly metrics?: { blocksSent: MetricGroup, dataSent: MetricGroup }
111
+ private readonly sendQueue: PeerQueue<void, SendMessageJobOptions>
112
+ private readonly messageSendTimeout: number
113
+ private readonly runOnTransientConnections: boolean
114
+
115
+ constructor (components: NetworkComponents, init: NetworkInit = {}) {
116
+ super()
117
+
118
+ this.log = components.logger.forComponent('helia:bitswap:network')
119
+ this.libp2p = components.libp2p
120
+ this.routing = components.routing
121
+ this.protocols = init.protocols ?? [BITSWAP_120]
122
+ this.registrarIds = []
123
+ this.running = false
124
+
125
+ // bind event listeners
126
+ this._onStream = this._onStream.bind(this)
127
+ this.maxInboundStreams = init.maxInboundStreams ?? DEFAULT_MAX_INBOUND_STREAMS
128
+ this.maxOutboundStreams = init.maxOutboundStreams ?? DEFAULT_MAX_OUTBOUND_STREAMS
129
+ this.messageReceiveTimeout = init.messageReceiveTimeout ?? DEFAULT_MESSAGE_RECEIVE_TIMEOUT
130
+ this.messageSendTimeout = init.messageSendTimeout ?? DEFAULT_MESSAGE_SEND_TIMEOUT
131
+ this.runOnTransientConnections = init.runOnTransientConnections ?? DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS
132
+
133
+ if (components.metrics != null) {
134
+ this.metrics = {
135
+ blocksSent: components.metrics?.registerMetricGroup('ipfs_bitswap_sent_blocks'),
136
+ dataSent: components.metrics?.registerMetricGroup('ipfs_bitswap_sent_data_bytes')
137
+ }
138
+ }
139
+
140
+ this.sendQueue = new PeerQueue({
141
+ concurrency: init.messageSendConcurrency,
142
+ metrics: components.metrics,
143
+ metricName: 'ipfs_bitswap_message_send_queue'
144
+ })
145
+ this.sendQueue.addEventListener('error', (evt) => {
146
+ this.log.error('error sending wantlist to peer', evt.detail)
147
+ })
148
+ }
149
+
150
+ async start (): Promise<void> {
151
+ if (this.running) {
152
+ return
153
+ }
154
+
155
+ this.running = true
156
+
157
+ await this.libp2p.handle(this.protocols, this._onStream, {
158
+ maxInboundStreams: this.maxInboundStreams,
159
+ maxOutboundStreams: this.maxOutboundStreams,
160
+ runOnTransientConnection: this.runOnTransientConnections
161
+ })
162
+
163
+ // register protocol with topology
164
+ const topology: Topology = {
165
+ onConnect: (peerId: PeerId) => {
166
+ this.safeDispatchEvent('peer:connected', {
167
+ detail: peerId
168
+ })
169
+ },
170
+ onDisconnect: (peerId: PeerId) => {
171
+ this.safeDispatchEvent('peer:disconnected', {
172
+ detail: peerId
173
+ })
174
+ }
175
+ }
176
+
177
+ this.registrarIds = []
178
+
179
+ for (const protocol of this.protocols) {
180
+ this.registrarIds.push(await this.libp2p.register(protocol, topology))
181
+ }
182
+
183
+ // All existing connections are like new ones for us
184
+ this.libp2p.getConnections().forEach(conn => {
185
+ this.safeDispatchEvent('peer:connected', {
186
+ detail: conn.remotePeer
187
+ })
188
+ })
189
+ }
190
+
191
+ async stop (): Promise<void> {
192
+ this.running = false
193
+
194
+ // Unhandle both, libp2p doesn't care if it's not already handled
195
+ await this.libp2p.unhandle(this.protocols)
196
+
197
+ // unregister protocol and handlers
198
+ if (this.registrarIds != null) {
199
+ for (const id of this.registrarIds) {
200
+ this.libp2p.unregister(id)
201
+ }
202
+
203
+ this.registrarIds = []
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Handles incoming bitswap messages
209
+ */
210
+ _onStream (info: IncomingStreamData): void {
211
+ if (!this.running) {
212
+ return
213
+ }
214
+
215
+ const { stream, connection } = info
216
+
217
+ Promise.resolve().then(async () => {
218
+ this.log('incoming new bitswap %s stream from %p', stream.protocol, connection.remotePeer)
219
+ const abortListener = (): void => {
220
+ stream.abort(new CodeError('Incoming Bitswap stream timed out', 'ERR_TIMEOUT'))
221
+ }
222
+
223
+ let signal = AbortSignal.timeout(this.messageReceiveTimeout)
224
+ setMaxListeners(Infinity, signal)
225
+ signal.addEventListener('abort', abortListener)
226
+
227
+ await pipe(
228
+ stream,
229
+ (source) => lp.decode(source),
230
+ async (source) => {
231
+ for await (const data of source) {
232
+ try {
233
+ const message = BitswapMessage.decode(data)
234
+ this.log('incoming new bitswap %s message from %p %B', stream.protocol, connection.remotePeer, message)
235
+
236
+ this.safeDispatchEvent('bitswap:message', {
237
+ detail: {
238
+ peer: connection.remotePeer,
239
+ message
240
+ }
241
+ })
242
+
243
+ // we have received some data so reset the timeout controller
244
+ signal.removeEventListener('abort', abortListener)
245
+ signal = AbortSignal.timeout(this.messageReceiveTimeout)
246
+ setMaxListeners(Infinity, signal)
247
+ signal.addEventListener('abort', abortListener)
248
+ } catch (err: any) {
249
+ this.log.error('error reading incoming bitswap message from %p', connection.remotePeer, err)
250
+ stream.abort(err)
251
+ break
252
+ }
253
+ }
254
+ }
255
+ )
256
+ })
257
+ .catch(err => {
258
+ this.log.error('error handling incoming stream from %p', connection.remotePeer, err)
259
+ stream.abort(err)
260
+ })
261
+ }
262
+
263
+ /**
264
+ * Find bitswap providers for a given `cid`.
265
+ */
266
+ async * findProviders (cid: CID, options?: AbortOptions & ProgressOptions<BitswapNetworkWantProgressEvents>): AsyncIterable<Provider> {
267
+ options?.onProgress?.(new CustomProgressEvent<PeerId>('bitswap:network:find-providers', cid))
268
+
269
+ for await (const provider of this.routing.findProviders(cid, options)) {
270
+ // unless we explicitly run on transient connections, skip peers that only
271
+ // have circuit relay addresses as bitswap won't run over them
272
+ if (!this.runOnTransientConnections) {
273
+ let hasDirectAddress = false
274
+
275
+ for (let ma of provider.multiaddrs) {
276
+ if (ma.getPeerId() == null) {
277
+ ma = ma.encapsulate(`/p2p/${provider.id}`)
278
+ }
279
+
280
+ if (!Circuit.exactMatch(ma)) {
281
+ hasDirectAddress = true
282
+ break
283
+ }
284
+ }
285
+
286
+ if (!hasDirectAddress) {
287
+ continue
288
+ }
289
+ }
290
+
291
+ // ignore non-bitswap providers
292
+ if (provider.protocols?.includes('transport-bitswap') === false) {
293
+ continue
294
+ }
295
+
296
+ yield provider
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Find the providers of a given `cid` and connect to them.
302
+ */
303
+ async findAndConnect (cid: CID, options?: WantOptions): Promise<void> {
304
+ await drain(
305
+ take(
306
+ map(this.findProviders(cid, options), async provider => this.connectTo(provider.id, options)),
307
+ options?.maxProviders ?? DEFAULT_MAX_PROVIDERS_PER_REQUEST
308
+ )
309
+ )
310
+ .catch(err => {
311
+ this.log.error(err)
312
+ })
313
+ }
314
+
315
+ /**
316
+ * Connect to the given peer
317
+ * Send the given msg (instance of Message) to the given peer
318
+ */
319
+ async sendMessage (peerId: PeerId, msg: Partial<BitswapMessage>, options?: AbortOptions & ProgressOptions<BitswapNetworkWantProgressEvents>): Promise<void> {
320
+ if (!this.running) {
321
+ throw new Error('network isn\'t running')
322
+ }
323
+
324
+ const message: BitswapMessage = {
325
+ wantlist: {
326
+ full: msg.wantlist?.full ?? false,
327
+ entries: msg.wantlist?.entries ?? []
328
+ },
329
+ blocks: msg.blocks ?? [],
330
+ blockPresences: msg.blockPresences ?? [],
331
+ pendingBytes: msg.pendingBytes ?? 0
332
+ }
333
+
334
+ const signal = anySignal([AbortSignal.timeout(this.messageSendTimeout), options?.signal])
335
+ setMaxListeners(Infinity, signal)
336
+
337
+ try {
338
+ const existingJob = this.sendQueue.find(peerId)
339
+
340
+ if (existingJob?.status === 'queued') {
341
+ // merge messages instead of adding new job
342
+ existingJob.options.message = mergeMessages(existingJob.options.message, message)
343
+
344
+ await existingJob.join({
345
+ signal
346
+ })
347
+
348
+ return
349
+ }
350
+
351
+ await this.sendQueue.add(async (options) => {
352
+ const message = options?.message
353
+
354
+ if (message == null) {
355
+ throw new CodeError('No message to send', 'ERR_NO_MESSAGE')
356
+ }
357
+
358
+ this.log('sendMessage to %p %B', peerId, message)
359
+
360
+ options?.onProgress?.(new CustomProgressEvent<PeerId>('bitswap:network:send-wantlist', peerId))
361
+
362
+ const stream = await this.libp2p.dialProtocol(peerId, BITSWAP_120, options)
363
+
364
+ try {
365
+ const lp = lpStream(stream)
366
+ await lp.write(BitswapMessage.encode(message), options)
367
+ await lp.unwrap().close(options)
368
+ } catch (err: any) {
369
+ options?.onProgress?.(new CustomProgressEvent<{ peer: PeerId, error: Error }>('bitswap:network:send-wantlist:error', { peer: peerId, error: err }))
370
+ this.log.error('error sending message to %p', peerId, err)
371
+ stream.abort(err)
372
+ }
373
+
374
+ this._updateSentStats(peerId, message.blocks)
375
+ }, {
376
+ peerId,
377
+ signal,
378
+ message
379
+ })
380
+ } finally {
381
+ signal.clear()
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Connects to another peer
387
+ */
388
+ async connectTo (peer: PeerId, options?: AbortOptions & ProgressOptions<BitswapNetworkProgressEvents>): Promise<Connection> { // eslint-disable-line require-await
389
+ if (!this.running) {
390
+ throw new CodeError('Network isn\'t running', 'ERR_NOT_STARTED')
391
+ }
392
+
393
+ options?.onProgress?.(new CustomProgressEvent<PeerId>('bitswap:network:dial', peer))
394
+
395
+ // dial and wait for identify - this is to avoid opening a protocol stream
396
+ // that we are not going to use but depends on the remote node running the
397
+ // identitfy protocol
398
+ const [
399
+ connection
400
+ ] = await Promise.all([
401
+ this.libp2p.dial(peer, options),
402
+ raceEvent(this.libp2p, 'peer:identify', options?.signal, {
403
+ filter: (evt: CustomEvent<IdentifyResult>): boolean => {
404
+ if (!evt.detail.peerId.equals(peer)) {
405
+ return false
406
+ }
407
+
408
+ if (evt.detail.protocols.includes(BITSWAP_120)) {
409
+ return true
410
+ }
411
+
412
+ throw new CodeError(`${peer} did not support ${BITSWAP_120}`, 'ERR_BITSWAP_UNSUPPORTED_BY_PEER')
413
+ }
414
+ })
415
+ ])
416
+
417
+ return connection
418
+ }
419
+
420
+ _updateSentStats (peerId: PeerId, blocks: Block[] = []): void {
421
+ if (this.metrics != null) {
422
+ let bytes = 0
423
+
424
+ for (const block of blocks.values()) {
425
+ bytes += block.data.byteLength
426
+ }
427
+
428
+ this.metrics.dataSent.increment({
429
+ global: bytes,
430
+ [peerId.toString()]: bytes
431
+ })
432
+ this.metrics.blocksSent.increment({
433
+ global: blocks.length,
434
+ [peerId.toString()]: blocks.length
435
+ })
436
+ }
437
+ }
438
+ }
439
+
440
+ function mergeMessages (messageA: BitswapMessage, messageB: BitswapMessage): BitswapMessage {
441
+ const wantListEntries = new Map<string, WantlistEntry>(
442
+ (messageA.wantlist?.entries ?? []).map(entry => ([
443
+ base64.encode(entry.cid),
444
+ entry
445
+ ]))
446
+ )
447
+
448
+ for (const entry of messageB.wantlist?.entries ?? []) {
449
+ const key = base64.encode(entry.cid)
450
+ const existingEntry = wantListEntries.get(key)
451
+
452
+ if (existingEntry != null) {
453
+ // take highest priority
454
+ if (existingEntry.priority > entry.priority) {
455
+ entry.priority = existingEntry.priority
456
+ }
457
+
458
+ // take later values if passed, otherwise use earlier ones
459
+ entry.cancel = entry.cancel ?? existingEntry.cancel
460
+ entry.wantType = entry.wantType ?? existingEntry.wantType
461
+ entry.sendDontHave = entry.sendDontHave ?? existingEntry.sendDontHave
462
+ }
463
+
464
+ wantListEntries.set(key, entry)
465
+ }
466
+
467
+ const blockPresences = new Map<string, BlockPresence>(
468
+ messageA.blockPresences.map(presence => ([
469
+ base64.encode(presence.cid),
470
+ presence
471
+ ]))
472
+ )
473
+
474
+ for (const blockPresence of messageB.blockPresences) {
475
+ const key = base64.encode(blockPresence.cid)
476
+
477
+ // override earlier block presence with later one as if duplicated it is
478
+ // likely to be more accurate since it is more recent
479
+ blockPresences.set(key, blockPresence)
480
+ }
481
+
482
+ const blocks = new Map<string, Block>(
483
+ messageA.blocks.map(block => ([
484
+ base64.encode(block.data),
485
+ block
486
+ ]))
487
+ )
488
+
489
+ for (const block of messageB.blocks) {
490
+ const key = base64.encode(block.data)
491
+
492
+ blocks.set(key, block)
493
+ }
494
+
495
+ const output: BitswapMessage = {
496
+ wantlist: {
497
+ full: messageA.wantlist?.full ?? messageB.wantlist?.full ?? false,
498
+ entries: [...wantListEntries.values()]
499
+ },
500
+ blockPresences: [...blockPresences.values()],
501
+ blocks: [...blocks.values()],
502
+ pendingBytes: messageA.pendingBytes + messageB.pendingBytes
503
+ }
504
+
505
+ return output
506
+ }
@@ -0,0 +1,42 @@
1
+ // adapted from https://github.com/ipfs/boxo/blob/main/bitswap/message/pb/message.proto
2
+ syntax = "proto3";
3
+
4
+ enum WantType {
5
+ WantBlock = 0; // send me the block for the CID
6
+ WantHave = 1; // just tell me if you have the block for the CID or send it if it's really small
7
+ }
8
+
9
+ message WantlistEntry {
10
+ bytes cid = 1; // the block cid (cidV0 in bitswap 1.0.0, cidV1 in bitswap 1.1.0)
11
+ int32 priority = 2; // the priority (normalized). default to 1
12
+ optional bool cancel = 3; // whether this revokes an entry
13
+ optional WantType wantType = 4; // Note: defaults to enum 0, ie Block
14
+ optional bool sendDontHave = 5; // Note: defaults to false
15
+ }
16
+
17
+ message Wantlist {
18
+ repeated WantlistEntry entries = 1; // a list of wantlist entries
19
+ optional bool full = 2; // whether this is the full wantlist. default to false
20
+ }
21
+
22
+ message Block {
23
+ bytes prefix = 1; // CID prefix (cid version, multicodec and multihash prefix (type + length)
24
+ bytes data = 2;
25
+ }
26
+
27
+ enum BlockPresenceType {
28
+ HaveBlock = 0;
29
+ DontHaveBlock = 1;
30
+ }
31
+
32
+ message BlockPresence {
33
+ bytes cid = 1;
34
+ BlockPresenceType type = 2;
35
+ }
36
+
37
+ message BitswapMessage {
38
+ Wantlist wantlist = 1;
39
+ repeated Block blocks = 3; // used to send Blocks in bitswap 1.1.0
40
+ repeated BlockPresence blockPresences = 4;
41
+ int32 pendingBytes = 5;
42
+ }