@helia/bitswap 0.0.0 → 1.0.0-59de059
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 +6 -1
- package/dist/src/bitswap.d.ts +3 -2
- package/dist/src/bitswap.d.ts.map +1 -1
- package/dist/src/bitswap.js +7 -14
- package/dist/src/bitswap.js.map +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 +22 -45
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/network.d.ts +8 -7
- package/dist/src/network.d.ts.map +1 -1
- package/dist/src/network.js +71 -169
- 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 -2
- package/dist/src/peer-want-lists/index.d.ts.map +1 -1
- package/dist/src/peer-want-lists/index.js +7 -3
- 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/session.d.ts +13 -8
- package/dist/src/session.d.ts.map +1 -1
- package/dist/src/session.js +25 -88
- 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/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 +14 -0
- package/dist/src/utils/split-message.d.ts.map +1 -0
- package/dist/src/utils/split-message.js +99 -0
- package/dist/src/utils/split-message.js.map +1 -0
- package/dist/src/want-list.d.ts +20 -23
- package/dist/src/want-list.d.ts.map +1 -1
- package/dist/src/want-list.js +136 -133
- package/dist/src/want-list.js.map +1 -1
- package/package.json +6 -6
- package/src/bitswap.ts +9 -16
- package/src/constants.ts +2 -0
- package/src/index.ts +24 -51
- package/src/network.ts +83 -200
- package/src/pb/message.ts +40 -20
- package/src/peer-want-lists/index.ts +9 -5
- package/src/peer-want-lists/ledger.ts +11 -1
- package/src/session.ts +31 -120
- package/src/stats.ts +6 -6
- package/src/utils/merge-messages.ts +70 -0
- package/src/utils/split-message.ts +131 -0
- package/src/want-list.ts +205 -212
- package/dist/typedoc-urls.json +0 -24
package/src/index.ts
CHANGED
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
import { Bitswap as BitswapClass } from './bitswap.js'
|
|
10
10
|
import type { BitswapNetworkNotifyProgressEvents, BitswapNetworkWantProgressEvents } from './network.js'
|
|
11
11
|
import type { WantType } from './pb/message.js'
|
|
12
|
-
import type { CreateSessionOptions } from '@helia/interface'
|
|
12
|
+
import type { BlockBroker, CreateSessionOptions } from '@helia/interface'
|
|
13
13
|
import type { Routing } from '@helia/interface/routing'
|
|
14
14
|
import type { Libp2p, AbortOptions, Startable, ComponentLogger, Metrics, PeerId } from '@libp2p/interface'
|
|
15
|
-
import type { PeerSet } from '@libp2p/peer-collections'
|
|
16
15
|
import type { Blockstore } from 'interface-blockstore'
|
|
17
16
|
import type { CID } from 'multiformats/cid'
|
|
18
17
|
import type { MultihashHasher } from 'multiformats/hashes/interface'
|
|
@@ -29,52 +28,12 @@ export type BitswapWantBlockProgressEvents =
|
|
|
29
28
|
ProgressEvent<'bitswap:want-block:block', CID> |
|
|
30
29
|
BitswapNetworkWantProgressEvents
|
|
31
30
|
|
|
32
|
-
/**
|
|
33
|
-
* A bitswap session is a network overlay consisting of peers that all have the
|
|
34
|
-
* first block in a file. Subsequent requests will only go to these peers.
|
|
35
|
-
*/
|
|
36
|
-
export interface BitswapSession {
|
|
37
|
-
/**
|
|
38
|
-
* The peers in this session
|
|
39
|
-
*/
|
|
40
|
-
peers: PeerSet
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Fetch an additional CID from this DAG
|
|
44
|
-
*/
|
|
45
|
-
want(cid: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<Uint8Array>
|
|
46
|
-
}
|
|
47
|
-
|
|
48
31
|
export interface WantListEntry {
|
|
49
32
|
cid: CID
|
|
50
33
|
priority: number
|
|
51
34
|
wantType: WantType
|
|
52
35
|
}
|
|
53
36
|
|
|
54
|
-
export interface CreateBitswapSessionOptions extends CreateSessionOptions<BitswapWantProgressEvents> {
|
|
55
|
-
/**
|
|
56
|
-
* If true, query connected peers before searching for providers via
|
|
57
|
-
* Helia routers
|
|
58
|
-
*
|
|
59
|
-
* @default true
|
|
60
|
-
*/
|
|
61
|
-
queryConnectedPeers?: boolean
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* If true, search for providers via Helia routers to query for the root CID
|
|
65
|
-
*
|
|
66
|
-
* @default true
|
|
67
|
-
*/
|
|
68
|
-
queryRoutingPeers?: boolean
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* The priority to use when querying availability of the root CID
|
|
72
|
-
*
|
|
73
|
-
* @default 1
|
|
74
|
-
*/
|
|
75
|
-
priority?: number
|
|
76
|
-
}
|
|
77
|
-
|
|
78
37
|
export interface Bitswap extends Startable {
|
|
79
38
|
/**
|
|
80
39
|
* Returns the current state of the wantlist
|
|
@@ -100,7 +59,7 @@ export interface Bitswap extends Startable {
|
|
|
100
59
|
/**
|
|
101
60
|
* Start a session to retrieve a file from the network
|
|
102
61
|
*/
|
|
103
|
-
createSession(
|
|
62
|
+
createSession(options?: CreateSessionOptions<BitswapWantProgressEvents>): Required<Pick<BlockBroker<BitswapWantProgressEvents>, 'retrieve'>>
|
|
104
63
|
}
|
|
105
64
|
|
|
106
65
|
export interface MultihashHasherLoader {
|
|
@@ -158,14 +117,6 @@ export interface BitswapOptions {
|
|
|
158
117
|
*/
|
|
159
118
|
protocol?: string
|
|
160
119
|
|
|
161
|
-
/**
|
|
162
|
-
* When a new peer connects, sending our WantList should complete within this
|
|
163
|
-
* many ms
|
|
164
|
-
*
|
|
165
|
-
* @default 5000
|
|
166
|
-
*/
|
|
167
|
-
messageSendTimeout?: number
|
|
168
|
-
|
|
169
120
|
/**
|
|
170
121
|
* When sending want list updates to peers, how many messages to send at once
|
|
171
122
|
*
|
|
@@ -208,6 +159,28 @@ export interface BitswapOptions {
|
|
|
208
159
|
* @default 1024
|
|
209
160
|
*/
|
|
210
161
|
maxSizeReplaceHasWithBlock?: number
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The maximum size in bytes of a message that we will send. If a message is
|
|
165
|
+
* larger than this (due to lots of blocks or wantlist entries) it will be
|
|
166
|
+
* broken up into several smaller messages that are under this size.
|
|
167
|
+
*
|
|
168
|
+
* @see https://github.com/ipfs/boxo/blob/eeea414587350401b6b804f0574ed8436833331d/bitswap/client/internal/messagequeue/messagequeue.go#L33
|
|
169
|
+
*
|
|
170
|
+
* @default 2097152
|
|
171
|
+
*/
|
|
172
|
+
maxOutgoingMessageSize?: number
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The maximum size in bytes of an incoming message that we will process.
|
|
176
|
+
*
|
|
177
|
+
* Messages larger than this will cause the incoming stream to be reset.
|
|
178
|
+
*
|
|
179
|
+
* Defaults to `maxOutgoingMessageSize`
|
|
180
|
+
*
|
|
181
|
+
* @default 2097152
|
|
182
|
+
*/
|
|
183
|
+
maxIncomingMessageSize?: number
|
|
211
184
|
}
|
|
212
185
|
|
|
213
186
|
export const createBitswap = (components: BitswapComponents, options: BitswapOptions = {}): Bitswap => {
|
package/src/network.ts
CHANGED
|
@@ -1,56 +1,25 @@
|
|
|
1
1
|
import { CodeError, TypedEventEmitter, setMaxListeners } from '@libp2p/interface'
|
|
2
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
3
|
import drain from 'it-drain'
|
|
7
4
|
import * as lp from 'it-length-prefixed'
|
|
8
|
-
import { lpStream } from 'it-length-prefixed-stream'
|
|
9
5
|
import map from 'it-map'
|
|
10
6
|
import { pipe } from 'it-pipe'
|
|
11
7
|
import take from 'it-take'
|
|
12
|
-
import { base64 } from 'multiformats/bases/base64'
|
|
13
|
-
import { CID } from 'multiformats/cid'
|
|
14
8
|
import { CustomProgressEvent } from 'progress-events'
|
|
15
9
|
import { raceEvent } from 'race-event'
|
|
16
|
-
import {
|
|
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'
|
|
10
|
+
import { BITSWAP_120, DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_INCOMING_MESSAGE_SIZE, DEFAULT_MAX_OUTBOUND_STREAMS, DEFAULT_MAX_OUTGOING_MESSAGE_SIZE, DEFAULT_MAX_PROVIDERS_PER_REQUEST, DEFAULT_MESSAGE_RECEIVE_TIMEOUT, DEFAULT_MESSAGE_SEND_CONCURRENCY, DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS } from './constants.js'
|
|
18
11
|
import { BitswapMessage } from './pb/message.js'
|
|
12
|
+
import { mergeMessages } from './utils/merge-messages.js'
|
|
13
|
+
import { splitMessage } from './utils/split-message.js'
|
|
19
14
|
import type { WantOptions } from './bitswap.js'
|
|
20
15
|
import type { MultihashHasherLoader } from './index.js'
|
|
21
|
-
import type { Block
|
|
16
|
+
import type { Block } from './pb/message.js'
|
|
22
17
|
import type { Provider, Routing } from '@helia/interface/routing'
|
|
23
|
-
import type { Libp2p, AbortOptions, Connection, PeerId, IncomingStreamData, Topology,
|
|
18
|
+
import type { Libp2p, AbortOptions, Connection, PeerId, IncomingStreamData, Topology, ComponentLogger, IdentifyResult, Counter } from '@libp2p/interface'
|
|
24
19
|
import type { Logger } from '@libp2p/logger'
|
|
20
|
+
import type { CID } from 'multiformats/cid'
|
|
25
21
|
import type { ProgressEvent, ProgressOptions } from 'progress-events'
|
|
26
22
|
|
|
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
23
|
export type BitswapNetworkProgressEvents =
|
|
55
24
|
ProgressEvent<'bitswap:network:dial', PeerId>
|
|
56
25
|
|
|
@@ -69,17 +38,17 @@ export interface NetworkInit {
|
|
|
69
38
|
maxInboundStreams?: number
|
|
70
39
|
maxOutboundStreams?: number
|
|
71
40
|
messageReceiveTimeout?: number
|
|
72
|
-
messageSendTimeout?: number
|
|
73
41
|
messageSendConcurrency?: number
|
|
74
42
|
protocols?: string[]
|
|
75
43
|
runOnTransientConnections?: boolean
|
|
44
|
+
maxOutgoingMessageSize?: number
|
|
45
|
+
maxIncomingMessageSize?: number
|
|
76
46
|
}
|
|
77
47
|
|
|
78
48
|
export interface NetworkComponents {
|
|
79
49
|
routing: Routing
|
|
80
50
|
logger: ComponentLogger
|
|
81
51
|
libp2p: Libp2p
|
|
82
|
-
metrics?: Metrics
|
|
83
52
|
}
|
|
84
53
|
|
|
85
54
|
export interface BitswapMessageEventDetail {
|
|
@@ -107,10 +76,11 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
107
76
|
private readonly maxOutboundStreams: number
|
|
108
77
|
private readonly messageReceiveTimeout: number
|
|
109
78
|
private registrarIds: string[]
|
|
110
|
-
private readonly metrics
|
|
79
|
+
private readonly metrics: { blocksSent?: Counter, dataSent?: Counter }
|
|
111
80
|
private readonly sendQueue: PeerQueue<void, SendMessageJobOptions>
|
|
112
|
-
private readonly messageSendTimeout: number
|
|
113
81
|
private readonly runOnTransientConnections: boolean
|
|
82
|
+
private readonly maxOutgoingMessageSize: number
|
|
83
|
+
private readonly maxIncomingMessageSize: number
|
|
114
84
|
|
|
115
85
|
constructor (components: NetworkComponents, init: NetworkInit = {}) {
|
|
116
86
|
super()
|
|
@@ -127,20 +97,18 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
127
97
|
this.maxInboundStreams = init.maxInboundStreams ?? DEFAULT_MAX_INBOUND_STREAMS
|
|
128
98
|
this.maxOutboundStreams = init.maxOutboundStreams ?? DEFAULT_MAX_OUTBOUND_STREAMS
|
|
129
99
|
this.messageReceiveTimeout = init.messageReceiveTimeout ?? DEFAULT_MESSAGE_RECEIVE_TIMEOUT
|
|
130
|
-
this.messageSendTimeout = init.messageSendTimeout ?? DEFAULT_MESSAGE_SEND_TIMEOUT
|
|
131
100
|
this.runOnTransientConnections = init.runOnTransientConnections ?? DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
101
|
+
this.maxIncomingMessageSize = init.maxIncomingMessageSize ?? DEFAULT_MAX_OUTGOING_MESSAGE_SIZE
|
|
102
|
+
this.maxOutgoingMessageSize = init.maxOutgoingMessageSize ?? init.maxIncomingMessageSize ?? DEFAULT_MAX_INCOMING_MESSAGE_SIZE
|
|
103
|
+
this.metrics = {
|
|
104
|
+
blocksSent: components.libp2p.metrics?.registerCounter('helia_bitswap_sent_blocks_total'),
|
|
105
|
+
dataSent: components.libp2p.metrics?.registerCounter('helia_bitswap_sent_data_bytes_total')
|
|
138
106
|
}
|
|
139
107
|
|
|
140
108
|
this.sendQueue = new PeerQueue({
|
|
141
|
-
concurrency: init.messageSendConcurrency,
|
|
142
|
-
metrics: components.metrics,
|
|
143
|
-
metricName: '
|
|
109
|
+
concurrency: init.messageSendConcurrency ?? DEFAULT_MESSAGE_SEND_CONCURRENCY,
|
|
110
|
+
metrics: components.libp2p.metrics,
|
|
111
|
+
metricName: 'helia_bitswap_message_send_queue'
|
|
144
112
|
})
|
|
145
113
|
this.sendQueue.addEventListener('error', (evt) => {
|
|
146
114
|
this.log.error('error sending wantlist to peer', evt.detail)
|
|
@@ -217,21 +185,29 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
217
185
|
Promise.resolve().then(async () => {
|
|
218
186
|
this.log('incoming new bitswap %s stream from %p', stream.protocol, connection.remotePeer)
|
|
219
187
|
const abortListener = (): void => {
|
|
220
|
-
|
|
188
|
+
if (stream.status === 'open') {
|
|
189
|
+
stream.abort(new CodeError('Incoming Bitswap stream timed out', 'ERR_TIMEOUT'))
|
|
190
|
+
} else {
|
|
191
|
+
this.log('stream aborted with status', stream.status)
|
|
192
|
+
}
|
|
221
193
|
}
|
|
222
194
|
|
|
223
195
|
let signal = AbortSignal.timeout(this.messageReceiveTimeout)
|
|
224
196
|
setMaxListeners(Infinity, signal)
|
|
225
197
|
signal.addEventListener('abort', abortListener)
|
|
226
198
|
|
|
199
|
+
await stream.closeWrite()
|
|
200
|
+
|
|
227
201
|
await pipe(
|
|
228
202
|
stream,
|
|
229
|
-
(source) => lp.decode(source
|
|
203
|
+
(source) => lp.decode(source, {
|
|
204
|
+
maxDataLength: this.maxIncomingMessageSize
|
|
205
|
+
}),
|
|
230
206
|
async (source) => {
|
|
231
207
|
for await (const data of source) {
|
|
232
208
|
try {
|
|
233
209
|
const message = BitswapMessage.decode(data)
|
|
234
|
-
this.log('incoming new bitswap %s message from %p
|
|
210
|
+
this.log('incoming new bitswap %s message from %p on stream', stream.protocol, connection.remotePeer, stream.id)
|
|
235
211
|
|
|
236
212
|
this.safeDispatchEvent('bitswap:message', {
|
|
237
213
|
detail: {
|
|
@@ -246,7 +222,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
246
222
|
setMaxListeners(Infinity, signal)
|
|
247
223
|
signal.addEventListener('abort', abortListener)
|
|
248
224
|
} catch (err: any) {
|
|
249
|
-
this.log.error('error reading incoming bitswap message from %p', connection.remotePeer, err)
|
|
225
|
+
this.log.error('error reading incoming bitswap message from %p on stream', connection.remotePeer, stream.id, err)
|
|
250
226
|
stream.abort(err)
|
|
251
227
|
break
|
|
252
228
|
}
|
|
@@ -267,29 +243,12 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
267
243
|
options?.onProgress?.(new CustomProgressEvent<PeerId>('bitswap:network:find-providers', cid))
|
|
268
244
|
|
|
269
245
|
for await (const provider of this.routing.findProviders(cid, options)) {
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
}
|
|
246
|
+
// make sure we can dial the provider
|
|
247
|
+
const dialable = await this.libp2p.isDialable(provider.multiaddrs, {
|
|
248
|
+
runOnTransientConnection: this.runOnTransientConnections
|
|
249
|
+
})
|
|
290
250
|
|
|
291
|
-
|
|
292
|
-
if (provider.protocols?.includes('transport-bitswap') === false) {
|
|
251
|
+
if (!dialable) {
|
|
293
252
|
continue
|
|
294
253
|
}
|
|
295
254
|
|
|
@@ -302,9 +261,9 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
302
261
|
*/
|
|
303
262
|
async findAndConnect (cid: CID, options?: WantOptions): Promise<void> {
|
|
304
263
|
await drain(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
264
|
+
map(
|
|
265
|
+
take(this.findProviders(cid, options), options?.maxProviders ?? DEFAULT_MAX_PROVIDERS_PER_REQUEST),
|
|
266
|
+
async provider => this.connectTo(provider.id, options)
|
|
308
267
|
)
|
|
309
268
|
)
|
|
310
269
|
.catch(err => {
|
|
@@ -331,55 +290,55 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
331
290
|
pendingBytes: msg.pendingBytes ?? 0
|
|
332
291
|
}
|
|
333
292
|
|
|
334
|
-
const
|
|
335
|
-
|
|
293
|
+
const existingJob = this.sendQueue.queue.find(job => {
|
|
294
|
+
return peerId.equals(job.options.peerId) && job.status === 'queued'
|
|
295
|
+
})
|
|
336
296
|
|
|
337
|
-
|
|
338
|
-
|
|
297
|
+
if (existingJob != null) {
|
|
298
|
+
// merge messages instead of adding new job
|
|
299
|
+
existingJob.options.message = mergeMessages(existingJob.options.message, message)
|
|
339
300
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
301
|
+
await existingJob.join({
|
|
302
|
+
signal: options?.signal
|
|
303
|
+
})
|
|
343
304
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
})
|
|
305
|
+
return
|
|
306
|
+
}
|
|
347
307
|
|
|
348
|
-
|
|
349
|
-
|
|
308
|
+
await this.sendQueue.add(async (options) => {
|
|
309
|
+
const message = options?.message
|
|
350
310
|
|
|
351
|
-
|
|
352
|
-
|
|
311
|
+
if (message == null) {
|
|
312
|
+
throw new CodeError('No message to send', 'ERR_NO_MESSAGE')
|
|
313
|
+
}
|
|
353
314
|
|
|
354
|
-
|
|
355
|
-
throw new CodeError('No message to send', 'ERR_NO_MESSAGE')
|
|
356
|
-
}
|
|
315
|
+
this.log('sendMessage to %p', peerId)
|
|
357
316
|
|
|
358
|
-
|
|
317
|
+
options?.onProgress?.(new CustomProgressEvent<PeerId>('bitswap:network:send-wantlist', peerId))
|
|
359
318
|
|
|
360
|
-
|
|
319
|
+
const stream = await this.libp2p.dialProtocol(peerId, BITSWAP_120, options)
|
|
320
|
+
await stream.closeRead()
|
|
361
321
|
|
|
362
|
-
|
|
322
|
+
try {
|
|
323
|
+
await pipe(
|
|
324
|
+
splitMessage(message, this.maxOutgoingMessageSize),
|
|
325
|
+
(source) => lp.encode(source),
|
|
326
|
+
stream
|
|
327
|
+
)
|
|
363
328
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this.log.error('error sending message to %p', peerId, err)
|
|
371
|
-
stream.abort(err)
|
|
372
|
-
}
|
|
329
|
+
await stream.close(options)
|
|
330
|
+
} catch (err: any) {
|
|
331
|
+
options?.onProgress?.(new CustomProgressEvent<{ peer: PeerId, error: Error }>('bitswap:network:send-wantlist:error', { peer: peerId, error: err }))
|
|
332
|
+
this.log.error('error sending message to %p', peerId, err)
|
|
333
|
+
stream.abort(err)
|
|
334
|
+
}
|
|
373
335
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
} finally {
|
|
381
|
-
signal.clear()
|
|
382
|
-
}
|
|
336
|
+
this._updateSentStats(message.blocks)
|
|
337
|
+
}, {
|
|
338
|
+
peerId,
|
|
339
|
+
signal: options?.signal,
|
|
340
|
+
message
|
|
341
|
+
})
|
|
383
342
|
}
|
|
384
343
|
|
|
385
344
|
/**
|
|
@@ -417,90 +376,14 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
|
|
|
417
376
|
return connection
|
|
418
377
|
}
|
|
419
378
|
|
|
420
|
-
_updateSentStats (
|
|
421
|
-
|
|
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
|
-
}
|
|
379
|
+
_updateSentStats (blocks: Block[] = []): void {
|
|
380
|
+
let bytes = 0
|
|
457
381
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
entry.wantType = entry.wantType ?? existingEntry.wantType
|
|
461
|
-
entry.sendDontHave = entry.sendDontHave ?? existingEntry.sendDontHave
|
|
382
|
+
for (const block of blocks.values()) {
|
|
383
|
+
bytes += block.data.byteLength
|
|
462
384
|
}
|
|
463
385
|
|
|
464
|
-
|
|
386
|
+
this.metrics.dataSent?.increment(bytes)
|
|
387
|
+
this.metrics.blocksSent?.increment(blocks.length)
|
|
465
388
|
}
|
|
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
389
|
}
|
package/src/pb/message.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
6
6
|
|
|
7
|
-
import { type Codec, decodeMessage, encodeMessage, enumeration, message } from 'protons-runtime'
|
|
7
|
+
import { type Codec, CodeError, decodeMessage, type DecodeOptions, encodeMessage, enumeration, message } from 'protons-runtime'
|
|
8
8
|
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
|
|
9
9
|
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
10
10
|
|
|
@@ -69,7 +69,7 @@ export namespace WantlistEntry {
|
|
|
69
69
|
if (opts.lengthDelimited !== false) {
|
|
70
70
|
w.ldelim()
|
|
71
71
|
}
|
|
72
|
-
}, (reader, length) => {
|
|
72
|
+
}, (reader, length, opts = {}) => {
|
|
73
73
|
const obj: any = {
|
|
74
74
|
cid: uint8ArrayAlloc(0),
|
|
75
75
|
priority: 0
|
|
@@ -119,8 +119,8 @@ export namespace WantlistEntry {
|
|
|
119
119
|
return encodeMessage(obj, WantlistEntry.codec())
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): WantlistEntry => {
|
|
123
|
-
return decodeMessage(buf, WantlistEntry.codec())
|
|
122
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<WantlistEntry>): WantlistEntry => {
|
|
123
|
+
return decodeMessage(buf, WantlistEntry.codec(), opts)
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -154,7 +154,7 @@ export namespace Wantlist {
|
|
|
154
154
|
if (opts.lengthDelimited !== false) {
|
|
155
155
|
w.ldelim()
|
|
156
156
|
}
|
|
157
|
-
}, (reader, length) => {
|
|
157
|
+
}, (reader, length, opts = {}) => {
|
|
158
158
|
const obj: any = {
|
|
159
159
|
entries: []
|
|
160
160
|
}
|
|
@@ -166,7 +166,13 @@ export namespace Wantlist {
|
|
|
166
166
|
|
|
167
167
|
switch (tag >>> 3) {
|
|
168
168
|
case 1: {
|
|
169
|
-
obj.entries.
|
|
169
|
+
if (opts.limits?.entries != null && obj.entries.length === opts.limits.entries) {
|
|
170
|
+
throw new CodeError('decode error - map field "entries" had too many elements', 'ERR_MAX_LENGTH')
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
obj.entries.push(WantlistEntry.codec().decode(reader, reader.uint32(), {
|
|
174
|
+
limits: opts.limits?.entries$
|
|
175
|
+
}))
|
|
170
176
|
break
|
|
171
177
|
}
|
|
172
178
|
case 2: {
|
|
@@ -191,8 +197,8 @@ export namespace Wantlist {
|
|
|
191
197
|
return encodeMessage(obj, Wantlist.codec())
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): Wantlist => {
|
|
195
|
-
return decodeMessage(buf, Wantlist.codec())
|
|
200
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Wantlist>): Wantlist => {
|
|
201
|
+
return decodeMessage(buf, Wantlist.codec(), opts)
|
|
196
202
|
}
|
|
197
203
|
}
|
|
198
204
|
|
|
@@ -224,7 +230,7 @@ export namespace Block {
|
|
|
224
230
|
if (opts.lengthDelimited !== false) {
|
|
225
231
|
w.ldelim()
|
|
226
232
|
}
|
|
227
|
-
}, (reader, length) => {
|
|
233
|
+
}, (reader, length, opts = {}) => {
|
|
228
234
|
const obj: any = {
|
|
229
235
|
prefix: uint8ArrayAlloc(0),
|
|
230
236
|
data: uint8ArrayAlloc(0)
|
|
@@ -262,8 +268,8 @@ export namespace Block {
|
|
|
262
268
|
return encodeMessage(obj, Block.codec())
|
|
263
269
|
}
|
|
264
270
|
|
|
265
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): Block => {
|
|
266
|
-
return decodeMessage(buf, Block.codec())
|
|
271
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<Block>): Block => {
|
|
272
|
+
return decodeMessage(buf, Block.codec(), opts)
|
|
267
273
|
}
|
|
268
274
|
}
|
|
269
275
|
|
|
@@ -310,7 +316,7 @@ export namespace BlockPresence {
|
|
|
310
316
|
if (opts.lengthDelimited !== false) {
|
|
311
317
|
w.ldelim()
|
|
312
318
|
}
|
|
313
|
-
}, (reader, length) => {
|
|
319
|
+
}, (reader, length, opts = {}) => {
|
|
314
320
|
const obj: any = {
|
|
315
321
|
cid: uint8ArrayAlloc(0),
|
|
316
322
|
type: BlockPresenceType.HaveBlock
|
|
@@ -348,8 +354,8 @@ export namespace BlockPresence {
|
|
|
348
354
|
return encodeMessage(obj, BlockPresence.codec())
|
|
349
355
|
}
|
|
350
356
|
|
|
351
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): BlockPresence => {
|
|
352
|
-
return decodeMessage(buf, BlockPresence.codec())
|
|
357
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<BlockPresence>): BlockPresence => {
|
|
358
|
+
return decodeMessage(buf, BlockPresence.codec(), opts)
|
|
353
359
|
}
|
|
354
360
|
}
|
|
355
361
|
|
|
@@ -397,7 +403,7 @@ export namespace BitswapMessage {
|
|
|
397
403
|
if (opts.lengthDelimited !== false) {
|
|
398
404
|
w.ldelim()
|
|
399
405
|
}
|
|
400
|
-
}, (reader, length) => {
|
|
406
|
+
}, (reader, length, opts = {}) => {
|
|
401
407
|
const obj: any = {
|
|
402
408
|
blocks: [],
|
|
403
409
|
blockPresences: [],
|
|
@@ -411,15 +417,29 @@ export namespace BitswapMessage {
|
|
|
411
417
|
|
|
412
418
|
switch (tag >>> 3) {
|
|
413
419
|
case 1: {
|
|
414
|
-
obj.wantlist = Wantlist.codec().decode(reader, reader.uint32()
|
|
420
|
+
obj.wantlist = Wantlist.codec().decode(reader, reader.uint32(), {
|
|
421
|
+
limits: opts.limits?.wantlist
|
|
422
|
+
})
|
|
415
423
|
break
|
|
416
424
|
}
|
|
417
425
|
case 3: {
|
|
418
|
-
obj.blocks.
|
|
426
|
+
if (opts.limits?.blocks != null && obj.blocks.length === opts.limits.blocks) {
|
|
427
|
+
throw new CodeError('decode error - map field "blocks" had too many elements', 'ERR_MAX_LENGTH')
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
obj.blocks.push(Block.codec().decode(reader, reader.uint32(), {
|
|
431
|
+
limits: opts.limits?.blocks$
|
|
432
|
+
}))
|
|
419
433
|
break
|
|
420
434
|
}
|
|
421
435
|
case 4: {
|
|
422
|
-
obj.blockPresences.
|
|
436
|
+
if (opts.limits?.blockPresences != null && obj.blockPresences.length === opts.limits.blockPresences) {
|
|
437
|
+
throw new CodeError('decode error - map field "blockPresences" had too many elements', 'ERR_MAX_LENGTH')
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
obj.blockPresences.push(BlockPresence.codec().decode(reader, reader.uint32(), {
|
|
441
|
+
limits: opts.limits?.blockPresences$
|
|
442
|
+
}))
|
|
423
443
|
break
|
|
424
444
|
}
|
|
425
445
|
case 5: {
|
|
@@ -444,7 +464,7 @@ export namespace BitswapMessage {
|
|
|
444
464
|
return encodeMessage(obj, BitswapMessage.codec())
|
|
445
465
|
}
|
|
446
466
|
|
|
447
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): BitswapMessage => {
|
|
448
|
-
return decodeMessage(buf, BitswapMessage.codec())
|
|
467
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<BitswapMessage>): BitswapMessage => {
|
|
468
|
+
return decodeMessage(buf, BitswapMessage.codec(), opts)
|
|
449
469
|
}
|
|
450
470
|
}
|