@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
@@ -0,0 +1,178 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * This module implements the [Bitswap protocol](https://docs.ipfs.tech/concepts/bitswap/) in TypeScript.
5
+ *
6
+ * It supersedes the older [ipfs-bitswap](https://www.npmjs.com/package/ipfs-bitswap) module with the aim of being smaller, faster, better integrated with libp2p/helia, having fewer dependencies and using standard JavaScript instead of Node.js APIs.
7
+ */
8
+ import type { BitswapNetworkNotifyProgressEvents, BitswapNetworkWantProgressEvents } from './network.js';
9
+ import type { WantType } from './pb/message.js';
10
+ import type { CreateSessionOptions } from '@helia/interface';
11
+ import type { Routing } from '@helia/interface/routing';
12
+ import type { Libp2p, AbortOptions, Startable, ComponentLogger, Metrics, PeerId } from '@libp2p/interface';
13
+ import type { PeerSet } from '@libp2p/peer-collections';
14
+ import type { Blockstore } from 'interface-blockstore';
15
+ import type { CID } from 'multiformats/cid';
16
+ import type { MultihashHasher } from 'multiformats/hashes/interface';
17
+ import type { ProgressEvent, ProgressOptions } from 'progress-events';
18
+ export type BitswapWantProgressEvents = BitswapWantBlockProgressEvents;
19
+ export type BitswapNotifyProgressEvents = BitswapNetworkNotifyProgressEvents;
20
+ export type BitswapWantBlockProgressEvents = ProgressEvent<'bitswap:want-block:unwant', CID> | ProgressEvent<'bitswap:want-block:block', CID> | BitswapNetworkWantProgressEvents;
21
+ /**
22
+ * A bitswap session is a network overlay consisting of peers that all have the
23
+ * first block in a file. Subsequent requests will only go to these peers.
24
+ */
25
+ export interface BitswapSession {
26
+ /**
27
+ * The peers in this session
28
+ */
29
+ peers: PeerSet;
30
+ /**
31
+ * Fetch an additional CID from this DAG
32
+ */
33
+ want(cid: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<Uint8Array>;
34
+ }
35
+ export interface WantListEntry {
36
+ cid: CID;
37
+ priority: number;
38
+ wantType: WantType;
39
+ }
40
+ export interface CreateBitswapSessionOptions extends CreateSessionOptions<BitswapWantProgressEvents> {
41
+ /**
42
+ * If true, query connected peers before searching for providers via
43
+ * Helia routers
44
+ *
45
+ * @default true
46
+ */
47
+ queryConnectedPeers?: boolean;
48
+ /**
49
+ * If true, search for providers via Helia routers to query for the root CID
50
+ *
51
+ * @default true
52
+ */
53
+ queryRoutingPeers?: boolean;
54
+ /**
55
+ * The priority to use when querying availability of the root CID
56
+ *
57
+ * @default 1
58
+ */
59
+ priority?: number;
60
+ }
61
+ export interface Bitswap extends Startable {
62
+ /**
63
+ * Returns the current state of the wantlist
64
+ */
65
+ getWantlist(): WantListEntry[];
66
+ /**
67
+ * Returns the current state of the wantlist for a peer, if it is being
68
+ * tracked
69
+ */
70
+ getPeerWantlist(peerId: PeerId): WantListEntry[] | undefined;
71
+ /**
72
+ * Notify bitswap that a new block is available
73
+ */
74
+ notify(cid: CID, block: Uint8Array, options?: ProgressOptions<BitswapNotifyProgressEvents>): Promise<void>;
75
+ /**
76
+ * Start a session to retrieve a file from the network
77
+ */
78
+ want(cid: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<Uint8Array>;
79
+ /**
80
+ * Start a session to retrieve a file from the network
81
+ */
82
+ createSession(root: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<BitswapSession>;
83
+ }
84
+ export interface MultihashHasherLoader {
85
+ getHasher(codeOrName: number | string): Promise<MultihashHasher>;
86
+ }
87
+ export interface BitswapComponents {
88
+ routing: Routing;
89
+ blockstore: Blockstore;
90
+ logger: ComponentLogger;
91
+ libp2p: Libp2p;
92
+ metrics?: Metrics;
93
+ }
94
+ export interface BitswapOptions {
95
+ /**
96
+ * This is the maximum number of concurrent inbound bitswap streams that are
97
+ * allowed
98
+ *
99
+ * @default 32
100
+ */
101
+ maxInboundStreams?: number;
102
+ /**
103
+ * This is the maximum number of concurrent outbound bitswap streams that are
104
+ * allowed
105
+ *
106
+ * @default 128
107
+ */
108
+ maxOutboundStreams?: number;
109
+ /**
110
+ * An incoming stream must resolve within this number of seconds
111
+ *
112
+ * @default 30000
113
+ */
114
+ incomingStreamTimeout?: number;
115
+ /**
116
+ * Whether to run on transient (e.g. time/data limited) connections
117
+ *
118
+ * @default false
119
+ */
120
+ runOnTransientConnections?: boolean;
121
+ /**
122
+ * Enables loading esoteric hash functions
123
+ */
124
+ hashLoader?: MultihashHasherLoader;
125
+ /**
126
+ * The protocol that we speak
127
+ *
128
+ * @default '/ipfs/bitswap/1.2.0'
129
+ */
130
+ protocol?: string;
131
+ /**
132
+ * When a new peer connects, sending our WantList should complete within this
133
+ * many ms
134
+ *
135
+ * @default 5000
136
+ */
137
+ messageSendTimeout?: number;
138
+ /**
139
+ * When sending want list updates to peers, how many messages to send at once
140
+ *
141
+ * @default 50
142
+ */
143
+ messageSendConcurrency?: number;
144
+ /**
145
+ * When sending blocks to peers, how many messages to send at once
146
+ *
147
+ * @default 50
148
+ */
149
+ sendBlocksConcurrency?: number;
150
+ /**
151
+ * When sending blocks to peers, timeout after this many milliseconds.
152
+ * This is useful for preventing slow/large peer-connections from consuming
153
+ * your bandwidth/streams.
154
+ *
155
+ * @default 10000
156
+ */
157
+ sendBlocksTimeout?: number;
158
+ /**
159
+ * When a block is added to the blockstore and we are about to send that block
160
+ * to peers who have it in their wantlist, wait this many milliseconds before
161
+ * queueing the send job in case more blocks are added that they want
162
+ *
163
+ * @default 10
164
+ */
165
+ sendBlocksDebounce?: number;
166
+ /**
167
+ * If the client sends a want-have, and we have the corresponding block, we
168
+ * check the size of the block and if it's small enough we send the block
169
+ * itself, rather than sending a HAVE.
170
+ *
171
+ * This defines the maximum size up to which we replace a HAVE with a block.
172
+ *
173
+ * @default 1024
174
+ */
175
+ maxSizeReplaceHasWithBlock?: number;
176
+ }
177
+ export declare const createBitswap: (components: BitswapComponents, options?: BitswapOptions) => Bitswap;
178
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * This module implements the [Bitswap protocol](https://docs.ipfs.tech/concepts/bitswap/) in TypeScript.
5
+ *
6
+ * It supersedes the older [ipfs-bitswap](https://www.npmjs.com/package/ipfs-bitswap) module with the aim of being smaller, faster, better integrated with libp2p/helia, having fewer dependencies and using standard JavaScript instead of Node.js APIs.
7
+ */
8
+ import { Bitswap as BitswapClass } from './bitswap.js';
9
+ export const createBitswap = (components, options = {}) => {
10
+ return new BitswapClass(components, options);
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,cAAc,CAAA;AA4MtD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,UAA6B,EAAE,UAA0B,EAAE,EAAW,EAAE;IACpG,OAAO,IAAI,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;AAC9C,CAAC,CAAA"}
@@ -0,0 +1,84 @@
1
+ import { TypedEventEmitter } from '@libp2p/interface';
2
+ import { CID } from 'multiformats/cid';
3
+ import { BitswapMessage } from './pb/message.js';
4
+ import type { WantOptions } from './bitswap.js';
5
+ import type { MultihashHasherLoader } from './index.js';
6
+ import type { Block } from './pb/message.js';
7
+ import type { Provider, Routing } from '@helia/interface/routing';
8
+ import type { Libp2p, AbortOptions, Connection, PeerId, IncomingStreamData, ComponentLogger, Metrics } from '@libp2p/interface';
9
+ import type { ProgressEvent, ProgressOptions } from 'progress-events';
10
+ export type BitswapNetworkProgressEvents = ProgressEvent<'bitswap:network:dial', PeerId>;
11
+ export type BitswapNetworkWantProgressEvents = ProgressEvent<'bitswap:network:send-wantlist', PeerId> | ProgressEvent<'bitswap:network:send-wantlist:error', {
12
+ peer: PeerId;
13
+ error: Error;
14
+ }> | ProgressEvent<'bitswap:network:find-providers', CID> | BitswapNetworkProgressEvents;
15
+ export type BitswapNetworkNotifyProgressEvents = BitswapNetworkProgressEvents | ProgressEvent<'bitswap:network:send-block', PeerId>;
16
+ export interface NetworkInit {
17
+ hashLoader?: MultihashHasherLoader;
18
+ maxInboundStreams?: number;
19
+ maxOutboundStreams?: number;
20
+ messageReceiveTimeout?: number;
21
+ messageSendTimeout?: number;
22
+ messageSendConcurrency?: number;
23
+ protocols?: string[];
24
+ runOnTransientConnections?: boolean;
25
+ }
26
+ export interface NetworkComponents {
27
+ routing: Routing;
28
+ logger: ComponentLogger;
29
+ libp2p: Libp2p;
30
+ metrics?: Metrics;
31
+ }
32
+ export interface BitswapMessageEventDetail {
33
+ peer: PeerId;
34
+ message: BitswapMessage;
35
+ }
36
+ export interface NetworkEvents {
37
+ 'bitswap:message': CustomEvent<{
38
+ peer: PeerId;
39
+ message: BitswapMessage;
40
+ }>;
41
+ 'peer:connected': CustomEvent<PeerId>;
42
+ 'peer:disconnected': CustomEvent<PeerId>;
43
+ }
44
+ export declare class Network extends TypedEventEmitter<NetworkEvents> {
45
+ private readonly log;
46
+ private readonly libp2p;
47
+ private readonly routing;
48
+ private readonly protocols;
49
+ private running;
50
+ private readonly maxInboundStreams;
51
+ private readonly maxOutboundStreams;
52
+ private readonly messageReceiveTimeout;
53
+ private registrarIds;
54
+ private readonly metrics?;
55
+ private readonly sendQueue;
56
+ private readonly messageSendTimeout;
57
+ private readonly runOnTransientConnections;
58
+ constructor(components: NetworkComponents, init?: NetworkInit);
59
+ start(): Promise<void>;
60
+ stop(): Promise<void>;
61
+ /**
62
+ * Handles incoming bitswap messages
63
+ */
64
+ _onStream(info: IncomingStreamData): void;
65
+ /**
66
+ * Find bitswap providers for a given `cid`.
67
+ */
68
+ findProviders(cid: CID, options?: AbortOptions & ProgressOptions<BitswapNetworkWantProgressEvents>): AsyncIterable<Provider>;
69
+ /**
70
+ * Find the providers of a given `cid` and connect to them.
71
+ */
72
+ findAndConnect(cid: CID, options?: WantOptions): Promise<void>;
73
+ /**
74
+ * Connect to the given peer
75
+ * Send the given msg (instance of Message) to the given peer
76
+ */
77
+ sendMessage(peerId: PeerId, msg: Partial<BitswapMessage>, options?: AbortOptions & ProgressOptions<BitswapNetworkWantProgressEvents>): Promise<void>;
78
+ /**
79
+ * Connects to another peer
80
+ */
81
+ connectTo(peer: PeerId, options?: AbortOptions & ProgressOptions<BitswapNetworkProgressEvents>): Promise<Connection>;
82
+ _updateSentStats(peerId: PeerId, blocks?: Block[]): void;
83
+ }
84
+ //# sourceMappingURL=network.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,370 @@
1
+ import { CodeError, TypedEventEmitter, setMaxListeners } from '@libp2p/interface';
2
+ import { PeerQueue } 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
+ // Add a formatter for a bitswap message
20
+ debug.formatters.B = (b) => {
21
+ if (b == null) {
22
+ return 'undefined';
23
+ }
24
+ return JSON.stringify({
25
+ blocks: b.blocks?.map(b => ({
26
+ data: `${uint8ArrayToString(b.data, 'base64').substring(0, 10)}...`,
27
+ prefix: uint8ArrayToString(b.prefix, 'base64')
28
+ })),
29
+ blockPresences: b.blockPresences?.map(p => ({
30
+ ...p,
31
+ cid: CID.decode(p.cid).toString()
32
+ })),
33
+ wantlist: b.wantlist == null
34
+ ? undefined
35
+ : {
36
+ full: b.wantlist.full,
37
+ entries: b.wantlist.entries.map(e => ({
38
+ ...e,
39
+ cid: CID.decode(e.cid).toString()
40
+ }))
41
+ }
42
+ }, null, 2);
43
+ };
44
+ export class Network extends TypedEventEmitter {
45
+ log;
46
+ libp2p;
47
+ routing;
48
+ protocols;
49
+ running;
50
+ maxInboundStreams;
51
+ maxOutboundStreams;
52
+ messageReceiveTimeout;
53
+ registrarIds;
54
+ metrics;
55
+ sendQueue;
56
+ messageSendTimeout;
57
+ runOnTransientConnections;
58
+ constructor(components, init = {}) {
59
+ super();
60
+ this.log = components.logger.forComponent('helia:bitswap:network');
61
+ this.libp2p = components.libp2p;
62
+ this.routing = components.routing;
63
+ this.protocols = init.protocols ?? [BITSWAP_120];
64
+ this.registrarIds = [];
65
+ this.running = false;
66
+ // bind event listeners
67
+ this._onStream = this._onStream.bind(this);
68
+ this.maxInboundStreams = init.maxInboundStreams ?? DEFAULT_MAX_INBOUND_STREAMS;
69
+ this.maxOutboundStreams = init.maxOutboundStreams ?? DEFAULT_MAX_OUTBOUND_STREAMS;
70
+ this.messageReceiveTimeout = init.messageReceiveTimeout ?? DEFAULT_MESSAGE_RECEIVE_TIMEOUT;
71
+ this.messageSendTimeout = init.messageSendTimeout ?? DEFAULT_MESSAGE_SEND_TIMEOUT;
72
+ this.runOnTransientConnections = init.runOnTransientConnections ?? DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS;
73
+ if (components.metrics != null) {
74
+ this.metrics = {
75
+ blocksSent: components.metrics?.registerMetricGroup('ipfs_bitswap_sent_blocks'),
76
+ dataSent: components.metrics?.registerMetricGroup('ipfs_bitswap_sent_data_bytes')
77
+ };
78
+ }
79
+ this.sendQueue = new PeerQueue({
80
+ concurrency: init.messageSendConcurrency,
81
+ metrics: components.metrics,
82
+ metricName: 'ipfs_bitswap_message_send_queue'
83
+ });
84
+ this.sendQueue.addEventListener('error', (evt) => {
85
+ this.log.error('error sending wantlist to peer', evt.detail);
86
+ });
87
+ }
88
+ async start() {
89
+ if (this.running) {
90
+ return;
91
+ }
92
+ this.running = true;
93
+ await this.libp2p.handle(this.protocols, this._onStream, {
94
+ maxInboundStreams: this.maxInboundStreams,
95
+ maxOutboundStreams: this.maxOutboundStreams,
96
+ runOnTransientConnection: this.runOnTransientConnections
97
+ });
98
+ // register protocol with topology
99
+ const topology = {
100
+ onConnect: (peerId) => {
101
+ this.safeDispatchEvent('peer:connected', {
102
+ detail: peerId
103
+ });
104
+ },
105
+ onDisconnect: (peerId) => {
106
+ this.safeDispatchEvent('peer:disconnected', {
107
+ detail: peerId
108
+ });
109
+ }
110
+ };
111
+ this.registrarIds = [];
112
+ for (const protocol of this.protocols) {
113
+ this.registrarIds.push(await this.libp2p.register(protocol, topology));
114
+ }
115
+ // All existing connections are like new ones for us
116
+ this.libp2p.getConnections().forEach(conn => {
117
+ this.safeDispatchEvent('peer:connected', {
118
+ detail: conn.remotePeer
119
+ });
120
+ });
121
+ }
122
+ async stop() {
123
+ this.running = false;
124
+ // Unhandle both, libp2p doesn't care if it's not already handled
125
+ await this.libp2p.unhandle(this.protocols);
126
+ // unregister protocol and handlers
127
+ if (this.registrarIds != null) {
128
+ for (const id of this.registrarIds) {
129
+ this.libp2p.unregister(id);
130
+ }
131
+ this.registrarIds = [];
132
+ }
133
+ }
134
+ /**
135
+ * Handles incoming bitswap messages
136
+ */
137
+ _onStream(info) {
138
+ if (!this.running) {
139
+ return;
140
+ }
141
+ const { stream, connection } = info;
142
+ Promise.resolve().then(async () => {
143
+ this.log('incoming new bitswap %s stream from %p', stream.protocol, connection.remotePeer);
144
+ const abortListener = () => {
145
+ stream.abort(new CodeError('Incoming Bitswap stream timed out', 'ERR_TIMEOUT'));
146
+ };
147
+ let signal = AbortSignal.timeout(this.messageReceiveTimeout);
148
+ setMaxListeners(Infinity, signal);
149
+ signal.addEventListener('abort', abortListener);
150
+ await pipe(stream, (source) => lp.decode(source), async (source) => {
151
+ for await (const data of source) {
152
+ try {
153
+ const message = BitswapMessage.decode(data);
154
+ this.log('incoming new bitswap %s message from %p %B', stream.protocol, connection.remotePeer, message);
155
+ this.safeDispatchEvent('bitswap:message', {
156
+ detail: {
157
+ peer: connection.remotePeer,
158
+ message
159
+ }
160
+ });
161
+ // we have received some data so reset the timeout controller
162
+ signal.removeEventListener('abort', abortListener);
163
+ signal = AbortSignal.timeout(this.messageReceiveTimeout);
164
+ setMaxListeners(Infinity, signal);
165
+ signal.addEventListener('abort', abortListener);
166
+ }
167
+ catch (err) {
168
+ this.log.error('error reading incoming bitswap message from %p', connection.remotePeer, err);
169
+ stream.abort(err);
170
+ break;
171
+ }
172
+ }
173
+ });
174
+ })
175
+ .catch(err => {
176
+ this.log.error('error handling incoming stream from %p', connection.remotePeer, err);
177
+ stream.abort(err);
178
+ });
179
+ }
180
+ /**
181
+ * Find bitswap providers for a given `cid`.
182
+ */
183
+ async *findProviders(cid, options) {
184
+ options?.onProgress?.(new CustomProgressEvent('bitswap:network:find-providers', cid));
185
+ for await (const provider of this.routing.findProviders(cid, options)) {
186
+ // unless we explicitly run on transient connections, skip peers that only
187
+ // have circuit relay addresses as bitswap won't run over them
188
+ if (!this.runOnTransientConnections) {
189
+ let hasDirectAddress = false;
190
+ for (let ma of provider.multiaddrs) {
191
+ if (ma.getPeerId() == null) {
192
+ ma = ma.encapsulate(`/p2p/${provider.id}`);
193
+ }
194
+ if (!Circuit.exactMatch(ma)) {
195
+ hasDirectAddress = true;
196
+ break;
197
+ }
198
+ }
199
+ if (!hasDirectAddress) {
200
+ continue;
201
+ }
202
+ }
203
+ // ignore non-bitswap providers
204
+ if (provider.protocols?.includes('transport-bitswap') === false) {
205
+ continue;
206
+ }
207
+ yield provider;
208
+ }
209
+ }
210
+ /**
211
+ * Find the providers of a given `cid` and connect to them.
212
+ */
213
+ async findAndConnect(cid, options) {
214
+ await drain(take(map(this.findProviders(cid, options), async (provider) => this.connectTo(provider.id, options)), options?.maxProviders ?? DEFAULT_MAX_PROVIDERS_PER_REQUEST))
215
+ .catch(err => {
216
+ this.log.error(err);
217
+ });
218
+ }
219
+ /**
220
+ * Connect to the given peer
221
+ * Send the given msg (instance of Message) to the given peer
222
+ */
223
+ async sendMessage(peerId, msg, options) {
224
+ if (!this.running) {
225
+ throw new Error('network isn\'t running');
226
+ }
227
+ const message = {
228
+ wantlist: {
229
+ full: msg.wantlist?.full ?? false,
230
+ entries: msg.wantlist?.entries ?? []
231
+ },
232
+ blocks: msg.blocks ?? [],
233
+ blockPresences: msg.blockPresences ?? [],
234
+ pendingBytes: msg.pendingBytes ?? 0
235
+ };
236
+ const signal = anySignal([AbortSignal.timeout(this.messageSendTimeout), options?.signal]);
237
+ setMaxListeners(Infinity, signal);
238
+ try {
239
+ const existingJob = this.sendQueue.find(peerId);
240
+ if (existingJob?.status === 'queued') {
241
+ // merge messages instead of adding new job
242
+ existingJob.options.message = mergeMessages(existingJob.options.message, message);
243
+ await existingJob.join({
244
+ signal
245
+ });
246
+ return;
247
+ }
248
+ await this.sendQueue.add(async (options) => {
249
+ const message = options?.message;
250
+ if (message == null) {
251
+ throw new CodeError('No message to send', 'ERR_NO_MESSAGE');
252
+ }
253
+ this.log('sendMessage to %p %B', peerId, message);
254
+ options?.onProgress?.(new CustomProgressEvent('bitswap:network:send-wantlist', peerId));
255
+ const stream = await this.libp2p.dialProtocol(peerId, BITSWAP_120, options);
256
+ try {
257
+ const lp = lpStream(stream);
258
+ await lp.write(BitswapMessage.encode(message), options);
259
+ await lp.unwrap().close(options);
260
+ }
261
+ catch (err) {
262
+ options?.onProgress?.(new CustomProgressEvent('bitswap:network:send-wantlist:error', { peer: peerId, error: err }));
263
+ this.log.error('error sending message to %p', peerId, err);
264
+ stream.abort(err);
265
+ }
266
+ this._updateSentStats(peerId, message.blocks);
267
+ }, {
268
+ peerId,
269
+ signal,
270
+ message
271
+ });
272
+ }
273
+ finally {
274
+ signal.clear();
275
+ }
276
+ }
277
+ /**
278
+ * Connects to another peer
279
+ */
280
+ async connectTo(peer, options) {
281
+ if (!this.running) {
282
+ throw new CodeError('Network isn\'t running', 'ERR_NOT_STARTED');
283
+ }
284
+ options?.onProgress?.(new CustomProgressEvent('bitswap:network:dial', peer));
285
+ // dial and wait for identify - this is to avoid opening a protocol stream
286
+ // that we are not going to use but depends on the remote node running the
287
+ // identitfy protocol
288
+ const [connection] = await Promise.all([
289
+ this.libp2p.dial(peer, options),
290
+ raceEvent(this.libp2p, 'peer:identify', options?.signal, {
291
+ filter: (evt) => {
292
+ if (!evt.detail.peerId.equals(peer)) {
293
+ return false;
294
+ }
295
+ if (evt.detail.protocols.includes(BITSWAP_120)) {
296
+ return true;
297
+ }
298
+ throw new CodeError(`${peer} did not support ${BITSWAP_120}`, 'ERR_BITSWAP_UNSUPPORTED_BY_PEER');
299
+ }
300
+ })
301
+ ]);
302
+ return connection;
303
+ }
304
+ _updateSentStats(peerId, blocks = []) {
305
+ if (this.metrics != null) {
306
+ let bytes = 0;
307
+ for (const block of blocks.values()) {
308
+ bytes += block.data.byteLength;
309
+ }
310
+ this.metrics.dataSent.increment({
311
+ global: bytes,
312
+ [peerId.toString()]: bytes
313
+ });
314
+ this.metrics.blocksSent.increment({
315
+ global: blocks.length,
316
+ [peerId.toString()]: blocks.length
317
+ });
318
+ }
319
+ }
320
+ }
321
+ function mergeMessages(messageA, messageB) {
322
+ const wantListEntries = new Map((messageA.wantlist?.entries ?? []).map(entry => ([
323
+ base64.encode(entry.cid),
324
+ entry
325
+ ])));
326
+ for (const entry of messageB.wantlist?.entries ?? []) {
327
+ const key = base64.encode(entry.cid);
328
+ const existingEntry = wantListEntries.get(key);
329
+ if (existingEntry != null) {
330
+ // take highest priority
331
+ if (existingEntry.priority > entry.priority) {
332
+ entry.priority = existingEntry.priority;
333
+ }
334
+ // take later values if passed, otherwise use earlier ones
335
+ entry.cancel = entry.cancel ?? existingEntry.cancel;
336
+ entry.wantType = entry.wantType ?? existingEntry.wantType;
337
+ entry.sendDontHave = entry.sendDontHave ?? existingEntry.sendDontHave;
338
+ }
339
+ wantListEntries.set(key, entry);
340
+ }
341
+ const blockPresences = new Map(messageA.blockPresences.map(presence => ([
342
+ base64.encode(presence.cid),
343
+ presence
344
+ ])));
345
+ for (const blockPresence of messageB.blockPresences) {
346
+ const key = base64.encode(blockPresence.cid);
347
+ // override earlier block presence with later one as if duplicated it is
348
+ // likely to be more accurate since it is more recent
349
+ blockPresences.set(key, blockPresence);
350
+ }
351
+ const blocks = new Map(messageA.blocks.map(block => ([
352
+ base64.encode(block.data),
353
+ block
354
+ ])));
355
+ for (const block of messageB.blocks) {
356
+ const key = base64.encode(block.data);
357
+ blocks.set(key, block);
358
+ }
359
+ const output = {
360
+ wantlist: {
361
+ full: messageA.wantlist?.full ?? messageB.wantlist?.full ?? false,
362
+ entries: [...wantListEntries.values()]
363
+ },
364
+ blockPresences: [...blockPresences.values()],
365
+ blocks: [...blocks.values()],
366
+ pendingBytes: messageA.pendingBytes + messageB.pendingBytes
367
+ };
368
+ return output;
369
+ }
370
+ //# sourceMappingURL=network.js.map