Skip to content

Commit 215051f

Browse files
authored
Merge branch 'master' into web3-bot/sync
2 parents 81cf83c + 364ca5b commit 215051f

24 files changed

+357
-321
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## [17.0.0](https://github.com/ipfs/js-ipfs-bitswap/compare/v16.0.0...v17.0.0) (2023-03-13)
2+
3+
4+
### ⚠ BREAKING CHANGES
5+
6+
* `.get`, `.getMany`, `.put` and `.putMany` are no longer part of the `Bitswap` interface - instead call `.want` and `.notify`
7+
8+
### Features
9+
10+
* simplify bitswap interface, add progress handlers ([#527](https://github.com/ipfs/js-ipfs-bitswap/issues/527)) ([1f31995](https://github.com/ipfs/js-ipfs-bitswap/commit/1f3199505ac53e3c16cb8ea713d2279fbe69acb1))
11+
112
## [16.0.0](https://github.com/ipfs/js-ipfs-bitswap/compare/v15.0.2...v16.0.0) (2023-02-13)
213

314

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Loading this module through a script tag will make it's exports available as `Ip
4040
```js
4141
const bitswapNode = // ...
4242

43-
const stats = bitswapNode.stat()
43+
const stats = bitswapNode.stats
4444
```
4545

4646
Stats contains a snapshot accessor, a moving average acessor and a peer accessor.

package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ipfs-bitswap",
3-
"version": "16.0.0",
3+
"version": "17.0.0",
44
"description": "JavaScript implementation of the Bitswap data exchange protocol used by IPFS",
55
"license": "Apache-2.0 OR MIT",
66
"homepage": "https://github.com/ipfs/js-ipfs-bitswap#readme",
@@ -158,7 +158,7 @@
158158
"test:firefox": "aegir test -t browser -- --browser firefox",
159159
"test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
160160
"test:electron-main": "aegir test -t electron-main",
161-
"dep-check": "aegir dep-check",
161+
"dep-check": "aegir dep-check -i protons",
162162
"generate": "protons ./src/message/message.proto",
163163
"docs": "aegir docs"
164164
},
@@ -176,12 +176,15 @@
176176
"@vascosantos/moving-average": "^1.1.0",
177177
"abortable-iterator": "^4.0.2",
178178
"any-signal": "^3.0.0",
179-
"blockstore-core": "^3.0.0",
180-
"interface-blockstore": "^4.0.0",
179+
"blockstore-core": "^4.0.0",
180+
"interface-blockstore": "^5.0.0",
181181
"it-length-prefixed": "^8.0.2",
182+
"it-map": "^2.0.1",
182183
"it-pipe": "^2.0.4",
184+
"it-take": "^2.0.1",
183185
"just-debounce-it": "^3.0.1",
184186
"multiformats": "^11.0.0",
187+
"progress-events": "^1.0.0",
185188
"protons-runtime": "^5.0.0",
186189
"timeout-abort-controller": "^3.0.0",
187190
"uint8arraylist": "^2.4.3",

src/bitswap.ts

Lines changed: 34 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { Notifications } from './notifications.js'
55
import { logger } from './utils/index.js'
66
import { Stats } from './stats/index.js'
77
import { anySignal } from 'any-signal'
8-
import { BaseBlockstore } from 'blockstore-core/base'
98
import { CID } from 'multiformats/cid'
10-
import type { BitswapOptions, Bitswap, MultihashHasherLoader, WantListEntry } from './index.js'
9+
import type { BitswapOptions, Bitswap, MultihashHasherLoader, WantListEntry, BitswapWantProgressEvents, BitswapNotifyProgressEvents } from './index.js'
1110
import type { Libp2p } from '@libp2p/interface-libp2p'
1211
import type { Blockstore, Options, Pair } from 'interface-blockstore'
1312
import type { Logger } from '@libp2p/logger'
1413
import type { PeerId } from '@libp2p/interface-peer-id'
1514
import type { BitswapMessage } from './message/index.js'
1615
import type { AbortOptions } from '@multiformats/multiaddr'
16+
import type { ProgressOptions } from 'progress-events'
1717

1818
const hashLoader: MultihashHasherLoader = {
1919
async getHasher () {
@@ -46,35 +46,33 @@ const statsKeys = [
4646
* JavaScript implementation of the Bitswap 'data exchange' protocol
4747
* used by IPFS.
4848
*/
49-
export class DefaultBitswap extends BaseBlockstore implements Bitswap {
49+
export class DefaultBitswap implements Bitswap {
5050
private readonly _libp2p: Libp2p
5151
private readonly _log: Logger
5252
private readonly _options: Required<BitswapOptions>
53-
private readonly _stats: Stats
53+
public readonly stats: Stats
5454
public network: Network
5555
public blockstore: Blockstore
5656
public engine: DecisionEngine
5757
public wm: WantManager
5858
public notifications: Notifications
59-
public started: boolean
59+
private started: boolean
6060

6161
constructor (libp2p: Libp2p, blockstore: Blockstore, options: BitswapOptions = {}) {
62-
super()
63-
6462
this._libp2p = libp2p
6563
this._log = logger(this.peerId)
6664

6765
this._options = Object.assign({}, defaultOptions, options)
6866

6967
// stats
70-
this._stats = new Stats(libp2p, statsKeys, {
68+
this.stats = new Stats(libp2p, statsKeys, {
7169
enabled: this._options.statsEnabled,
7270
computeThrottleTimeout: this._options.statsComputeThrottleTimeout,
7371
computeThrottleMaxQueueSize: this._options.statsComputeThrottleMaxQueueSize
7472
})
7573

76-
// the network delivers messages
77-
this.network = new Network(libp2p, this, this._stats, {
74+
// the network delivers a messages
75+
this.network = new Network(libp2p, this, this.stats, {
7876
hashLoader: options.hashLoader,
7977
maxInboundStreams: options.maxInboundStreams,
8078
maxOutboundStreams: options.maxOutboundStreams,
@@ -84,10 +82,10 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
8482
// local database
8583
this.blockstore = blockstore
8684

87-
this.engine = new DecisionEngine(this.peerId, blockstore, this.network, this._stats, libp2p)
85+
this.engine = new DecisionEngine(this.peerId, blockstore, this.network, this.stats, libp2p)
8886

8987
// handle message sending
90-
this.wm = new WantManager(this.peerId, this.network, this._stats, libp2p)
88+
this.wm = new WantManager(this.peerId, this.network, this.stats, libp2p)
9189
this.notifications = new Notifications(this.peerId)
9290
this.started = false
9391
}
@@ -162,12 +160,12 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
162160
}
163161

164162
_updateReceiveCounters (peerIdStr: string, cid: CID, data: Uint8Array, exists: boolean): void {
165-
this._stats.push(peerIdStr, 'blocksReceived', 1)
166-
this._stats.push(peerIdStr, 'dataReceived', data.length)
163+
this.stats.push(peerIdStr, 'blocksReceived', 1)
164+
this.stats.push(peerIdStr, 'dataReceived', data.length)
167165

168166
if (exists) {
169-
this._stats.push(peerIdStr, 'dupBlksReceived', 1)
170-
this._stats.push(peerIdStr, 'dupDataReceived', data.length)
167+
this.stats.push(peerIdStr, 'dupBlksReceived', 1)
168+
this.stats.push(peerIdStr, 'dupDataReceived', data.length)
171169
}
172170
}
173171

@@ -191,15 +189,15 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
191189
_onPeerDisconnected (peerId: PeerId): void {
192190
this.wm.disconnected(peerId)
193191
this.engine.peerDisconnected(peerId)
194-
this._stats.disconnected(peerId)
192+
this.stats.disconnected(peerId)
195193
}
196194

197195
enableStats (): void {
198-
this._stats.enable()
196+
this.stats.enable()
199197
}
200198

201199
disableStats (): void {
202-
this._stats.disable()
200+
this.stats.disable()
203201
}
204202

205203
/**
@@ -220,8 +218,8 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
220218
* Fetch a given block by cid. If the block is in the local
221219
* blockstore it is returned, otherwise the block is added to the wantlist and returned once another node sends it to us.
222220
*/
223-
async get (cid: CID, options: AbortOptions = {}): Promise<Uint8Array> {
224-
const fetchFromNetwork = async (cid: CID, options: AbortOptions): Promise<Uint8Array> => {
221+
async want (cid: CID, options: AbortOptions & ProgressOptions<BitswapWantProgressEvents> = {}): Promise<Uint8Array> {
222+
const fetchFromNetwork = async (cid: CID, options: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<Uint8Array> => {
225223
// add it to the want list - n.b. later we will abort the AbortSignal
226224
// so no need to remove the blocks from the wantlist after we have it
227225
this.wm.wantBlocks([cid], options)
@@ -231,7 +229,7 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
231229

232230
let promptedNetwork = false
233231

234-
const loadOrFetchFromNetwork = async (cid: CID, options: AbortOptions): Promise<Uint8Array> => {
232+
const loadOrFetchFromNetwork = async (cid: CID, options: AbortOptions & ProgressOptions<BitswapWantProgressEvents>): Promise<Uint8Array> => {
235233
try {
236234
// have to await here as we want to handle ERR_NOT_FOUND
237235
const block = await this.blockstore.get(cid, options)
@@ -266,30 +264,23 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
266264
try {
267265
const block = await Promise.race([
268266
this.notifications.wantBlock(cid, {
267+
...options,
269268
signal
270269
}),
271270
loadOrFetchFromNetwork(cid, {
271+
...options,
272272
signal
273273
})
274274
])
275275

276276
return block
277277
} finally {
278-
// since we have the block we can now remove our listener
278+
// since we have the block we can now abort any outstanding attempts to
279+
// fetch it
279280
controller.abort()
280281
}
281282
}
282283

283-
/**
284-
* Fetch a a list of blocks by cid. If the blocks are in the local
285-
* blockstore they are returned, otherwise the blocks are added to the wantlist and returned once another node sends them to us.
286-
*/
287-
async * getMany (cids: AsyncIterable<CID> | Iterable<CID>, options: AbortOptions = {}): AsyncGenerator<Uint8Array> {
288-
for await (const cid of cids) {
289-
yield this.get(cid, options)
290-
}
291-
}
292-
293284
/**
294285
* Removes the given CIDs from the wantlist independent of any ref counts.
295286
*
@@ -320,29 +311,29 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
320311
*/
321312
async put (cid: CID, block: Uint8Array, _options?: any): Promise<void> {
322313
await this.blockstore.put(cid, block)
323-
this._sendHaveBlockNotifications(cid, block)
314+
this.notify(cid, block)
324315
}
325316

326317
/**
327318
* Put the given blocks to the underlying blockstore and
328319
* send it to nodes that have it them their wantlist.
329320
*/
330321
async * putMany (source: Iterable<Pair> | AsyncIterable<Pair>, options?: Options): AsyncGenerator<Pair> {
331-
for await (const { key, value } of this.blockstore.putMany(source, options)) {
332-
this._sendHaveBlockNotifications(key, value)
322+
for await (const { cid, block } of this.blockstore.putMany(source, options)) {
323+
this.notify(cid, block)
333324

334-
yield { key, value }
325+
yield { cid, block }
335326
}
336327
}
337328

338329
/**
339330
* Sends notifications about the arrival of a block
340331
*/
341-
_sendHaveBlockNotifications (cid: CID, data: Uint8Array): void {
342-
this.notifications.hasBlock(cid, data)
343-
this.engine.receivedBlocks([{ cid, data }])
332+
notify (cid: CID, block: Uint8Array, options: ProgressOptions<BitswapNotifyProgressEvents> = {}): void {
333+
this.notifications.hasBlock(cid, block)
334+
this.engine.receivedBlocks([{ cid, block }])
344335
// Note: Don't wait for provide to finish before returning
345-
this.network.provide(cid).catch((err) => {
336+
this.network.provide(cid, options).catch((err) => {
346337
this._log.error('Failed to provide: %s', err.message)
347338
})
348339
}
@@ -357,17 +348,10 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
357348
/**
358349
* Get the current list of partners
359350
*/
360-
peers (): PeerId[] {
351+
get peers (): PeerId[] {
361352
return this.engine.peers()
362353
}
363354

364-
/**
365-
* Get stats about the bitswap node
366-
*/
367-
stat (): Stats {
368-
return this._stats
369-
}
370-
371355
/**
372356
* Start the bitswap node
373357
*/
@@ -382,18 +366,10 @@ export class DefaultBitswap extends BaseBlockstore implements Bitswap {
382366
* Stop the bitswap node
383367
*/
384368
async stop (): Promise<void> {
385-
this._stats.stop()
369+
this.stats.stop()
386370
this.wm.stop()
387371
await this.network.stop()
388372
this.engine.stop()
389373
this.started = false
390374
}
391-
392-
unwrap (): Blockstore {
393-
return this.blockstore
394-
}
395-
396-
async has (cid: CID): Promise<boolean> {
397-
return await this.blockstore.has(cid)
398-
}
399375
}

src/decision-engine/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,24 +260,24 @@ export class DecisionEngine {
260260
* Receive blocks either from an incoming message from the network, or from
261261
* blocks being added by the client on the localhost (eg IPFS add)
262262
*/
263-
receivedBlocks (blocks: Array<{ cid: CID, data: Uint8Array }>): void {
263+
receivedBlocks (blocks: Array<{ cid: CID, block: Uint8Array }>): void {
264264
if (blocks.length === 0) {
265265
return
266266
}
267267

268268
// For each connected peer, check if it wants the block we received
269269
for (const ledger of this.ledgerMap.values()) {
270-
for (const block of blocks) {
270+
for (const { cid, block } of blocks) {
271271
// Filter out blocks that we don't want
272-
const want = ledger.wantlistContains(block.cid)
272+
const want = ledger.wantlistContains(cid)
273273

274274
if (want == null) {
275275
continue
276276
}
277277

278278
// If the block is small enough, just send the block, even if the
279279
// client asked for a HAVE
280-
const blockSize = block.data.length
280+
const blockSize = block.length
281281
const isWantBlock = this._sendAsBlock(want.wantType, blockSize)
282282

283283
let entrySize = blockSize

src/index.ts

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import type { Message } from './message/message'
66
import type { IMovingAverage } from '@vascosantos/moving-average'
77
import type { MultihashHasher } from 'multiformats/hashes/interface'
88
import type { Libp2p } from '@libp2p/interface-libp2p'
9+
import type { AbortOptions } from '@libp2p/interfaces'
10+
import type { Startable } from '@libp2p/interfaces/startable'
11+
import type { ProgressEvent, ProgressOptions } from 'progress-events'
12+
import type { BitswapNetworkNotifyProgressEvents, BitswapNetworkWantProgressEvents } from './network.js'
913

1014
export interface WantListEntry {
1115
cid: CID
@@ -54,21 +58,43 @@ export interface Stats {
5458
push: (peer: string, counter: string, inc: number) => void
5559
}
5660

57-
export interface Bitswap extends Blockstore {
58-
peerId: PeerId
59-
isStarted: () => boolean
60-
enableStats: () => void
61-
disableStats: () => void
61+
export type BitswapWantProgressEvents =
62+
BitswapWantBlockProgressEvents
63+
64+
export type BitswapNotifyProgressEvents =
65+
BitswapNetworkNotifyProgressEvents
66+
67+
export type BitswapWantBlockProgressEvents =
68+
ProgressEvent<'bitswap:want-block:unwant', CID> |
69+
ProgressEvent<'bitswap:want-block:block', CID> |
70+
BitswapNetworkWantProgressEvents
71+
72+
export interface Bitswap extends Startable {
73+
/**
74+
* Bitswap statistics
75+
*/
76+
stats: Stats
77+
78+
/**
79+
* The peers that we are tracking a ledger for
80+
*/
81+
peers: PeerId[]
82+
6283
wantlistForPeer: (peerId: PeerId) => Map<string, WantListEntry>
6384
ledgerForPeer: (peerId: PeerId) => Ledger | undefined
6485
unwant: (cids: CID | CID[]) => void
6586
cancelWants: (cids: CID | CID[]) => void
6687
getWantlist: () => IterableIterator<[string, WantListEntry]>
67-
peers: () => PeerId[]
68-
stat: () => Stats
69-
start: () => void
70-
stop: () => void
71-
unwrap: () => Blockstore
88+
89+
/**
90+
* Notify bitswap that a new block is available
91+
*/
92+
notify: (cid: CID, block: Uint8Array, options?: ProgressOptions<BitswapNotifyProgressEvents>) => void
93+
94+
/**
95+
* Retrieve a block from the network
96+
*/
97+
want: (cid: CID, options?: AbortOptions & ProgressOptions<BitswapWantProgressEvents>) => Promise<Uint8Array>
7298
}
7399

74100
export interface MultihashHasherLoader {

0 commit comments

Comments
 (0)