@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
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
+ }
@@ -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
+ }