@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.
- package/dist/index.min.js +1 -1
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +2 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/index.d.ts +20 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/network.d.ts +7 -4
- package/dist/src/network.d.ts.map +1 -1
- package/dist/src/network.js +55 -131
- package/dist/src/network.js.map +1 -1
- package/dist/src/pb/message.d.ts +6 -6
- package/dist/src/pb/message.d.ts.map +1 -1
- package/dist/src/pb/message.js +37 -20
- package/dist/src/pb/message.js.map +1 -1
- package/dist/src/peer-want-lists/index.d.ts +3 -1
- package/dist/src/peer-want-lists/index.d.ts.map +1 -1
- package/dist/src/peer-want-lists/index.js +6 -2
- package/dist/src/peer-want-lists/index.js.map +1 -1
- package/dist/src/peer-want-lists/ledger.d.ts +3 -1
- package/dist/src/peer-want-lists/ledger.d.ts.map +1 -1
- package/dist/src/peer-want-lists/ledger.js +8 -0
- package/dist/src/peer-want-lists/ledger.js.map +1 -1
- package/dist/src/stats.d.ts +2 -1
- 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/utils/merge-messages.d.ts +3 -0
- package/dist/src/utils/merge-messages.d.ts.map +1 -0
- package/dist/src/utils/merge-messages.js +51 -0
- package/dist/src/utils/merge-messages.js.map +1 -0
- package/dist/src/utils/split-message.d.ts +18 -0
- package/dist/src/utils/split-message.d.ts.map +1 -0
- package/dist/src/utils/split-message.js +101 -0
- package/dist/src/utils/split-message.js.map +1 -0
- package/dist/src/want-list.d.ts +2 -1
- package/dist/src/want-list.d.ts.map +1 -1
- package/dist/src/want-list.js +8 -5
- package/dist/src/want-list.js.map +1 -1
- package/package.json +4 -4
- package/src/constants.ts +2 -0
- package/src/index.ts +22 -8
- package/src/network.ts +65 -154
- package/src/pb/message.ts +40 -20
- package/src/peer-want-lists/index.ts +8 -3
- package/src/peer-want-lists/ledger.ts +11 -1
- package/src/stats.ts +6 -5
- package/src/utils/merge-messages.ts +70 -0
- package/src/utils/split-message.ts +133 -0
- package/src/want-list.ts +12 -6
- 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.
|
|
118
|
+
metrics: components.metrics
|
|
118
119
|
})
|
|
119
120
|
this.wants = trackedMap({
|
|
120
121
|
name: 'helia_bitswap_wantlist',
|
|
121
|
-
metrics: components.
|
|
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
|
-
|
|
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
|
-
|
|
432
|
+
continue
|
|
427
433
|
}
|
|
428
434
|
|
|
429
435
|
// since we received the block, flip the cancel flag to send cancels to
|
package/dist/typedoc-urls.json
DELETED
|
@@ -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
|
-
}
|