@helia/bitswap 3.2.3-f6cc7640 → 4.0.0

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.
@@ -0,0 +1,119 @@
1
+ import { isPeerId } from '@libp2p/interface'
2
+ import { CustomProgressEvent } from 'progress-events'
3
+ import { Bitswap } from './bitswap.ts'
4
+ import type { BitswapOptions, BitswapWantBlockProgressEvents, BitswapNotifyProgressEvents } from './index.ts'
5
+ import type { BlockAnnounceOptions, BlockBroker, BlockRetrievalOptions, CreateSessionOptions, Routing, HasherLoader, SessionBlockBroker, BlockBrokerConnectProgressEvent, BlockBrokerConnectedProgressEvent, BlockBrokerRequestBlockProgressEvent, BlockBrokerReceiveBlockProgressEvent } from '@helia/interface'
6
+ import type { Libp2p, Startable, ComponentLogger } from '@libp2p/interface'
7
+ import type { Blockstore } from 'interface-blockstore'
8
+ import type { CID } from 'multiformats/cid'
9
+
10
+ export interface BitswapBlockBrokerComponents {
11
+ libp2p: Libp2p
12
+ blockstore: Blockstore
13
+ routing: Routing
14
+ logger: ComponentLogger
15
+ getHasher: HasherLoader
16
+ }
17
+
18
+ export interface BitswapBlockBrokerInit extends BitswapOptions {
19
+
20
+ }
21
+
22
+ export class BitswapBlockBroker implements BlockBroker<BitswapWantBlockProgressEvents, BitswapNotifyProgressEvents>, Startable {
23
+ public readonly name = 'bitswap'
24
+ private readonly bitswap: Bitswap
25
+ private started: boolean
26
+
27
+ constructor (components: BitswapBlockBrokerComponents, init: BitswapBlockBrokerInit = {}) {
28
+ this.bitswap = new Bitswap(components, init)
29
+ this.started = false
30
+ }
31
+
32
+ isStarted (): boolean {
33
+ return this.started
34
+ }
35
+
36
+ async start (): Promise<void> {
37
+ await this.bitswap.start()
38
+ this.started = true
39
+ }
40
+
41
+ async stop (): Promise<void> {
42
+ await this.bitswap.stop()
43
+ this.started = false
44
+ }
45
+
46
+ async announce (cid: CID, options?: BlockAnnounceOptions<BitswapNotifyProgressEvents>): Promise<void> {
47
+ await this.bitswap.notify(cid, options)
48
+ }
49
+
50
+ async retrieve (cid: CID, options: BlockRetrievalOptions<BitswapWantBlockProgressEvents> = {}): Promise<Uint8Array> {
51
+ return this.bitswap.want(cid, {
52
+ ...options,
53
+ onProgress: function bitswapBlockBrokerRetrieveCallback (evt) {
54
+ if (options?.onProgress == null) {
55
+ return
56
+ }
57
+
58
+ options.onProgress(evt)
59
+
60
+ if (evt.type === 'connection:open') {
61
+ if (!isPeerId(evt.detail)) {
62
+ // should not happen as bitswap impl only sends wantlist to
63
+ // connected peers so we always have a peer id
64
+ return
65
+ }
66
+
67
+ options.onProgress(new CustomProgressEvent<BlockBrokerConnectProgressEvent>('helia:block-broker:connect', {
68
+ broker: 'bitswap',
69
+ type: 'connect',
70
+ provider: evt.detail.toCID(),
71
+ cid
72
+ }))
73
+ } else if (evt.type === 'connection:opened') {
74
+ options.onProgress(new CustomProgressEvent<BlockBrokerConnectedProgressEvent>('helia:block-broker:connected', {
75
+ broker: 'bitswap',
76
+ type: 'connected',
77
+ provider: evt.detail.remotePeer.toCID(),
78
+ address: evt.detail.remoteAddr,
79
+ cid
80
+ }))
81
+ } else if (evt.type === 'bitswap:send-wantlist') {
82
+ options.onProgress(new CustomProgressEvent<BlockBrokerRequestBlockProgressEvent>('helia:block-broker:request-block', {
83
+ broker: 'bitswap',
84
+ type: 'request-block',
85
+ provider: evt.detail.toCID(),
86
+ cid
87
+ }))
88
+ } else if (evt.type === 'bitswap:block') {
89
+ options.onProgress(new CustomProgressEvent<BlockBrokerReceiveBlockProgressEvent>('helia:block-broker:receive-block', {
90
+ broker: 'bitswap',
91
+ type: 'receive-block',
92
+ provider: evt.detail.sender.toCID(),
93
+ cid
94
+ }))
95
+ }
96
+ }
97
+ })
98
+ }
99
+
100
+ createSession (options?: CreateSessionOptions<BitswapWantBlockProgressEvents>): SessionBlockBroker<BitswapWantBlockProgressEvents, BitswapNotifyProgressEvents> {
101
+ const session = this.bitswap.createSession(options)
102
+
103
+ return {
104
+ name: 'bitswap-session',
105
+
106
+ addPeer: async (peer, options) => {
107
+ await session.addPeer(peer, options)
108
+ },
109
+
110
+ announce: async (cid, options) => {
111
+ await this.bitswap.notify(cid, options)
112
+ },
113
+
114
+ retrieve: async (cid, options) => {
115
+ return session.retrieve(cid, options)
116
+ }
117
+ }
118
+ }
119
+ }
package/src/index.ts CHANGED
@@ -6,11 +6,14 @@
6
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
7
  */
8
8
 
9
- import { Bitswap as BitswapClass } from './bitswap.ts'
9
+ import { start } from '@libp2p/interface'
10
+ import { BitswapBlockBroker } from './block-broker.ts'
11
+ import type { BitswapBlockBrokerInit } from './block-broker.ts'
10
12
  import type { BitswapNetworkNotifyProgressEvents, BitswapNetworkWantProgressEvents, BitswapNetworkProgressEvents } from './network.ts'
11
13
  import type { WantType } from './pb/message.ts'
12
- import type { BlockBrokerGetBlockProgressEvents, CreateSessionOptions, HasherLoader, ProviderOptions, SessionBlockBroker } from '@helia/interface'
13
- import type { Routing } from '@helia/interface/routing'
14
+ import type { BlockBrokerGetBlockProgressEvents, CreateSessionOptions, HasherLoader, HeliaMixin, ProviderOptions, SessionBlockBroker } from '@helia/interface'
15
+ import type { Routing } from '@helia/interface'
16
+ import type { HeliaWithLibp2p } from '@helia/libp2p'
14
17
  import type { Libp2p, AbortOptions, Startable, ComponentLogger, Metrics, PeerId } from '@libp2p/interface'
15
18
  import type { Blockstore } from 'interface-blockstore'
16
19
  import type { CID } from 'multiformats/cid'
@@ -34,6 +37,7 @@ export type { BitswapNetworkWantProgressEvents }
34
37
  export type { BitswapNetworkProgressEvents }
35
38
  export type { WantType }
36
39
  export type { BitswapProvider } from './network.ts'
40
+ export type { BitswapBlockBrokerInit } from './block-broker.ts'
37
41
 
38
42
  export type WantStatus = 'want' | 'sending' | 'sent'
39
43
 
@@ -227,6 +231,23 @@ export interface BitswapOptions {
227
231
  maxWantlistSize?: number
228
232
  }
229
233
 
230
- export const createBitswap = (components: BitswapComponents, options: BitswapOptions = {}): Bitswap => {
231
- return new BitswapClass(components, options)
234
+ /**
235
+ * Return a Helia node augmented with a libp2p instance
236
+ */
237
+ export function withBitswap (helia: HeliaWithLibp2p, options: BitswapBlockBrokerInit = {}): HeliaWithLibp2p {
238
+ const mixin: HeliaMixin<HeliaWithLibp2p> = {
239
+ name: 'bitswap',
240
+ start: async (helia) => {
241
+ if (helia.status === 'starting' && !helia.hasBlockBroker('bitswap')) {
242
+ const broker = new BitswapBlockBroker(helia, options)
243
+ await start(broker)
244
+
245
+ helia.addBlockBroker(broker)
246
+ }
247
+ }
248
+ }
249
+
250
+ helia.addMixin(mixin)
251
+
252
+ return helia
232
253
  }
package/src/network.ts CHANGED
@@ -1,5 +1,8 @@
1
- import { InvalidParametersError, NotStartedError, TimeoutError, TypedEventEmitter, UnsupportedProtocolError, setMaxListeners } from '@libp2p/interface'
1
+ import { isCID } from '@helia/utils'
2
+ import { InvalidParametersError, NotStartedError, TimeoutError, TypedEventEmitter, isPeerId, setMaxListeners } from '@libp2p/interface'
3
+ import { peerIdFromCID } from '@libp2p/peer-id'
2
4
  import { PeerQueue } from '@libp2p/utils'
5
+ import { isMultiaddr } from '@multiformats/multiaddr'
3
6
  import drain from 'it-drain'
4
7
  import * as lp from 'it-length-prefixed'
5
8
  import map from 'it-map'
@@ -7,7 +10,6 @@ import { pushable } from 'it-pushable'
7
10
  import take from 'it-take'
8
11
  import { CID } from 'multiformats/cid'
9
12
  import { CustomProgressEvent } from 'progress-events'
10
- import { raceEvent } from 'race-event'
11
13
  import { BITSWAP_120, DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_INCOMING_MESSAGE_SIZE, DEFAULT_MAX_OUTBOUND_STREAMS, DEFAULT_MAX_OUTGOING_MESSAGE_SIZE, DEFAULT_MAX_PROVIDERS_PER_REQUEST, DEFAULT_MESSAGE_RECEIVE_TIMEOUT, DEFAULT_MESSAGE_SEND_CONCURRENCY, DEFAULT_MESSAGE_SEND_TIMEOUT, DEFAULT_RUN_ON_TRANSIENT_CONNECTIONS } from './constants.ts'
12
14
  import { BitswapMessage } from './pb/message.ts'
13
15
  import { mergeMessages } from './utils/merge-messages.ts'
@@ -16,8 +18,8 @@ import type { WantOptions } from './bitswap.ts'
16
18
  import type { Block } from './pb/message.ts'
17
19
  import type { QueuedBitswapMessage } from './utils/bitswap-message.ts'
18
20
  import type { BlockBrokerGetBlockProgressEvents } from '@helia/interface'
19
- import type { Provider, Routing, RoutingFindProvidersProgressEvents } from '@helia/interface/routing'
20
- import type { Libp2p, AbortOptions, Connection, PeerId, Topology, ComponentLogger, IdentifyResult, Counter, Metrics, Stream, OpenConnectionProgressEvents, NewStreamProgressEvents } from '@libp2p/interface'
21
+ import type { Provider, Routing, RoutingFindProvidersProgressEvents } from '@helia/interface'
22
+ import type { Libp2p, AbortOptions, Connection, PeerId, Topology, ComponentLogger, Counter, Metrics, Stream, OpenConnectionProgressEvents, NewStreamProgressEvents } from '@libp2p/interface'
21
23
  import type { Logger } from '@libp2p/logger'
22
24
  import type { PeerQueueJobOptions } from '@libp2p/utils'
23
25
  import type { Multiaddr } from '@multiformats/multiaddr'
@@ -102,6 +104,22 @@ interface SendMessageJobOptions extends AbortOptions, ProgressOptions<BitswapNet
102
104
  message: QueuedBitswapMessage
103
105
  }
104
106
 
107
+ function toDialable (peer: CID | PeerId | Multiaddr | Multiaddr[]): PeerId | Multiaddr | Multiaddr[] {
108
+ if (isPeerId(peer)) {
109
+ return peer
110
+ }
111
+
112
+ if (isCID(peer)) {
113
+ return peerIdFromCID(peer)
114
+ }
115
+
116
+ if (isMultiaddr(peer) || Array.isArray(peer)) {
117
+ return peer
118
+ }
119
+
120
+ throw new InvalidParametersError(`Peer ${peer} was not a CID, a Multiaddr or a Multiaddr[]`)
121
+ }
122
+
105
123
  export class Network extends TypedEventEmitter<NetworkEvents> {
106
124
  private readonly log: Logger
107
125
  private readonly libp2p: Libp2p
@@ -236,7 +254,6 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
236
254
  const input = pushable<Uint8Array | Uint8ArrayList>()
237
255
 
238
256
  stream.addEventListener('message', (evt) => {
239
- // @ts-expect-error libp2p needs dep updates
240
257
  input.push(evt.data)
241
258
  })
242
259
  stream.addEventListener('remoteCloseWrite', () => {
@@ -316,7 +333,7 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
316
333
  // connect to initial session providers if supplied
317
334
  if (options?.providers != null) {
318
335
  await Promise.all(
319
- options.providers.map(async prov => this.connectTo(prov)
336
+ options.providers.map(async prov => this.connectTo(prov, options)
320
337
  .catch(err => {
321
338
  this.log.error('could not connect to supplied provider - %e', err)
322
339
  }))
@@ -371,7 +388,6 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
371
388
 
372
389
  try {
373
390
  for (const buf of splitMessage(message, this.maxOutgoingMessageSize)) {
374
- // @ts-expect-error libp2p needs dep updates
375
391
  if (!stream.send(lp.encode.single(buf))) {
376
392
  await stream.onDrain(options)
377
393
  }
@@ -399,36 +415,16 @@ export class Network extends TypedEventEmitter<NetworkEvents> {
399
415
  /**
400
416
  * Connects to another peer
401
417
  */
402
- async connectTo (peer: PeerId | Multiaddr | Multiaddr[], options?: AbortOptions & ProgressOptions<BitswapNetworkProgressEvents>): Promise<Connection> {
418
+ async connectTo (peer: CID | PeerId | Multiaddr | Multiaddr[], options?: AbortOptions & ProgressOptions<BitswapNetworkProgressEvents>): Promise<Connection> {
403
419
  if (!this.running) {
404
420
  throw new NotStartedError('Network isn\'t running')
405
421
  }
406
422
 
407
- options?.onProgress?.(new CustomProgressEvent<PeerId | Multiaddr | Multiaddr[]>('bitswap:dial', peer))
408
-
409
- // dial and wait for identify - this is to avoid opening a protocol stream
410
- // that we are not going to use but depends on the remote node running the
411
- // identify protocol
412
- const [
413
- connection
414
- ] = await Promise.all([
415
- this.libp2p.dial(peer, options),
416
- raceEvent(this.libp2p, 'peer:identify', options?.signal, {
417
- filter: (evt: CustomEvent<IdentifyResult>): boolean => {
418
- if (!evt.detail.peerId.equals(peer)) {
419
- return false
420
- }
421
-
422
- if (evt.detail.protocols.includes(BITSWAP_120)) {
423
- return true
424
- }
423
+ peer = toDialable(peer)
425
424
 
426
- throw new UnsupportedProtocolError(`${peer} did not support ${BITSWAP_120}`)
427
- }
428
- })
429
- ])
425
+ options?.onProgress?.(new CustomProgressEvent<PeerId | Multiaddr | Multiaddr[]>('bitswap:dial', peer))
430
426
 
431
- return connection
427
+ return this.libp2p.dial(peer, options)
432
428
  }
433
429
 
434
430
  _updateSentStats (blocks: Map<string, Block>): void {
package/src/session.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { AbstractSession } from '@helia/utils'
2
- import { isPeerId } from '@libp2p/interface'
1
+ import { AbstractSession, isCID } from '@helia/utils'
2
+ import { peerIdFromCID } from '@libp2p/peer-id'
3
+ import { CID } from 'multiformats/cid'
3
4
  import { CustomProgressEvent } from 'progress-events'
4
5
  import type { BitswapProvider, BitswapWantProgressEvents } from './index.ts'
5
6
  import type { Network } from './network.ts'
6
7
  import type { WantList } from './want-list.ts'
7
8
  import type { BlockRetrievalOptions, CreateSessionOptions } from '@helia/interface'
8
- import type { ComponentLogger, Libp2p, PeerId, AbortOptions } from '@libp2p/interface'
9
+ import type { ComponentLogger, Libp2p, AbortOptions } from '@libp2p/interface'
9
10
  import type { Multiaddr } from '@multiformats/multiaddr'
10
- import type { CID } from 'multiformats/cid'
11
11
 
12
12
  export interface BitswapSessionComponents {
13
13
  network: Network
@@ -17,7 +17,7 @@ export interface BitswapSessionComponents {
17
17
  }
18
18
 
19
19
  interface ProviderPeer {
20
- peerId: PeerId
20
+ peerId: CID
21
21
  routing: string
22
22
  toString(): string
23
23
  }
@@ -40,9 +40,11 @@ class BitswapSession extends AbstractSession<ProviderPeer, BitswapWantProgressEv
40
40
  }
41
41
 
42
42
  async queryProvider (cid: CID, provider: ProviderPeer, options: AbortOptions): Promise<Uint8Array> {
43
- this.log('sending WANT-BLOCK for %c to %p', cid, provider)
43
+ const peerId = peerIdFromCID(provider.peerId)
44
44
 
45
- const result = await this.wantList.wantSessionBlock(cid, provider.peerId, options)
45
+ this.log('sending WANT-BLOCK for %c to %p', cid, peerId)
46
+
47
+ const result = await this.wantList.wantSessionBlock(cid, peerId, options)
46
48
 
47
49
  this.log('%p %s %c', provider, result.has ? 'has' : 'does not have', cid)
48
50
 
@@ -68,15 +70,15 @@ class BitswapSession extends AbstractSession<ProviderPeer, BitswapWantProgressEv
68
70
  }
69
71
 
70
72
  toFilterKey (provider: ProviderPeer): Uint8Array | string {
71
- return provider.peerId.toMultihash().bytes
73
+ return provider.peerId.multihash.bytes
72
74
  }
73
75
 
74
76
  equals (providerA: ProviderPeer, providerB: ProviderPeer): boolean {
75
77
  return providerA.peerId.equals(providerB.peerId)
76
78
  }
77
79
 
78
- async convertToProvider (provider: PeerId | Multiaddr | Multiaddr[], routing: string, options?: AbortOptions): Promise<ProviderPeer | undefined> {
79
- if (isPeerId(provider)) {
80
+ async convertToProvider (provider: CID | Multiaddr | Multiaddr[], routing: string, options?: AbortOptions): Promise<ProviderPeer | undefined> {
81
+ if (isCID(provider)) {
80
82
  return {
81
83
  peerId: provider,
82
84
  routing,
@@ -92,7 +94,7 @@ class BitswapSession extends AbstractSession<ProviderPeer, BitswapWantProgressEv
92
94
  const connection = await this.libp2p.dial(provider, options)
93
95
 
94
96
  return {
95
- peerId: connection.remotePeer,
97
+ peerId: connection.remotePeer.toCID(),
96
98
  routing,
97
99
  toString: () => `Bitswap(${connection.remotePeer})`
98
100
  }