@helia/bitswap 1.0.0 → 1.0.1-0a528bb

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 (52) hide show
  1. package/dist/index.min.js +1 -1
  2. package/dist/src/constants.d.ts +2 -0
  3. package/dist/src/constants.d.ts.map +1 -1
  4. package/dist/src/constants.js +2 -0
  5. package/dist/src/constants.js.map +1 -1
  6. package/dist/src/index.d.ts +20 -7
  7. package/dist/src/index.d.ts.map +1 -1
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/network.d.ts +7 -4
  10. package/dist/src/network.d.ts.map +1 -1
  11. package/dist/src/network.js +55 -131
  12. package/dist/src/network.js.map +1 -1
  13. package/dist/src/pb/message.d.ts +6 -6
  14. package/dist/src/pb/message.d.ts.map +1 -1
  15. package/dist/src/pb/message.js +37 -20
  16. package/dist/src/pb/message.js.map +1 -1
  17. package/dist/src/peer-want-lists/index.d.ts +3 -1
  18. package/dist/src/peer-want-lists/index.d.ts.map +1 -1
  19. package/dist/src/peer-want-lists/index.js +6 -2
  20. package/dist/src/peer-want-lists/index.js.map +1 -1
  21. package/dist/src/peer-want-lists/ledger.d.ts +3 -1
  22. package/dist/src/peer-want-lists/ledger.d.ts.map +1 -1
  23. package/dist/src/peer-want-lists/ledger.js +8 -0
  24. package/dist/src/peer-want-lists/ledger.js.map +1 -1
  25. package/dist/src/stats.d.ts +2 -1
  26. package/dist/src/stats.d.ts.map +1 -1
  27. package/dist/src/stats.js +4 -4
  28. package/dist/src/stats.js.map +1 -1
  29. package/dist/src/utils/merge-messages.d.ts +3 -0
  30. package/dist/src/utils/merge-messages.d.ts.map +1 -0
  31. package/dist/src/utils/merge-messages.js +51 -0
  32. package/dist/src/utils/merge-messages.js.map +1 -0
  33. package/dist/src/utils/split-message.d.ts +18 -0
  34. package/dist/src/utils/split-message.d.ts.map +1 -0
  35. package/dist/src/utils/split-message.js +101 -0
  36. package/dist/src/utils/split-message.js.map +1 -0
  37. package/dist/src/want-list.d.ts +2 -1
  38. package/dist/src/want-list.d.ts.map +1 -1
  39. package/dist/src/want-list.js +8 -5
  40. package/dist/src/want-list.js.map +1 -1
  41. package/package.json +4 -4
  42. package/src/constants.ts +2 -0
  43. package/src/index.ts +22 -8
  44. package/src/network.ts +65 -154
  45. package/src/pb/message.ts +40 -20
  46. package/src/peer-want-lists/index.ts +8 -3
  47. package/src/peer-want-lists/ledger.ts +11 -1
  48. package/src/stats.ts +6 -5
  49. package/src/utils/merge-messages.ts +70 -0
  50. package/src/utils/split-message.ts +133 -0
  51. package/src/want-list.ts +12 -6
  52. package/dist/typedoc-urls.json +0 -20
@@ -0,0 +1,70 @@
1
+ import { base64 } from 'multiformats/bases/base64'
2
+ import type { BitswapMessage, Block, BlockPresence, WantlistEntry } from '../pb/message.js'
3
+
4
+ export function mergeMessages (messageA: BitswapMessage, messageB: BitswapMessage): BitswapMessage {
5
+ const wantListEntries = new Map<string, WantlistEntry>(
6
+ (messageA.wantlist?.entries ?? []).map(entry => ([
7
+ base64.encode(entry.cid),
8
+ entry
9
+ ]))
10
+ )
11
+
12
+ for (const entry of messageB.wantlist?.entries ?? []) {
13
+ const key = base64.encode(entry.cid)
14
+ const existingEntry = wantListEntries.get(key)
15
+
16
+ if (existingEntry != null) {
17
+ // take highest priority
18
+ if (existingEntry.priority > entry.priority) {
19
+ entry.priority = existingEntry.priority
20
+ }
21
+
22
+ // take later values if passed, otherwise use earlier ones
23
+ entry.cancel = entry.cancel ?? existingEntry.cancel
24
+ entry.wantType = entry.wantType ?? existingEntry.wantType
25
+ entry.sendDontHave = entry.sendDontHave ?? existingEntry.sendDontHave
26
+ }
27
+
28
+ wantListEntries.set(key, entry)
29
+ }
30
+
31
+ const blockPresences = new Map<string, BlockPresence>(
32
+ messageA.blockPresences.map(presence => ([
33
+ base64.encode(presence.cid),
34
+ presence
35
+ ]))
36
+ )
37
+
38
+ for (const blockPresence of messageB.blockPresences) {
39
+ const key = base64.encode(blockPresence.cid)
40
+
41
+ // override earlier block presence with later one as if duplicated it is
42
+ // likely to be more accurate since it is more recent
43
+ blockPresences.set(key, blockPresence)
44
+ }
45
+
46
+ const blocks = new Map<string, Block>(
47
+ messageA.blocks.map(block => ([
48
+ base64.encode(block.data),
49
+ block
50
+ ]))
51
+ )
52
+
53
+ for (const block of messageB.blocks) {
54
+ const key = base64.encode(block.data)
55
+
56
+ blocks.set(key, block)
57
+ }
58
+
59
+ const output: BitswapMessage = {
60
+ wantlist: {
61
+ full: messageA.wantlist?.full ?? messageB.wantlist?.full ?? false,
62
+ entries: [...wantListEntries.values()]
63
+ },
64
+ blockPresences: [...blockPresences.values()],
65
+ blocks: [...blocks.values()],
66
+ pendingBytes: messageA.pendingBytes + messageB.pendingBytes
67
+ }
68
+
69
+ return output
70
+ }
@@ -0,0 +1,133 @@
1
+ /* eslint-disable complexity */
2
+ import { CodeError } from '@libp2p/interface'
3
+ import { encodingLength } from 'uint8-varint'
4
+ import { BitswapMessage, Block, BlockPresence, WantlistEntry } from '../pb/message.js'
5
+
6
+ /**
7
+ * https://github.com/ipfs/kubo/issues/4473#issuecomment-350390693
8
+ */
9
+ export const MAX_BLOCK_SIZE = 4193648
10
+ const MAX_ENCODED_BLOCK_SIZE = MAX_BLOCK_SIZE + 16
11
+
12
+ /**
13
+ * Split the passed Bitswap message into multiple smaller messages that when
14
+ * serialized will be under the maximum message size.
15
+ *
16
+ * Since blocks are the largest thing to send, we first try to fit as many
17
+ * blocks as possible into the message, then add (smaller) block presences and
18
+ * wants until the max size is reached.
19
+ *
20
+ * If a block is encountered that is larger than the max message size an error
21
+ * will be thrown.
22
+ */
23
+ export async function * splitMessage (message: BitswapMessage, maxSize: number): AsyncGenerator<Uint8Array> {
24
+ const wantListEntries = message.wantlist?.entries ?? []
25
+ const blockPresences = message.blockPresences
26
+ const blocks = message.blocks
27
+
28
+ let wantListIndex = 0
29
+ let blockPresencesIndex = 0
30
+ let blocksIndex = 0
31
+ let doneSending = false
32
+
33
+ while (true) {
34
+ const subMessage: Required<BitswapMessage> = {
35
+ wantlist: {
36
+ full: message.wantlist?.full ?? false,
37
+ entries: []
38
+ },
39
+ blockPresences: [],
40
+ blocks: [],
41
+ pendingBytes: 0
42
+ }
43
+
44
+ let size = BitswapMessage.encode(subMessage).byteLength
45
+
46
+ let { added, hasMore, newSize } = addToMessage(blocks, subMessage.blocks, blocksIndex, maxSize, size, calculateEncodedBlockSize)
47
+
48
+ blocksIndex += added
49
+ size = newSize
50
+ const haveMoreBlocks = hasMore
51
+
52
+ ;({ added, hasMore, newSize } = addToMessage(blockPresences, subMessage.blockPresences, blockPresencesIndex, maxSize, size, calculateEncodedBlockPresenceSize))
53
+
54
+ blockPresencesIndex += added
55
+ size = newSize
56
+ const haveMorePresences = hasMore
57
+
58
+ ;({ added, hasMore, newSize } = addToMessage(wantListEntries, subMessage.wantlist.entries, wantListIndex, maxSize, size, calculateEncodedWantlistEntrySize))
59
+
60
+ wantListIndex += added
61
+ size = newSize
62
+ const haveMoreWantlistEntries = hasMore
63
+
64
+ doneSending = !haveMoreBlocks && !haveMorePresences && !haveMoreWantlistEntries
65
+
66
+ // if we're sending multiple messages this is no longer the full wantlist
67
+ if (!doneSending) {
68
+ subMessage.wantlist.full = false
69
+ }
70
+
71
+ yield BitswapMessage.encode(subMessage)
72
+
73
+ if (doneSending) {
74
+ break
75
+ }
76
+ }
77
+ }
78
+
79
+ interface AddResult {
80
+ hasMore: boolean
81
+ added: number
82
+ newSize: number
83
+ }
84
+
85
+ function addToMessage <T> (input: T[], output: T[], start: number, maxSize: number, size: number, calculateSize: (arg: T) => number): AddResult {
86
+ let added = 0
87
+ let hasMore = false
88
+
89
+ // try to send as many blocks as possible
90
+ for (let i = start; i < input.length; i++) {
91
+ const item = input[i]
92
+ const itemSize = calculateSize(item)
93
+
94
+ if (itemSize > MAX_ENCODED_BLOCK_SIZE) {
95
+ throw new CodeError('Cannot send block as after encoding it is over the max message size', 'ERR_BLOCK_TOO_LARGE')
96
+ }
97
+
98
+ const newSize = size + itemSize
99
+
100
+ if (newSize > maxSize) {
101
+ hasMore = true
102
+ break
103
+ }
104
+
105
+ output.push(item)
106
+ added++
107
+ size = newSize
108
+ }
109
+
110
+ return { hasMore, added, newSize: size }
111
+ }
112
+
113
+ function calculateEncodedBlockSize (block: Block): number {
114
+ // 3 is the "blocks" field number in message.proto
115
+ return calculateLength(3, Block.encode(block))
116
+ }
117
+
118
+ function calculateEncodedBlockPresenceSize (blockPresence: BlockPresence): number {
119
+ // 4 is the "blockPresences" field number in message.proto
120
+ return calculateLength(4, BlockPresence.encode(blockPresence))
121
+ }
122
+
123
+ function calculateEncodedWantlistEntrySize (entry: WantlistEntry): number {
124
+ // 1 is the "entries" field number in message.proto
125
+ return calculateLength(1, WantlistEntry.encode(entry))
126
+ }
127
+
128
+ function calculateLength (fieldNumber: number, data: Uint8Array): number {
129
+ const fieldNumberLength = encodingLength(fieldNumber)
130
+ const dataLengthLength = encodingLength(data.byteLength)
131
+
132
+ return fieldNumberLength + dataLengthLength + data.byteLength
133
+ }
package/src/want-list.ts CHANGED
@@ -17,7 +17,7 @@ import vd from './utils/varint-decoder.js'
17
17
  import type { BitswapNotifyProgressEvents, MultihashHasherLoader } from './index.js'
18
18
  import type { BitswapNetworkWantProgressEvents, Network } from './network.js'
19
19
  import type { BitswapMessage } from './pb/message.js'
20
- import type { ComponentLogger, PeerId, Startable, AbortOptions, Libp2p, TypedEventTarget } from '@libp2p/interface'
20
+ import type { ComponentLogger, PeerId, Startable, AbortOptions, Libp2p, TypedEventTarget, Metrics } from '@libp2p/interface'
21
21
  import type { Logger } from '@libp2p/logger'
22
22
  import type { PeerMap } from '@libp2p/peer-collections'
23
23
  import type { DeferredPromise } from 'p-defer'
@@ -27,6 +27,7 @@ export interface WantListComponents {
27
27
  network: Network
28
28
  logger: ComponentLogger
29
29
  libp2p: Libp2p
30
+ metrics?: Metrics
30
31
  }
31
32
 
32
33
  export interface WantListInit {
@@ -114,11 +115,11 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
114
115
  setMaxListeners(Infinity, this)
115
116
  this.peers = trackedPeerMap({
116
117
  name: 'helia_bitswap_peers',
117
- metrics: components.libp2p.metrics
118
+ metrics: components.metrics
118
119
  })
119
120
  this.wants = trackedMap({
120
121
  name: 'helia_bitswap_wantlist',
121
- metrics: components.libp2p.metrics
122
+ metrics: components.metrics
122
123
  })
123
124
  this.network = components.network
124
125
  this.sendMessagesDelay = init.sendMessagesDelay ?? DEFAULT_MESSAGE_SEND_DELAY
@@ -375,7 +376,7 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
375
376
  * Invoked when a message is received from a bitswap peer
376
377
  */
377
378
  private async receiveMessage (sender: PeerId, message: BitswapMessage): Promise<void> {
378
- this.log('received message from %p', sender)
379
+ this.log('received message %d from %p with %d blocks', sender, message.blocks.length)
379
380
  let blocksCancelled = false
380
381
 
381
382
  // process blocks
@@ -397,7 +398,12 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
397
398
  continue
398
399
  }
399
400
 
400
- const hash = await hasher.digest(block.data)
401
+ let hash: any = hasher.digest(block.data)
402
+
403
+ if (hash.then != null) {
404
+ hash = await hash
405
+ }
406
+
401
407
  const cid = CID.create(cidVersion === 0 ? 0 : 1, multicodec, hash)
402
408
 
403
409
  this.log('received block from %p for %c', sender, cid)
@@ -423,7 +429,7 @@ export class WantList extends TypedEventEmitter<WantListEvents> implements Start
423
429
  const entry = this.wants.get(cidStr)
424
430
 
425
431
  if (entry == null) {
426
- return
432
+ continue
427
433
  }
428
434
 
429
435
  // since we received the block, flip the cancel flag to send cancels to
@@ -1,20 +0,0 @@
1
- {
2
- "Bitswap": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.Bitswap.html",
3
- ".:Bitswap": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.Bitswap.html",
4
- "BitswapComponents": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.BitswapComponents.html",
5
- ".:BitswapComponents": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.BitswapComponents.html",
6
- "BitswapOptions": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.BitswapOptions.html",
7
- ".:BitswapOptions": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.BitswapOptions.html",
8
- "MultihashHasherLoader": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.MultihashHasherLoader.html",
9
- ".:MultihashHasherLoader": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.MultihashHasherLoader.html",
10
- "WantListEntry": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.WantListEntry.html",
11
- ".:WantListEntry": "https://ipfs.github.io/helia/interfaces/_helia_bitswap.WantListEntry.html",
12
- "BitswapNotifyProgressEvents": "https://ipfs.github.io/helia/types/_helia_bitswap.BitswapNotifyProgressEvents.html",
13
- ".:BitswapNotifyProgressEvents": "https://ipfs.github.io/helia/types/_helia_bitswap.BitswapNotifyProgressEvents.html",
14
- "BitswapWantBlockProgressEvents": "https://ipfs.github.io/helia/types/_helia_bitswap.BitswapWantBlockProgressEvents.html",
15
- ".:BitswapWantBlockProgressEvents": "https://ipfs.github.io/helia/types/_helia_bitswap.BitswapWantBlockProgressEvents.html",
16
- "BitswapWantProgressEvents": "https://ipfs.github.io/helia/types/_helia_bitswap.BitswapWantProgressEvents.html",
17
- ".:BitswapWantProgressEvents": "https://ipfs.github.io/helia/types/_helia_bitswap.BitswapWantProgressEvents.html",
18
- "createBitswap": "https://ipfs.github.io/helia/functions/_helia_bitswap.createBitswap.html",
19
- ".:createBitswap": "https://ipfs.github.io/helia/functions/_helia_bitswap.createBitswap.html"
20
- }