@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.
- package/LICENSE +4 -0
- package/README.md +64 -0
- package/dist/index.min.js +3 -0
- package/dist/src/bitswap.d.ts +50 -0
- package/dist/src/bitswap.d.ts.map +1 -0
- package/dist/src/bitswap.js +120 -0
- package/dist/src/bitswap.js.map +1 -0
- package/dist/src/constants.d.ts +12 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +12 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/index.d.ts +178 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/network.d.ts +84 -0
- package/dist/src/network.d.ts.map +1 -0
- package/dist/src/network.js +370 -0
- package/dist/src/network.js.map +1 -0
- package/dist/src/pb/message.d.ts +67 -0
- package/dist/src/pb/message.d.ts.map +1 -0
- package/dist/src/pb/message.js +359 -0
- package/dist/src/pb/message.js.map +1 -0
- package/dist/src/peer-want-lists/index.d.ts +44 -0
- package/dist/src/peer-want-lists/index.d.ts.map +1 -0
- package/dist/src/peer-want-lists/index.js +116 -0
- package/dist/src/peer-want-lists/index.js.map +1 -0
- package/dist/src/peer-want-lists/ledger.d.ts +54 -0
- package/dist/src/peer-want-lists/ledger.d.ts.map +1 -0
- package/dist/src/peer-want-lists/ledger.js +104 -0
- package/dist/src/peer-want-lists/ledger.js.map +1 -0
- package/dist/src/session.d.ts +20 -0
- package/dist/src/session.d.ts.map +1 -0
- package/dist/src/session.js +100 -0
- package/dist/src/session.js.map +1 -0
- package/dist/src/stats.d.ts +16 -0
- package/dist/src/stats.d.ts.map +1 -0
- package/dist/src/stats.js +49 -0
- package/dist/src/stats.js.map +1 -0
- package/dist/src/utils/cid-prefix.d.ts +3 -0
- package/dist/src/utils/cid-prefix.d.ts.map +1 -0
- package/dist/src/utils/cid-prefix.js +7 -0
- package/dist/src/utils/cid-prefix.js.map +1 -0
- package/dist/src/utils/varint-decoder.d.ts +3 -0
- package/dist/src/utils/varint-decoder.d.ts.map +1 -0
- package/dist/src/utils/varint-decoder.js +15 -0
- package/dist/src/utils/varint-decoder.js.map +1 -0
- package/dist/src/utils/varint-encoder.d.ts +3 -0
- package/dist/src/utils/varint-encoder.d.ts.map +1 -0
- package/dist/src/utils/varint-encoder.js +14 -0
- package/dist/src/utils/varint-encoder.js.map +1 -0
- package/dist/src/want-list.d.ts +120 -0
- package/dist/src/want-list.d.ts.map +1 -0
- package/dist/src/want-list.js +361 -0
- package/dist/src/want-list.js.map +1 -0
- package/package.json +200 -0
- package/src/bitswap.ts +152 -0
- package/src/constants.ts +11 -0
- package/src/index.ts +215 -0
- package/src/network.ts +506 -0
- package/src/pb/message.proto +42 -0
- package/src/pb/message.ts +450 -0
- package/src/peer-want-lists/index.ts +165 -0
- package/src/peer-want-lists/ledger.ts +161 -0
- package/src/session.ts +150 -0
- package/src/stats.ts +67 -0
- package/src/utils/cid-prefix.ts +8 -0
- package/src/utils/varint-decoder.ts +19 -0
- package/src/utils/varint-encoder.ts +18 -0
- 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
|