@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
package/src/bitswap.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/* eslint-disable no-loop-func */
|
|
2
|
+
import { DEFAULT_SESSION_MAX_PROVIDERS, DEFAULT_SESSION_MIN_PROVIDERS, DEFAULT_SESSION_PROVIDER_QUERY_CONCURRENCY } from '@helia/interface'
|
|
3
|
+
import { setMaxListeners } from '@libp2p/interface'
|
|
4
|
+
import { anySignal } from 'any-signal'
|
|
5
|
+
import { Network } from './network.js'
|
|
6
|
+
import { PeerWantLists } from './peer-want-lists/index.js'
|
|
7
|
+
import { createBitswapSession } from './session.js'
|
|
8
|
+
import { Stats } from './stats.js'
|
|
9
|
+
import { WantList } from './want-list.js'
|
|
10
|
+
import type { BitswapOptions, Bitswap as BitswapInterface, BitswapWantProgressEvents, BitswapNotifyProgressEvents, BitswapSession, WantListEntry, BitswapComponents, CreateBitswapSessionOptions } from './index.js'
|
|
11
|
+
import type { ComponentLogger, PeerId } from '@libp2p/interface'
|
|
12
|
+
import type { Logger } from '@libp2p/logger'
|
|
13
|
+
import type { AbortOptions } from '@multiformats/multiaddr'
|
|
14
|
+
import type { Blockstore } from 'interface-blockstore'
|
|
15
|
+
import type { CID } from 'multiformats/cid'
|
|
16
|
+
import type { ProgressOptions } from 'progress-events'
|
|
17
|
+
|
|
18
|
+
export interface WantOptions extends AbortOptions, ProgressOptions<BitswapWantProgressEvents> {
|
|
19
|
+
/**
|
|
20
|
+
* When searching the routing for providers, stop searching after finding this
|
|
21
|
+
* many providers.
|
|
22
|
+
*
|
|
23
|
+
* @default 3
|
|
24
|
+
*/
|
|
25
|
+
maxProviders?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* JavaScript implementation of the Bitswap 'data exchange' protocol
|
|
30
|
+
* used by IPFS.
|
|
31
|
+
*/
|
|
32
|
+
export class Bitswap implements BitswapInterface {
|
|
33
|
+
private readonly log: Logger
|
|
34
|
+
private readonly logger: ComponentLogger
|
|
35
|
+
public readonly stats: Stats
|
|
36
|
+
public network: Network
|
|
37
|
+
public blockstore: Blockstore
|
|
38
|
+
public peerWantLists: PeerWantLists
|
|
39
|
+
public wantList: WantList
|
|
40
|
+
|
|
41
|
+
constructor (components: BitswapComponents, init: BitswapOptions = {}) {
|
|
42
|
+
this.logger = components.logger
|
|
43
|
+
this.log = components.logger.forComponent('helia:bitswap')
|
|
44
|
+
this.blockstore = components.blockstore
|
|
45
|
+
|
|
46
|
+
// report stats to libp2p metrics
|
|
47
|
+
this.stats = new Stats(components)
|
|
48
|
+
|
|
49
|
+
// the network delivers messages
|
|
50
|
+
this.network = new Network(components, init)
|
|
51
|
+
|
|
52
|
+
// handle which blocks we send to peers
|
|
53
|
+
this.peerWantLists = new PeerWantLists({
|
|
54
|
+
...components,
|
|
55
|
+
network: this.network
|
|
56
|
+
}, init)
|
|
57
|
+
|
|
58
|
+
// handle which blocks we ask peers for
|
|
59
|
+
this.wantList = new WantList({
|
|
60
|
+
...components,
|
|
61
|
+
network: this.network
|
|
62
|
+
}, init)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createSession (root: CID, options?: CreateBitswapSessionOptions): Promise<BitswapSession> {
|
|
66
|
+
const minProviders = options?.minProviders ?? DEFAULT_SESSION_MIN_PROVIDERS
|
|
67
|
+
const maxProviders = options?.maxProviders ?? DEFAULT_SESSION_MAX_PROVIDERS
|
|
68
|
+
|
|
69
|
+
return createBitswapSession({
|
|
70
|
+
wantList: this.wantList,
|
|
71
|
+
network: this.network,
|
|
72
|
+
logger: this.logger
|
|
73
|
+
}, {
|
|
74
|
+
root,
|
|
75
|
+
queryConcurrency: options?.providerQueryConcurrency ?? DEFAULT_SESSION_PROVIDER_QUERY_CONCURRENCY,
|
|
76
|
+
minProviders,
|
|
77
|
+
maxProviders,
|
|
78
|
+
connectedPeers: options?.queryConnectedPeers !== false ? [...this.wantList.peers.keys()] : [],
|
|
79
|
+
signal: options?.signal
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async want (cid: CID, options: WantOptions = {}): Promise<Uint8Array> {
|
|
84
|
+
const controller = new AbortController()
|
|
85
|
+
setMaxListeners(Infinity, controller.signal)
|
|
86
|
+
const signal = anySignal([controller.signal, options.signal])
|
|
87
|
+
|
|
88
|
+
// find providers and connect to them
|
|
89
|
+
this.network.findAndConnect(cid, {
|
|
90
|
+
...options,
|
|
91
|
+
signal
|
|
92
|
+
})
|
|
93
|
+
.catch(err => {
|
|
94
|
+
// if the controller was aborted we found the block already so ignore
|
|
95
|
+
// the error
|
|
96
|
+
if (!controller.signal.aborted) {
|
|
97
|
+
this.log.error('error during finding and connect for cid %c', cid, err)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const result = await this.wantList.wantBlock(cid, {
|
|
103
|
+
...options,
|
|
104
|
+
signal
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
return result.block
|
|
108
|
+
} finally {
|
|
109
|
+
// since we have the block we can now abort any outstanding attempts to
|
|
110
|
+
// find providers for it
|
|
111
|
+
controller.abort()
|
|
112
|
+
signal.clear()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sends notifications about the arrival of a block
|
|
118
|
+
*/
|
|
119
|
+
async notify (cid: CID, block: Uint8Array, options: ProgressOptions<BitswapNotifyProgressEvents> & AbortOptions = {}): Promise<void> {
|
|
120
|
+
await this.peerWantLists.receivedBlock(cid, options)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getWantlist (): WantListEntry[] {
|
|
124
|
+
return [...this.wantList.wants.values()]
|
|
125
|
+
.filter(entry => !entry.cancel)
|
|
126
|
+
.map(entry => ({
|
|
127
|
+
cid: entry.cid,
|
|
128
|
+
priority: entry.priority,
|
|
129
|
+
wantType: entry.wantType
|
|
130
|
+
}))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getPeerWantlist (peer: PeerId): WantListEntry[] | undefined {
|
|
134
|
+
return this.peerWantLists.wantListForPeer(peer)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Start the bitswap node
|
|
139
|
+
*/
|
|
140
|
+
async start (): Promise<void> {
|
|
141
|
+
this.wantList.start()
|
|
142
|
+
await this.network.start()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Stop the bitswap node
|
|
147
|
+
*/
|
|
148
|
+
async stop (): Promise<void> {
|
|
149
|
+
this.wantList.stop()
|
|
150
|
+
await this.network.stop()
|
|
151
|
+
}
|
|
152
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const BITSWAP_120 = '/ipfs/bitswap/1.2.0'
|
|
2
|
+
export const DEFAULT_MAX_SIZE_REPLACE_HAS_WITH_BLOCK = 1024
|
|
3
|
+
export const DEFAULT_MAX_INBOUND_STREAMS = 1024
|
|
4
|
+
export const DEFAULT_MAX_OUTBOUND_STREAMS = 1024
|
|
5
|
+
export const DEFAULT_MESSAGE_RECEIVE_TIMEOUT = 5000
|
|
6
|
+
export const DEFAULT_MESSAGE_SEND_DELAY = 10
|
|
7
|
+
export const DEFAULT_MESSAGE_SEND_TIMEOUT = 5000
|
|
8
|
+
export const DEFAULT_MESSAGE_SEND_CONCURRENCY = 50
|
|
9
|
+
export const DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS = false
|
|
10
|
+
export const DEFAULT_SESSION_ROOT_PRIORITY = 1
|
|
11
|
+
export const DEFAULT_MAX_PROVIDERS_PER_REQUEST = 3
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
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
|
+
|
|
9
|
+
import { Bitswap as BitswapClass } from './bitswap.js'
|
|
10
|
+
import type { BitswapNetworkNotifyProgressEvents, BitswapNetworkWantProgressEvents } from './network.js'
|
|
11
|
+
import type { WantType } from './pb/message.js'
|
|
12
|
+
import type { CreateSessionOptions } from '@helia/interface'
|
|
13
|
+
import type { Routing } from '@helia/interface/routing'
|
|
14
|
+
import type { Libp2p, AbortOptions, Startable, ComponentLogger, Metrics, PeerId } from '@libp2p/interface'
|
|
15
|
+
import type { PeerSet } from '@libp2p/peer-collections'
|
|
16
|
+
import type { Blockstore } from 'interface-blockstore'
|
|
17
|
+
import type { CID } from 'multiformats/cid'
|
|
18
|
+
import type { MultihashHasher } from 'multiformats/hashes/interface'
|
|
19
|
+
import type { ProgressEvent, ProgressOptions } from 'progress-events'
|
|
20
|
+
|
|
21
|
+
export type BitswapWantProgressEvents =
|
|
22
|
+
BitswapWantBlockProgressEvents
|
|
23
|
+
|
|
24
|
+
export type BitswapNotifyProgressEvents =
|
|
25
|
+
BitswapNetworkNotifyProgressEvents
|
|
26
|
+
|
|
27
|
+
export type BitswapWantBlockProgressEvents =
|
|
28
|
+
ProgressEvent<'bitswap:want-block:unwant', CID> |
|
|
29
|
+
ProgressEvent<'bitswap:want-block:block', CID> |
|
|
30
|
+
BitswapNetworkWantProgressEvents
|
|
31
|
+
|
|
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
|
+
export interface WantListEntry {
|
|
49
|
+
cid: CID
|
|
50
|
+
priority: number
|
|
51
|
+
wantType: WantType
|
|
52
|
+
}
|
|
53
|
+
|
|
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
|
+
export interface Bitswap extends Startable {
|
|
79
|
+
/**
|
|
80
|
+
* Returns the current state of the wantlist
|
|
81
|
+
*/
|
|
82
|
+
getWantlist(): WantListEntry[]
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns the current state of the wantlist for a peer, if it is being
|
|
86
|
+
* tracked
|
|
87
|
+
*/
|
|
88
|
+
getPeerWantlist(peerId: PeerId): WantListEntry[] | undefined
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Notify bitswap that a new block is available
|
|
92
|
+
*/
|
|
93
|
+
notify(cid: CID, block: Uint8Array, options?: ProgressOptions<BitswapNotifyProgressEvents>): Promise<void>
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Start a session to retrieve a file from the network
|
|
97
|
+
*/
|
|
98
|
+
want(cid: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<Uint8Array>
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Start a session to retrieve a file from the network
|
|
102
|
+
*/
|
|
103
|
+
createSession(root: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<BitswapSession>
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface MultihashHasherLoader {
|
|
107
|
+
getHasher(codeOrName: number | string): Promise<MultihashHasher>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface BitswapComponents {
|
|
111
|
+
routing: Routing
|
|
112
|
+
blockstore: Blockstore
|
|
113
|
+
logger: ComponentLogger
|
|
114
|
+
libp2p: Libp2p
|
|
115
|
+
metrics?: Metrics
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface BitswapOptions {
|
|
119
|
+
/**
|
|
120
|
+
* This is the maximum number of concurrent inbound bitswap streams that are
|
|
121
|
+
* allowed
|
|
122
|
+
*
|
|
123
|
+
* @default 32
|
|
124
|
+
*/
|
|
125
|
+
maxInboundStreams?: number
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* This is the maximum number of concurrent outbound bitswap streams that are
|
|
129
|
+
* allowed
|
|
130
|
+
*
|
|
131
|
+
* @default 128
|
|
132
|
+
*/
|
|
133
|
+
maxOutboundStreams?: number
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* An incoming stream must resolve within this number of seconds
|
|
137
|
+
*
|
|
138
|
+
* @default 30000
|
|
139
|
+
*/
|
|
140
|
+
incomingStreamTimeout?: number
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Whether to run on transient (e.g. time/data limited) connections
|
|
144
|
+
*
|
|
145
|
+
* @default false
|
|
146
|
+
*/
|
|
147
|
+
runOnTransientConnections?: boolean
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Enables loading esoteric hash functions
|
|
151
|
+
*/
|
|
152
|
+
hashLoader?: MultihashHasherLoader
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The protocol that we speak
|
|
156
|
+
*
|
|
157
|
+
* @default '/ipfs/bitswap/1.2.0'
|
|
158
|
+
*/
|
|
159
|
+
protocol?: string
|
|
160
|
+
|
|
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
|
+
/**
|
|
170
|
+
* When sending want list updates to peers, how many messages to send at once
|
|
171
|
+
*
|
|
172
|
+
* @default 50
|
|
173
|
+
*/
|
|
174
|
+
messageSendConcurrency?: number
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* When sending blocks to peers, how many messages to send at once
|
|
178
|
+
*
|
|
179
|
+
* @default 50
|
|
180
|
+
*/
|
|
181
|
+
sendBlocksConcurrency?: number
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* When sending blocks to peers, timeout after this many milliseconds.
|
|
185
|
+
* This is useful for preventing slow/large peer-connections from consuming
|
|
186
|
+
* your bandwidth/streams.
|
|
187
|
+
*
|
|
188
|
+
* @default 10000
|
|
189
|
+
*/
|
|
190
|
+
sendBlocksTimeout?: number
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* When a block is added to the blockstore and we are about to send that block
|
|
194
|
+
* to peers who have it in their wantlist, wait this many milliseconds before
|
|
195
|
+
* queueing the send job in case more blocks are added that they want
|
|
196
|
+
*
|
|
197
|
+
* @default 10
|
|
198
|
+
*/
|
|
199
|
+
sendBlocksDebounce?: number
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* If the client sends a want-have, and we have the corresponding block, we
|
|
203
|
+
* check the size of the block and if it's small enough we send the block
|
|
204
|
+
* itself, rather than sending a HAVE.
|
|
205
|
+
*
|
|
206
|
+
* This defines the maximum size up to which we replace a HAVE with a block.
|
|
207
|
+
*
|
|
208
|
+
* @default 1024
|
|
209
|
+
*/
|
|
210
|
+
maxSizeReplaceHasWithBlock?: number
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export const createBitswap = (components: BitswapComponents, options: BitswapOptions = {}): Bitswap => {
|
|
214
|
+
return new BitswapClass(components, options)
|
|
215
|
+
}
|