Skip to content

Commit 5a911c6

Browse files
authored
feat: expose providers option (#834)
Allows pre-seeding transfer sessions with known providers if they are available.
1 parent 7d471a0 commit 5a911c6

7 files changed

Lines changed: 246 additions & 15 deletions

File tree

packages/car/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878

7979
import { Car as CarClass } from './car.js'
8080
import type { CodecLoader } from '@helia/interface'
81-
import type { PutManyBlocksProgressEvents, GetBlockProgressEvents } from '@helia/interface/blocks'
81+
import type { PutManyBlocksProgressEvents, GetBlockProgressEvents, ProviderOptions } from '@helia/interface/blocks'
8282
import type { CarWriter, CarReader } from '@ipld/car'
8383
import type { AbortOptions, ComponentLogger } from '@libp2p/interface'
8484
import type { Filter } from '@libp2p/utils/filters'
@@ -127,7 +127,7 @@ export interface ExportStrategy {
127127
export * from './export-strategies/index.js'
128128
export * from './traversal-strategies/index.js'
129129

130-
export interface ExportCarOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents> {
130+
export interface ExportCarOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents>, ProviderOptions {
131131

132132
/**
133133
* If true, the blockstore will not do any network requests.

packages/dag-cbor/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import * as codec from '@ipld/dag-cbor'
2929
import { CID } from 'multiformats/cid'
3030
import { sha256 } from 'multiformats/hashes/sha2'
31-
import type { GetBlockProgressEvents, PutBlockProgressEvents } from '@helia/interface/blocks'
31+
import type { GetBlockProgressEvents, ProviderOptions, PutBlockProgressEvents } from '@helia/interface/blocks'
3232
import type { AbortOptions } from '@libp2p/interface'
3333
import type { Blockstore } from 'interface-blockstore'
3434
import type { BlockCodec } from 'multiformats/codecs/interface'
@@ -43,7 +43,7 @@ export interface AddOptions extends AbortOptions, ProgressOptions<PutBlockProgre
4343
hasher: MultihashHasher
4444
}
4545

46-
export interface GetOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents> {
46+
export interface GetOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents>, ProviderOptions {
4747
codec: BlockCodec<any, unknown>
4848
}
4949

packages/dag-json/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import * as codec from '@ipld/dag-json'
2929
import { CID } from 'multiformats/cid'
3030
import { sha256 } from 'multiformats/hashes/sha2'
31-
import type { GetBlockProgressEvents, PutBlockProgressEvents } from '@helia/interface/blocks'
31+
import type { GetBlockProgressEvents, ProviderOptions, PutBlockProgressEvents } from '@helia/interface/blocks'
3232
import type { AbortOptions } from '@libp2p/interface'
3333
import type { Blockstore } from 'interface-blockstore'
3434
import type { BlockCodec } from 'multiformats/codecs/interface'
@@ -43,7 +43,7 @@ export interface AddOptions extends AbortOptions, ProgressOptions<PutBlockProgre
4343
hasher: MultihashHasher
4444
}
4545

46-
export interface GetOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents> {
46+
export interface GetOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents>, ProviderOptions {
4747
codec: BlockCodec<any, unknown>
4848
}
4949

packages/interop/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"@libp2p/kad-dht": "^15.0.2",
8080
"@libp2p/keychain": "^5.0.10",
8181
"@libp2p/peer-id": "^5.0.8",
82+
"@libp2p/utils": "^6.7.1",
8283
"@libp2p/websockets": "^9.0.13",
8384
"@multiformats/multiaddr": "^12.4.0",
8485
"@multiformats/sha3": "^3.0.2",
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/* eslint-env mocha */
2+
3+
import { car } from '@helia/car'
4+
import { dagCbor } from '@helia/dag-cbor'
5+
import { dagJson } from '@helia/dag-json'
6+
import { mfs } from '@helia/mfs'
7+
import { strings } from '@helia/strings'
8+
import { unixfs } from '@helia/unixfs'
9+
import { peerIdFromString } from '@libp2p/peer-id'
10+
import { createScalableCuckooFilter } from '@libp2p/utils/filters'
11+
import { expect } from 'aegir/chai'
12+
import toBuffer from 'it-to-buffer'
13+
import { multiaddr } from 'kubo-rpc-client'
14+
import { CID } from 'multiformats/cid'
15+
import { createHeliaNode } from './fixtures/create-helia.js'
16+
import { createKuboNode } from './fixtures/create-kubo.js'
17+
import type { PeerId } from '@libp2p/interface'
18+
import type { Helia } from 'helia'
19+
import type { FileCandidate } from 'ipfs-unixfs-importer'
20+
import type { KuboInfo, KuboNode } from 'ipfsd-ctl'
21+
22+
describe('providers', () => {
23+
let helia: Helia
24+
let kubo: KuboNode
25+
let cid: CID
26+
let kuboInfo: KuboInfo
27+
let input: Uint8Array[]
28+
29+
beforeEach(async () => {
30+
// helia and kubo are not connected together before the test
31+
helia = await createHeliaNode()
32+
kubo = await createKuboNode()
33+
34+
const chunkSize = 1024 * 1024
35+
const size = chunkSize * 10
36+
input = []
37+
38+
const candidate: FileCandidate = {
39+
content: (async function * () {
40+
for (let i = 0; i < size; i += chunkSize) {
41+
const buf = new Uint8Array(chunkSize)
42+
input.push(buf)
43+
44+
yield buf
45+
}
46+
}())
47+
}
48+
49+
const importResult = await kubo.api.add(candidate.content)
50+
cid = CID.parse(importResult.cid.toString())
51+
kuboInfo = await kubo.info()
52+
})
53+
54+
afterEach(async () => {
55+
if (helia != null) {
56+
await helia.stop()
57+
}
58+
59+
if (kubo != null) {
60+
await kubo.stop()
61+
}
62+
})
63+
64+
it('should fail to fetch without using a provider', async () => {
65+
await expect(helia.blockstore.get(cid, {
66+
signal: AbortSignal.timeout(100)
67+
})).to.eventually.be.rejected()
68+
.with.nested.property('errors[0].name', 'AbortError')
69+
})
70+
71+
it('should fetch raw using a provider', async () => {
72+
let sender: PeerId | undefined
73+
74+
const buf = await helia.blockstore.get(cid, {
75+
providers: [
76+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
77+
],
78+
onProgress (evt) {
79+
// @ts-expect-error cannot derive config-based progress event types
80+
if (evt.type === 'bitswap:want-block:received') {
81+
// @ts-expect-error cannot derive config-based progress event types
82+
sender = evt.detail.sender
83+
}
84+
}
85+
})
86+
87+
expect(buf).to.have.lengthOf(1930)
88+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
89+
})
90+
91+
it('should fetch dag-cbor using a provider', async () => {
92+
let sender: PeerId | undefined
93+
const obj = { hello: 'world' }
94+
const cid = await kubo.api.dag.put(obj, {
95+
storeCodec: 'dag-cbor'
96+
})
97+
98+
const d = dagCbor(helia)
99+
100+
await expect(d.get(cid, {
101+
providers: [
102+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
103+
],
104+
onProgress (evt) {
105+
// @ts-expect-error cannot derive config-based progress event types
106+
if (evt.type === 'bitswap:want-block:received') {
107+
// @ts-expect-error cannot derive config-based progress event types
108+
sender = evt.detail.sender
109+
}
110+
}
111+
})).to.eventually.deep.equal(obj)
112+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
113+
})
114+
115+
it('should fetch dag-json using a provider', async () => {
116+
let sender: PeerId | undefined
117+
const obj = { hello: 'world' }
118+
const cid = await kubo.api.dag.put(obj, {
119+
storeCodec: 'dag-json'
120+
})
121+
122+
const d = dagJson(helia)
123+
124+
await expect(d.get(cid, {
125+
providers: [
126+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
127+
],
128+
onProgress (evt) {
129+
// @ts-expect-error cannot derive config-based progress event types
130+
if (evt.type === 'bitswap:want-block:received') {
131+
// @ts-expect-error cannot derive config-based progress event types
132+
sender = evt.detail.sender
133+
}
134+
}
135+
})).to.eventually.deep.equal(obj)
136+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
137+
})
138+
139+
it('should fetch string using a provider', async () => {
140+
let sender: PeerId | undefined
141+
const obj = 'hello world'
142+
const cid = await kubo.api.dag.put(obj, {
143+
storeCodec: 'dag-json'
144+
})
145+
146+
const s = strings(helia)
147+
148+
await expect(s.get(cid, {
149+
providers: [
150+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
151+
],
152+
onProgress (evt) {
153+
// @ts-expect-error cannot derive config-based progress event types
154+
if (evt.type === 'bitswap:want-block:received') {
155+
// @ts-expect-error cannot derive config-based progress event types
156+
sender = evt.detail.sender
157+
}
158+
}
159+
})).to.eventually.equal(JSON.stringify(obj))
160+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
161+
})
162+
163+
it('should fetch via unixfs using a provider', async () => {
164+
let sender: PeerId | undefined
165+
const fs = unixfs(helia)
166+
167+
const bytes = await toBuffer(fs.cat(cid, {
168+
providers: [
169+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
170+
],
171+
onProgress (evt) {
172+
// @ts-expect-error cannot derive config-based progress event types
173+
if (evt.type === 'bitswap:want-block:received') {
174+
// @ts-expect-error cannot derive config-based progress event types
175+
sender = evt.detail.sender
176+
}
177+
}
178+
}))
179+
180+
expect(bytes).to.equalBytes(toBuffer(input))
181+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
182+
})
183+
184+
it('should fetch via mfs using a provider', async () => {
185+
let sender: PeerId | undefined
186+
const fs = mfs(helia)
187+
188+
await fs.cp(cid, '/file.txt', {
189+
providers: [
190+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
191+
],
192+
onProgress (evt) {
193+
// @ts-expect-error cannot derive config-based progress event types
194+
if (evt.type === 'bitswap:want-block:received') {
195+
// @ts-expect-error cannot derive config-based progress event types
196+
sender = evt.detail.sender
197+
}
198+
}
199+
})
200+
201+
const bytes = await toBuffer(fs.cat('/file.txt'))
202+
203+
expect(bytes).to.equalBytes(toBuffer(input))
204+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
205+
})
206+
207+
it('should fetch via car using a provider', async () => {
208+
let sender: PeerId | undefined
209+
const c = car(helia)
210+
211+
expect(await toBuffer(
212+
c.stream(cid, {
213+
providers: [
214+
kuboInfo.multiaddrs.map(ma => multiaddr(ma))
215+
],
216+
blockFilter: createScalableCuckooFilter(10),
217+
onProgress (evt) {
218+
// @ts-expect-error cannot derive config-based progress event types
219+
if (evt.type === 'bitswap:want-block:received') {
220+
// @ts-expect-error cannot derive config-based progress event types
221+
sender = evt.detail.sender
222+
}
223+
}
224+
}))
225+
).to.equalBytes(await toBuffer(
226+
kubo.api.dag.export(cid)
227+
))
228+
expect(sender).to.deep.equal(peerIdFromString(kuboInfo.peerId?.toString() ?? ''))
229+
})
230+
})

packages/strings/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import * as raw from 'multiformats/codecs/raw'
2727
import { sha256 } from 'multiformats/hashes/sha2'
2828
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
2929
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
30-
import type { GetBlockProgressEvents, PutBlockProgressEvents } from '@helia/interface/blocks'
30+
import type { GetBlockProgressEvents, ProviderOptions, PutBlockProgressEvents } from '@helia/interface/blocks'
3131
import type { AbortOptions } from '@libp2p/interface'
3232
import type { Blockstore } from 'interface-blockstore'
3333
import type { BlockCodec } from 'multiformats/codecs/interface'
@@ -43,7 +43,7 @@ export interface AddOptions extends AbortOptions, ProgressOptions<PutBlockProgre
4343
codec: BlockCodec<any, unknown>
4444
}
4545

46-
export interface GetOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents> {
46+
export interface GetOptions extends AbortOptions, ProgressOptions<GetBlockProgressEvents>, ProviderOptions {
4747
codec: BlockCodec<any, unknown>
4848
}
4949

packages/unixfs/src/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
*/
7777

7878
import { UnixFS as UnixFSClass } from './unixfs.js'
79-
import type { GetBlockProgressEvents, PutBlockProgressEvents } from '@helia/interface/blocks'
79+
import type { GetBlockProgressEvents, ProviderOptions, PutBlockProgressEvents } from '@helia/interface/blocks'
8080
import type { AbortOptions } from '@libp2p/interface'
8181
import type { Filter } from '@libp2p/utils/filters'
8282
import type { Blockstore } from 'interface-blockstore'
@@ -112,7 +112,7 @@ export type GetEvents = GetBlockProgressEvents
112112
/**
113113
* Options to pass to the cat command
114114
*/
115-
export interface CatOptions extends AbortOptions, ProgressOptions<GetEvents> {
115+
export interface CatOptions extends AbortOptions, ProgressOptions<GetEvents>, ProviderOptions {
116116
/**
117117
* Start reading the file at this offset
118118
*/
@@ -138,7 +138,7 @@ export interface CatOptions extends AbortOptions, ProgressOptions<GetEvents> {
138138
/**
139139
* Options to pass to the chmod command
140140
*/
141-
export interface ChmodOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents> {
141+
export interface ChmodOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents>, ProviderOptions {
142142
/**
143143
* If the target of the operation is a directory and this is true,
144144
* apply the new mode to all directory contents
@@ -166,7 +166,7 @@ export interface ChmodOptions extends AbortOptions, ProgressOptions<GetEvents |
166166
/**
167167
* Options to pass to the cp command
168168
*/
169-
export interface CpOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents> {
169+
export interface CpOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents>, ProviderOptions {
170170
/**
171171
* If true, allow overwriting existing directory entries (default: false)
172172
*/
@@ -215,7 +215,7 @@ export interface LsOptions extends AbortOptions, ProgressOptions<GetEvents> {
215215
/**
216216
* Options to pass to the mkdir command
217217
*/
218-
export interface MkdirOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents> {
218+
export interface MkdirOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents>, ProviderOptions {
219219
/**
220220
* The CID version to create the new directory with - defaults to the same
221221
* version as the containing directory
@@ -253,7 +253,7 @@ export interface MkdirOptions extends AbortOptions, ProgressOptions<GetEvents |
253253
/**
254254
* Options to pass to the rm command
255255
*/
256-
export interface RmOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents> {
256+
export interface RmOptions extends AbortOptions, ProgressOptions<GetEvents | PutBlockProgressEvents>, ProviderOptions {
257257
/**
258258
* DAGs with a root block larger than this value will be sharded. Blocks
259259
* smaller than this value will be regular UnixFS directories.
@@ -270,7 +270,7 @@ export interface RmOptions extends AbortOptions, ProgressOptions<GetEvents | Put
270270
/**
271271
* Options to pass to the stat command
272272
*/
273-
export interface StatOptions extends AbortOptions, ProgressOptions<GetEvents> {
273+
export interface StatOptions extends AbortOptions, ProgressOptions<GetEvents>, ProviderOptions {
274274
/**
275275
* An optional path to allow getting stats of paths inside directories
276276
*/

0 commit comments

Comments
 (0)