ipfs-bitswap
Version:
JavaScript implementation of the Bitswap data exchange protocol used by IPFS
149 lines (123 loc) • 4.03 kB
text/typescript
import { trackedMap } from '@libp2p/utils/tracked-map'
import { base58btc } from 'multiformats/bases/base58'
import * as CONSTANTS from '../constants.js'
import { BitswapMessage as Message } from '../message/index.js'
import { logger } from '../utils/index.js'
import { Wantlist } from '../wantlist/index.js'
import { MsgQueue } from './msg-queue.js'
import type { BitswapWantBlockProgressEvents } from '../index.js'
import type { Network } from '../network.js'
import type { Stats } from '../stats/index.js'
import type { Libp2p, PeerId } from '@libp2p/interface'
import type { Logger } from '@libp2p/logger'
import type { AbortOptions } from '@multiformats/multiaddr'
import type { CID } from 'multiformats/cid'
import type { ProgressOptions } from 'progress-events'
export class WantManager {
private readonly peers: Map<string, MsgQueue>
public wantlist: Wantlist
public network: Network
private readonly _peerId: PeerId
private readonly _log: Logger
constructor (peerId: PeerId, network: Network, stats: Stats, libp2p: Libp2p) {
this.peers = trackedMap({
name: 'ipfs_bitswap_want_manager_peers',
metrics: libp2p.metrics
})
this.wantlist = new Wantlist(stats, libp2p)
this.network = network
this._peerId = peerId
this._log = logger(peerId, 'want')
}
_addEntries (cids: CID[], cancel: boolean, force?: boolean, options: ProgressOptions<BitswapWantBlockProgressEvents> = {}): void {
const entries = cids.map((cid, i) => {
return new Message.Entry(cid, CONSTANTS.kMaxPriority - i, Message.WantType.Block, cancel)
})
entries.forEach((e) => {
// add changes to our wantlist
if (e.cancel) {
if (force === true) {
this.wantlist.removeForce(e.cid.toString(base58btc))
} else {
this.wantlist.remove(e.cid)
}
} else {
this._log('adding to wantlist')
// TODO: Figure out the wantType
// @ts-expect-error - requires wantType
this.wantlist.add(e.cid, e.priority)
}
})
// broadcast changes
for (const p of this.peers.values()) {
p.addEntries(entries, options)
}
}
_startPeerHandler (peerId: PeerId): MsgQueue | undefined {
let mq = this.peers.get(peerId.toString())
if (mq != null) {
mq.refcnt++
return
}
mq = new MsgQueue(this._peerId, peerId, this.network)
// new peer, give them the full wantlist
const fullwantlist = new Message(true)
for (const entry of this.wantlist.entries()) {
fullwantlist.addEntry(entry[1].cid, entry[1].priority)
}
mq.addMessage(fullwantlist)
this.peers.set(peerId.toString(), mq)
return mq
}
_stopPeerHandler (peerId: PeerId): void {
const mq = this.peers.get(peerId.toString())
if (mq == null) {
return
}
mq.refcnt--
if (mq.refcnt > 0) {
return
}
this.peers.delete(peerId.toString())
}
/**
* add all the cids to the wantlist
*/
wantBlocks (cids: CID[], options: AbortOptions & ProgressOptions<BitswapWantBlockProgressEvents> = {}): void {
this._addEntries(cids, false, false, options)
options.signal?.addEventListener('abort', () => {
this.cancelWants(cids)
})
}
/**
* Remove blocks of all the given keys without respecting refcounts
*/
unwantBlocks (cids: CID[]): void {
this._log('unwant blocks: %s', cids.length)
this._addEntries(cids, true, true)
}
/**
* Cancel wanting all of the given keys
*/
cancelWants (cids: CID[]): void {
this._log('cancel wants: %s', cids.length)
this._addEntries(cids, true)
}
/**
* Returns a list of all currently connected peers
*/
connectedPeers (): string[] {
return Array.from(this.peers.keys())
}
connected (peerId: PeerId): void {
this._startPeerHandler(peerId)
}
disconnected (peerId: PeerId): void {
this._stopPeerHandler(peerId)
}
start (): void {
}
stop (): void {
this.peers.forEach((mq) => { this.disconnected(mq.peerId) })
}
}