ipfs-bitswap
Version:
JavaScript implementation of the Bitswap data exchange protocol used by IPFS
91 lines (72 loc) • 2.75 kB
text/typescript
import { EventEmitter } from 'events'
import { CustomProgressEvent, type ProgressOptions } from 'progress-events'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import * as CONSTANTS from './constants.js'
import { logger } from './utils/index.js'
import type { BitswapWantBlockProgressEvents } from './index.js'
import type { AbortOptions, PeerId } from '@libp2p/interface'
import type { Logger } from '@libp2p/logger'
import type { CID } from 'multiformats/cid'
const unwantEvent = (cid: CID): string => `unwant:${uint8ArrayToString(cid.multihash.bytes, 'base64')}`
const blockEvent = (cid: CID): string => `block:${uint8ArrayToString(cid.multihash.bytes, 'base64')}`
export class Notifications extends EventEmitter {
private readonly _log: Logger
/**
* Internal module used to track events about incoming blocks,
* wants and unwants.
*/
constructor (peerId: PeerId) {
super()
this.setMaxListeners(CONSTANTS.maxListeners)
this._log = logger(peerId, 'notif')
}
/**
* Signal the system that we received `block`.
*/
hasBlock (cid: CID, block: Uint8Array): void {
const event = blockEvent(cid)
this._log(event)
this.emit(event, block)
}
/**
* Signal the system that we are waiting to receive the
* block associated with the given `cid`.
* Returns a Promise that resolves to the block when it is received,
* or undefined when the block is unwanted.
*/
async wantBlock (cid: CID, options: AbortOptions & ProgressOptions<BitswapWantBlockProgressEvents> = {}): Promise<Uint8Array> {
if (cid == null) {
throw new Error('Not a valid cid')
}
const blockEvt = blockEvent(cid)
const unwantEvt = unwantEvent(cid)
this._log(`wantBlock:${cid}`)
return new Promise((resolve, reject) => {
const onUnwant = (): void => {
this.removeListener(blockEvt, onBlock)
options.onProgress?.(new CustomProgressEvent<CID>('bitswap:want-block:unwant', cid))
reject(new Error(`Block for ${cid} unwanted`))
}
const onBlock = (data: Uint8Array): void => {
this.removeListener(unwantEvt, onUnwant)
options.onProgress?.(new CustomProgressEvent<CID>('bitswap:want-block:block', cid))
resolve(data)
}
this.once(unwantEvt, onUnwant)
this.once(blockEvt, onBlock)
options.signal?.addEventListener('abort', () => {
this.removeListener(blockEvt, onBlock)
this.removeListener(unwantEvt, onUnwant)
reject(new Error(`Want for ${cid} aborted`))
})
})
}
/**
* Signal that the block is not wanted anymore
*/
unwantBlock (cid: CID): void {
const event = unwantEvent(cid)
this._log(event)
this.emit(event)
}
}