helia 2.0.3 → 2.1.0-299bb09
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.
- package/README.md +17 -8
- package/dist/index.min.js +52 -42
- package/dist/src/block-brokers/bitswap.d.ts +19 -0
- package/dist/src/block-brokers/bitswap.d.ts.map +1 -0
- package/dist/src/block-brokers/bitswap.js +48 -0
- package/dist/src/block-brokers/bitswap.js.map +1 -0
- package/dist/src/block-brokers/index.d.ts +3 -0
- package/dist/src/block-brokers/index.d.ts.map +1 -0
- package/dist/src/block-brokers/index.js +3 -0
- package/dist/src/block-brokers/index.js.map +1 -0
- package/dist/src/block-brokers/trustless-gateway/broker.d.ts +15 -0
- package/dist/src/block-brokers/trustless-gateway/broker.d.ts.map +1 -0
- package/dist/src/block-brokers/trustless-gateway/broker.js +55 -0
- package/dist/src/block-brokers/trustless-gateway/broker.js.map +1 -0
- package/dist/src/block-brokers/trustless-gateway/index.d.ts +13 -0
- package/dist/src/block-brokers/trustless-gateway/index.d.ts.map +1 -0
- package/dist/src/block-brokers/trustless-gateway/index.js +15 -0
- package/dist/src/block-brokers/trustless-gateway/index.js.map +1 -0
- package/dist/src/block-brokers/trustless-gateway/trustless-gateway.d.ts +31 -0
- package/dist/src/block-brokers/trustless-gateway/trustless-gateway.d.ts.map +1 -0
- package/dist/src/block-brokers/trustless-gateway/trustless-gateway.js +114 -0
- package/dist/src/block-brokers/trustless-gateway/trustless-gateway.js.map +1 -0
- package/dist/src/helia.d.ts +3 -2
- package/dist/src/helia.d.ts.map +1 -1
- package/dist/src/helia.js +28 -31
- package/dist/src/helia.js.map +1 -1
- package/dist/src/index.d.ts +25 -10
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -30
- package/dist/src/index.js.map +1 -1
- package/dist/src/pins.js.map +1 -1
- package/dist/src/storage.d.ts +7 -3
- package/dist/src/storage.d.ts.map +1 -1
- package/dist/src/storage.js +14 -0
- package/dist/src/storage.js.map +1 -1
- package/dist/src/utils/dag-walkers.js.map +1 -1
- package/dist/src/utils/datastore-version.js.map +1 -1
- package/dist/src/utils/default-hashers.d.ts +3 -0
- package/dist/src/utils/default-hashers.d.ts.map +1 -0
- package/dist/src/utils/default-hashers.js +11 -0
- package/dist/src/utils/default-hashers.js.map +1 -0
- package/dist/src/utils/libp2p-defaults.browser.d.ts +11 -7
- package/dist/src/utils/libp2p-defaults.browser.d.ts.map +1 -1
- package/dist/src/utils/libp2p-defaults.browser.js +20 -15
- package/dist/src/utils/libp2p-defaults.browser.js.map +1 -1
- package/dist/src/utils/libp2p-defaults.d.ts +13 -9
- package/dist/src/utils/libp2p-defaults.d.ts.map +1 -1
- package/dist/src/utils/libp2p-defaults.js +23 -17
- package/dist/src/utils/libp2p-defaults.js.map +1 -1
- package/dist/src/utils/libp2p.d.ts +13 -5
- package/dist/src/utils/libp2p.d.ts.map +1 -1
- package/dist/src/utils/libp2p.js +21 -4
- package/dist/src/utils/libp2p.js.map +1 -1
- package/dist/src/utils/networked-storage.d.ts +22 -10
- package/dist/src/utils/networked-storage.d.ts.map +1 -1
- package/dist/src/utils/networked-storage.js +115 -16
- package/dist/src/utils/networked-storage.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/package.json +60 -23
- package/src/block-brokers/bitswap.ts +77 -0
- package/src/block-brokers/index.ts +2 -0
- package/src/block-brokers/trustless-gateway/broker.ts +65 -0
- package/src/block-brokers/trustless-gateway/index.ts +33 -0
- package/src/block-brokers/trustless-gateway/trustless-gateway.ts +126 -0
- package/src/helia.ts +31 -39
- package/src/index.ts +35 -36
- package/src/storage.ts +20 -3
- package/src/utils/default-hashers.ts +12 -0
- package/src/utils/libp2p-defaults.browser.ts +27 -19
- package/src/utils/libp2p-defaults.ts +31 -22
- package/src/utils/libp2p.ts +40 -8
- package/src/utils/networked-storage.ts +145 -25
- package/src/version.ts +1 -1
- package/dist/typedoc-urls.json +0 -8
package/src/utils/libp2p.ts
CHANGED
|
@@ -1,21 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { keychain } from '@libp2p/keychain'
|
|
2
|
+
import { defaultLogger } from '@libp2p/logger'
|
|
3
|
+
import { Key } from 'interface-datastore'
|
|
4
|
+
import { createLibp2p as create } from 'libp2p'
|
|
5
|
+
import { libp2pDefaults } from './libp2p-defaults.js'
|
|
6
|
+
import type { DefaultLibp2pServices } from './libp2p-defaults.js'
|
|
7
|
+
import type { ComponentLogger, Libp2p, PeerId } from '@libp2p/interface'
|
|
8
|
+
import type { KeychainInit } from '@libp2p/keychain'
|
|
4
9
|
import type { Datastore } from 'interface-datastore'
|
|
10
|
+
import type { Libp2pOptions } from 'libp2p'
|
|
5
11
|
|
|
6
|
-
export interface CreateLibp2pOptions {
|
|
12
|
+
export interface CreateLibp2pOptions<T extends Record<string, unknown>> {
|
|
7
13
|
datastore: Datastore
|
|
14
|
+
libp2p?: Libp2pOptions<T>
|
|
15
|
+
logger?: ComponentLogger
|
|
16
|
+
keychain?: KeychainInit
|
|
8
17
|
start?: boolean
|
|
9
18
|
}
|
|
10
19
|
|
|
11
|
-
export
|
|
12
|
-
|
|
20
|
+
export interface Libp2pDefaultsOptions {
|
|
21
|
+
peerId?: PeerId
|
|
22
|
+
keychain?: KeychainInit
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function createLibp2p <T extends Record<string, unknown> = DefaultLibp2pServices> (options: CreateLibp2pOptions<T>): Promise<Libp2p<T>> {
|
|
26
|
+
let peerId = options.libp2p?.peerId
|
|
27
|
+
const logger = options.logger ?? defaultLogger()
|
|
28
|
+
|
|
29
|
+
// if no peer id was passed, try to load it from the keychain
|
|
30
|
+
if (peerId == null) {
|
|
31
|
+
const chain = keychain(options.keychain)({
|
|
32
|
+
datastore: options.datastore,
|
|
33
|
+
logger
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const selfKey = new Key('/pkcs8/self')
|
|
37
|
+
|
|
38
|
+
if (await options.datastore.has(selfKey)) {
|
|
39
|
+
// load the peer id from the keychain
|
|
40
|
+
peerId = await chain.exportPeerId('self')
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const defaults = libp2pDefaults(options)
|
|
13
45
|
options = options ?? {}
|
|
14
46
|
|
|
47
|
+
// @ts-expect-error derived ServiceMap is not compatible with ServiceFactoryMap
|
|
15
48
|
return create({
|
|
16
|
-
datastore,
|
|
17
49
|
...defaults,
|
|
18
|
-
...options,
|
|
50
|
+
...options.libp2p,
|
|
19
51
|
start: false
|
|
20
52
|
})
|
|
21
53
|
}
|
|
@@ -1,36 +1,74 @@
|
|
|
1
|
+
import { CodeError, start, stop } from '@libp2p/interface'
|
|
2
|
+
import { anySignal } from 'any-signal'
|
|
1
3
|
import filter from 'it-filter'
|
|
2
4
|
import forEach from 'it-foreach'
|
|
3
5
|
import { CustomProgressEvent, type ProgressOptions } from 'progress-events'
|
|
4
|
-
import
|
|
5
|
-
import type {
|
|
6
|
+
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
7
|
+
import type { BlockBroker, Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions, BlockRetriever, BlockAnnouncer, BlockRetrievalOptions } from '@helia/interface/blocks'
|
|
8
|
+
import type { AbortOptions, ComponentLogger, Logger, LoggerOptions, Startable } from '@libp2p/interface'
|
|
6
9
|
import type { Blockstore } from 'interface-blockstore'
|
|
7
10
|
import type { AwaitIterable } from 'interface-store'
|
|
8
|
-
import type { Bitswap } from 'ipfs-bitswap'
|
|
9
11
|
import type { CID } from 'multiformats/cid'
|
|
12
|
+
import type { MultihashHasher } from 'multiformats/hashes/interface'
|
|
10
13
|
|
|
11
|
-
export interface
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
export interface NetworkedStorageStorageInit {
|
|
15
|
+
blockBrokers?: BlockBroker[]
|
|
16
|
+
hashers?: MultihashHasher[]
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export interface GetOptions extends AbortOptions {
|
|
17
|
-
progress
|
|
20
|
+
progress?(evt: Event): void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isBlockRetriever (b: any): b is BlockRetriever {
|
|
24
|
+
return typeof b.retrieve === 'function'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isBlockAnnouncer (b: any): b is BlockAnnouncer {
|
|
28
|
+
return typeof b.announce === 'function'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface NetworkedStorageComponents {
|
|
32
|
+
blockstore: Blockstore
|
|
33
|
+
logger: ComponentLogger
|
|
18
34
|
}
|
|
19
35
|
|
|
20
36
|
/**
|
|
21
37
|
* Networked storage wraps a regular blockstore - when getting blocks if the
|
|
22
38
|
* blocks are not present Bitswap will be used to fetch them from network peers.
|
|
23
39
|
*/
|
|
24
|
-
export class NetworkedStorage implements Blocks {
|
|
40
|
+
export class NetworkedStorage implements Blocks, Startable {
|
|
25
41
|
private readonly child: Blockstore
|
|
26
|
-
private readonly
|
|
42
|
+
private readonly blockRetrievers: BlockRetriever[]
|
|
43
|
+
private readonly blockAnnouncers: BlockAnnouncer[]
|
|
44
|
+
private readonly hashers: MultihashHasher[]
|
|
45
|
+
private started: boolean
|
|
46
|
+
private readonly log: Logger
|
|
27
47
|
|
|
28
48
|
/**
|
|
29
49
|
* Create a new BlockStorage
|
|
30
50
|
*/
|
|
31
|
-
constructor (
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
51
|
+
constructor (components: NetworkedStorageComponents, init: NetworkedStorageStorageInit) {
|
|
52
|
+
this.log = components.logger.forComponent('helia:networked-storage')
|
|
53
|
+
this.child = components.blockstore
|
|
54
|
+
this.blockRetrievers = (init.blockBrokers ?? []).filter(isBlockRetriever)
|
|
55
|
+
this.blockAnnouncers = (init.blockBrokers ?? []).filter(isBlockAnnouncer)
|
|
56
|
+
this.hashers = init.hashers ?? []
|
|
57
|
+
this.started = false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
isStarted (): boolean {
|
|
61
|
+
return this.started
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async start (): Promise<void> {
|
|
65
|
+
await start(this.child, ...new Set([...this.blockRetrievers, ...this.blockAnnouncers]))
|
|
66
|
+
this.started = true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async stop (): Promise<void> {
|
|
70
|
+
await stop(this.child, ...new Set([...this.blockRetrievers, ...this.blockAnnouncers]))
|
|
71
|
+
this.started = false
|
|
34
72
|
}
|
|
35
73
|
|
|
36
74
|
unwrap (): Blockstore {
|
|
@@ -46,10 +84,11 @@ export class NetworkedStorage implements Blocks {
|
|
|
46
84
|
return cid
|
|
47
85
|
}
|
|
48
86
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
87
|
+
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:providers:notify', cid))
|
|
88
|
+
|
|
89
|
+
this.blockAnnouncers.forEach(provider => {
|
|
90
|
+
provider.announce(cid, block, options)
|
|
91
|
+
})
|
|
53
92
|
|
|
54
93
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:blockstore:put', cid))
|
|
55
94
|
|
|
@@ -71,8 +110,10 @@ export class NetworkedStorage implements Blocks {
|
|
|
71
110
|
})
|
|
72
111
|
|
|
73
112
|
const notifyEach = forEach(missingBlocks, ({ cid, block }): void => {
|
|
74
|
-
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put-many:
|
|
75
|
-
this.
|
|
113
|
+
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put-many:providers:notify', cid))
|
|
114
|
+
this.blockAnnouncers.forEach(provider => {
|
|
115
|
+
provider.announce(cid, block, options)
|
|
116
|
+
})
|
|
76
117
|
})
|
|
77
118
|
|
|
78
119
|
options.onProgress?.(new CustomProgressEvent('blocks:put-many:blockstore:put-many'))
|
|
@@ -83,13 +124,22 @@ export class NetworkedStorage implements Blocks {
|
|
|
83
124
|
* Get a block by cid
|
|
84
125
|
*/
|
|
85
126
|
async get (cid: CID, options: GetOfflineOptions & AbortOptions & ProgressOptions<GetBlockProgressEvents> = {}): Promise<Uint8Array> {
|
|
86
|
-
if (options.offline !== true &&
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
127
|
+
if (options.offline !== true && !(await this.child.has(cid))) {
|
|
128
|
+
// we do not have the block locally, get it from a block provider
|
|
129
|
+
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:get', cid))
|
|
130
|
+
const block = await raceBlockRetrievers(cid, this.blockRetrievers, this.hashers, {
|
|
131
|
+
...options,
|
|
132
|
+
log: this.log
|
|
133
|
+
})
|
|
90
134
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:blockstore:put', cid))
|
|
91
135
|
await this.child.put(cid, block, options)
|
|
92
136
|
|
|
137
|
+
// notify other block providers of the new block
|
|
138
|
+
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:notify', cid))
|
|
139
|
+
this.blockAnnouncers.forEach(provider => {
|
|
140
|
+
provider.announce(cid, block, options)
|
|
141
|
+
})
|
|
142
|
+
|
|
93
143
|
return block
|
|
94
144
|
}
|
|
95
145
|
|
|
@@ -105,11 +155,21 @@ export class NetworkedStorage implements Blocks {
|
|
|
105
155
|
options.onProgress?.(new CustomProgressEvent('blocks:get-many:blockstore:get-many'))
|
|
106
156
|
|
|
107
157
|
yield * this.child.getMany(forEach(cids, async (cid): Promise<void> => {
|
|
108
|
-
if (options.offline !== true &&
|
|
109
|
-
|
|
110
|
-
|
|
158
|
+
if (options.offline !== true && !(await this.child.has(cid))) {
|
|
159
|
+
// we do not have the block locally, get it from a block provider
|
|
160
|
+
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:get', cid))
|
|
161
|
+
const block = await raceBlockRetrievers(cid, this.blockRetrievers, this.hashers, {
|
|
162
|
+
...options,
|
|
163
|
+
log: this.log
|
|
164
|
+
})
|
|
111
165
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:blockstore:put', cid))
|
|
112
166
|
await this.child.put(cid, block, options)
|
|
167
|
+
|
|
168
|
+
// notify other block providers of the new block
|
|
169
|
+
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:notify', cid))
|
|
170
|
+
this.blockAnnouncers.forEach(provider => {
|
|
171
|
+
provider.announce(cid, block, options)
|
|
172
|
+
})
|
|
113
173
|
}
|
|
114
174
|
}))
|
|
115
175
|
}
|
|
@@ -144,3 +204,63 @@ export class NetworkedStorage implements Blocks {
|
|
|
144
204
|
yield * this.child.getAll(options)
|
|
145
205
|
}
|
|
146
206
|
}
|
|
207
|
+
|
|
208
|
+
export const getCidBlockVerifierFunction = (cid: CID, hashers: MultihashHasher[]): Required<BlockRetrievalOptions>['validateFn'] => {
|
|
209
|
+
const hasher = hashers.find(hasher => hasher.code === cid.multihash.code)
|
|
210
|
+
|
|
211
|
+
if (hasher == null) {
|
|
212
|
+
throw new CodeError(`No hasher configured for multihash code 0x${cid.multihash.code.toString(16)}, please configure one. You can look up which hash this is at https://github.com/multiformats/multicodec/blob/master/table.csv`, 'ERR_UNKNOWN_HASH_ALG')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return async (block: Uint8Array): Promise<void> => {
|
|
216
|
+
// verify block
|
|
217
|
+
const hash = await hasher.digest(block)
|
|
218
|
+
|
|
219
|
+
if (!uint8ArrayEquals(hash.digest, cid.multihash.digest)) {
|
|
220
|
+
// if a hash mismatch occurs for a TrustlessGatewayBlockBroker, we should try another gateway
|
|
221
|
+
throw new CodeError('Hash of downloaded block did not match multihash from passed CID', 'ERR_HASH_MISMATCH')
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Race block providers cancelling any pending requests once the block has been
|
|
228
|
+
* found.
|
|
229
|
+
*/
|
|
230
|
+
async function raceBlockRetrievers (cid: CID, providers: BlockRetriever[], hashers: MultihashHasher[], options: AbortOptions & LoggerOptions): Promise<Uint8Array> {
|
|
231
|
+
const validateFn = getCidBlockVerifierFunction(cid, hashers)
|
|
232
|
+
|
|
233
|
+
const controller = new AbortController()
|
|
234
|
+
const signal = anySignal([controller.signal, options.signal])
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
return await Promise.any(
|
|
238
|
+
providers.map(async provider => {
|
|
239
|
+
try {
|
|
240
|
+
let blocksWereValidated = false
|
|
241
|
+
const block = await provider.retrieve(cid, {
|
|
242
|
+
...options,
|
|
243
|
+
signal,
|
|
244
|
+
validateFn: async (block: Uint8Array): Promise<void> => {
|
|
245
|
+
await validateFn(block)
|
|
246
|
+
blocksWereValidated = true
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
if (!blocksWereValidated) {
|
|
251
|
+
// the blockBroker either did not throw an error when attempting to validate the block
|
|
252
|
+
// or did not call the validateFn at all. We should validate the block ourselves
|
|
253
|
+
await validateFn(block)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return block
|
|
257
|
+
} catch (err) {
|
|
258
|
+
options.log.error('could not retrieve verified block for %c', cid, err)
|
|
259
|
+
throw err
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
)
|
|
263
|
+
} finally {
|
|
264
|
+
signal.clear()
|
|
265
|
+
}
|
|
266
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '2.0
|
|
1
|
+
export const version = '2.1.0-299bb09'
|
|
2
2
|
export const name = 'helia'
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"DAGWalker": "https://ipfs.github.io/helia/interfaces/helia.DAGWalker.html",
|
|
3
|
-
".:DAGWalker": "https://ipfs.github.io/helia/interfaces/helia.DAGWalker.html",
|
|
4
|
-
"HeliaInit": "https://ipfs.github.io/helia/interfaces/helia.HeliaInit.html",
|
|
5
|
-
".:HeliaInit": "https://ipfs.github.io/helia/interfaces/helia.HeliaInit.html",
|
|
6
|
-
"createHelia": "https://ipfs.github.io/helia/functions/helia.createHelia.html",
|
|
7
|
-
".:createHelia": "https://ipfs.github.io/helia/functions/helia.createHelia.html"
|
|
8
|
-
}
|