@helia/utils 0.1.0-1561e4a → 0.1.0-395cd9e
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/dist/index.min.js +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -10
- package/dist/src/index.js.map +1 -1
- package/dist/src/routing.d.ts.map +1 -1
- package/dist/src/routing.js +0 -13
- package/dist/src/routing.js.map +1 -1
- package/dist/src/storage.d.ts +2 -1
- package/dist/src/storage.d.ts.map +1 -1
- package/dist/src/storage.js +14 -1
- package/dist/src/storage.js.map +1 -1
- package/dist/src/utils/networked-storage.d.ts +9 -5
- package/dist/src/utils/networked-storage.d.ts.map +1 -1
- package/dist/src/utils/networked-storage.js +49 -33
- package/dist/src/utils/networked-storage.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +14 -13
- package/src/routing.ts +0 -19
- package/src/storage.ts +19 -3
- package/src/utils/networked-storage.ts +87 -56
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CodeError, start, stop } from '@libp2p/interface'
|
|
1
|
+
import { CodeError, setMaxListeners, start, stop } from '@libp2p/interface'
|
|
2
2
|
import { anySignal } from 'any-signal'
|
|
3
3
|
import { IdentityBlockstore } from 'blockstore-core/identity'
|
|
4
4
|
import { TieredBlockstore } from 'blockstore-core/tiered'
|
|
@@ -6,30 +6,26 @@ import filter from 'it-filter'
|
|
|
6
6
|
import forEach from 'it-foreach'
|
|
7
7
|
import { CustomProgressEvent, type ProgressOptions } from 'progress-events'
|
|
8
8
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
9
|
-
import type { BlockBroker, Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions,
|
|
9
|
+
import type { BlockBroker, Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions, BlockRetrievalOptions } from '@helia/interface/blocks'
|
|
10
10
|
import type { AbortOptions, ComponentLogger, Logger, LoggerOptions, Startable } from '@libp2p/interface'
|
|
11
11
|
import type { Blockstore } from 'interface-blockstore'
|
|
12
12
|
import type { AwaitIterable } from 'interface-store'
|
|
13
13
|
import type { CID } from 'multiformats/cid'
|
|
14
14
|
import type { MultihashHasher } from 'multiformats/hashes/interface'
|
|
15
15
|
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function isBlockRetriever (b: any): b is BlockRetriever {
|
|
21
|
-
return typeof b.retrieve === 'function'
|
|
16
|
+
export interface NetworkedStorageInit {
|
|
17
|
+
root?: CID
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
export interface GetOptions extends AbortOptions {
|
|
21
|
+
progress?(evt: Event): void
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
export interface NetworkedStorageComponents {
|
|
29
25
|
blockstore: Blockstore
|
|
30
26
|
logger: ComponentLogger
|
|
31
|
-
blockBrokers
|
|
32
|
-
hashers
|
|
27
|
+
blockBrokers: BlockBroker[]
|
|
28
|
+
hashers: Record<number, MultihashHasher>
|
|
33
29
|
}
|
|
34
30
|
|
|
35
31
|
/**
|
|
@@ -38,23 +34,23 @@ export interface NetworkedStorageComponents {
|
|
|
38
34
|
*/
|
|
39
35
|
export class NetworkedStorage implements Blocks, Startable {
|
|
40
36
|
private readonly child: Blockstore
|
|
41
|
-
private readonly blockRetrievers: BlockRetriever[]
|
|
42
|
-
private readonly blockAnnouncers: BlockAnnouncer[]
|
|
43
37
|
private readonly hashers: Record<number, MultihashHasher>
|
|
44
38
|
private started: boolean
|
|
45
39
|
private readonly log: Logger
|
|
40
|
+
private readonly logger: ComponentLogger
|
|
41
|
+
private readonly components: NetworkedStorageComponents
|
|
46
42
|
|
|
47
43
|
/**
|
|
48
44
|
* Create a new BlockStorage
|
|
49
45
|
*/
|
|
50
|
-
constructor (components: NetworkedStorageComponents) {
|
|
51
|
-
this.log = components.logger.forComponent(
|
|
46
|
+
constructor (components: NetworkedStorageComponents, init: NetworkedStorageInit = {}) {
|
|
47
|
+
this.log = components.logger.forComponent(`helia:networked-storage${init.root == null ? '' : `:${init.root}`}`)
|
|
48
|
+
this.logger = components.logger
|
|
49
|
+
this.components = components
|
|
52
50
|
this.child = new TieredBlockstore([
|
|
53
51
|
new IdentityBlockstore(),
|
|
54
52
|
components.blockstore
|
|
55
53
|
])
|
|
56
|
-
this.blockRetrievers = (components.blockBrokers ?? []).filter(isBlockRetriever)
|
|
57
|
-
this.blockAnnouncers = (components.blockBrokers ?? []).filter(isBlockAnnouncer)
|
|
58
54
|
this.hashers = components.hashers ?? {}
|
|
59
55
|
this.started = false
|
|
60
56
|
}
|
|
@@ -64,12 +60,12 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
async start (): Promise<void> {
|
|
67
|
-
await start(this.child, ...
|
|
63
|
+
await start(this.child, ...this.components.blockBrokers)
|
|
68
64
|
this.started = true
|
|
69
65
|
}
|
|
70
66
|
|
|
71
67
|
async stop (): Promise<void> {
|
|
72
|
-
await stop(this.child, ...
|
|
68
|
+
await stop(this.child, ...this.components.blockBrokers)
|
|
73
69
|
this.started = false
|
|
74
70
|
}
|
|
75
71
|
|
|
@@ -88,9 +84,9 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
88
84
|
|
|
89
85
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:providers:notify', cid))
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
await Promise.all(
|
|
88
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
89
|
+
)
|
|
94
90
|
|
|
95
91
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:blockstore:put', cid))
|
|
96
92
|
|
|
@@ -111,11 +107,11 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
111
107
|
return !has
|
|
112
108
|
})
|
|
113
109
|
|
|
114
|
-
const notifyEach = forEach(missingBlocks, ({ cid, block }): void => {
|
|
110
|
+
const notifyEach = forEach(missingBlocks, async ({ cid, block }): Promise<void> => {
|
|
115
111
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put-many:providers:notify', cid))
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
await Promise.all(
|
|
113
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
114
|
+
)
|
|
119
115
|
})
|
|
120
116
|
|
|
121
117
|
options.onProgress?.(new CustomProgressEvent('blocks:put-many:blockstore:put-many'))
|
|
@@ -129,7 +125,7 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
129
125
|
if (options.offline !== true && !(await this.child.has(cid))) {
|
|
130
126
|
// we do not have the block locally, get it from a block provider
|
|
131
127
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:get', cid))
|
|
132
|
-
const block = await raceBlockRetrievers(cid, this.
|
|
128
|
+
const block = await raceBlockRetrievers(cid, this.components.blockBrokers, this.hashers[cid.multihash.code], {
|
|
133
129
|
...options,
|
|
134
130
|
log: this.log
|
|
135
131
|
})
|
|
@@ -138,9 +134,9 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
138
134
|
|
|
139
135
|
// notify other block providers of the new block
|
|
140
136
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:notify', cid))
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
137
|
+
await Promise.all(
|
|
138
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
139
|
+
)
|
|
144
140
|
|
|
145
141
|
return block
|
|
146
142
|
}
|
|
@@ -160,7 +156,7 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
160
156
|
if (options.offline !== true && !(await this.child.has(cid))) {
|
|
161
157
|
// we do not have the block locally, get it from a block provider
|
|
162
158
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:get', cid))
|
|
163
|
-
const block = await raceBlockRetrievers(cid, this.
|
|
159
|
+
const block = await raceBlockRetrievers(cid, this.components.blockBrokers, this.hashers[cid.multihash.code], {
|
|
164
160
|
...options,
|
|
165
161
|
log: this.log
|
|
166
162
|
})
|
|
@@ -169,9 +165,9 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
169
165
|
|
|
170
166
|
// notify other block providers of the new block
|
|
171
167
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:notify', cid))
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
await Promise.all(
|
|
169
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
170
|
+
)
|
|
175
171
|
}
|
|
176
172
|
}))
|
|
177
173
|
}
|
|
@@ -205,8 +201,30 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
205
201
|
options.onProgress?.(new CustomProgressEvent('blocks:get-all:blockstore:get-many'))
|
|
206
202
|
yield * this.child.getAll(options)
|
|
207
203
|
}
|
|
204
|
+
|
|
205
|
+
async createSession (root: CID, options?: AbortOptions & ProgressOptions<GetBlockProgressEvents>): Promise<Blocks> {
|
|
206
|
+
const blockBrokers = await Promise.all(this.components.blockBrokers.map(async broker => {
|
|
207
|
+
if (broker.createSession == null) {
|
|
208
|
+
return broker
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return broker.createSession(root, options)
|
|
212
|
+
}))
|
|
213
|
+
|
|
214
|
+
return new NetworkedStorage({
|
|
215
|
+
blockstore: this.child,
|
|
216
|
+
blockBrokers,
|
|
217
|
+
hashers: this.hashers,
|
|
218
|
+
logger: this.logger
|
|
219
|
+
}, {
|
|
220
|
+
root
|
|
221
|
+
})
|
|
222
|
+
}
|
|
208
223
|
}
|
|
209
224
|
|
|
225
|
+
function isRetrievingBlockBroker (broker: BlockBroker): broker is Required<Pick<BlockBroker, 'retrieve'>> {
|
|
226
|
+
return typeof broker.retrieve === 'function'
|
|
227
|
+
}
|
|
210
228
|
export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher): Required<BlockRetrievalOptions>['validateFn'] => {
|
|
211
229
|
if (hasher == null) {
|
|
212
230
|
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')
|
|
@@ -227,40 +245,53 @@ export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher):
|
|
|
227
245
|
* Race block providers cancelling any pending requests once the block has been
|
|
228
246
|
* found.
|
|
229
247
|
*/
|
|
230
|
-
async function raceBlockRetrievers (cid: CID,
|
|
248
|
+
async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hasher: MultihashHasher, options: AbortOptions & LoggerOptions): Promise<Uint8Array> {
|
|
231
249
|
const validateFn = getCidBlockVerifierFunction(cid, hasher)
|
|
232
250
|
|
|
233
251
|
const controller = new AbortController()
|
|
234
252
|
const signal = anySignal([controller.signal, options.signal])
|
|
253
|
+
setMaxListeners(Infinity, controller.signal, signal)
|
|
254
|
+
|
|
255
|
+
const retrievers: Array<Required<Pick<BlockBroker, 'retrieve'>>> = []
|
|
256
|
+
|
|
257
|
+
for (const broker of blockBrokers) {
|
|
258
|
+
if (isRetrievingBlockBroker(broker)) {
|
|
259
|
+
retrievers.push(broker)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
235
262
|
|
|
236
263
|
try {
|
|
237
264
|
return await Promise.any(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
265
|
+
retrievers
|
|
266
|
+
.map(async retriever => {
|
|
267
|
+
try {
|
|
268
|
+
let blocksWereValidated = false
|
|
269
|
+
const block = await retriever.retrieve(cid, {
|
|
270
|
+
...options,
|
|
271
|
+
signal,
|
|
272
|
+
validateFn: async (block: Uint8Array): Promise<void> => {
|
|
273
|
+
await validateFn(block)
|
|
274
|
+
blocksWereValidated = true
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
if (!blocksWereValidated) {
|
|
279
|
+
// the blockBroker either did not throw an error when attempting to validate the block
|
|
280
|
+
// or did not call the validateFn at all. We should validate the block ourselves
|
|
245
281
|
await validateFn(block)
|
|
246
|
-
blocksWereValidated = true
|
|
247
282
|
}
|
|
248
|
-
})
|
|
249
283
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
284
|
+
return block
|
|
285
|
+
} catch (err) {
|
|
286
|
+
options.log.error('could not retrieve verified block for %c', cid, err)
|
|
287
|
+
throw err
|
|
254
288
|
}
|
|
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
|
-
})
|
|
289
|
+
})
|
|
262
290
|
)
|
|
263
291
|
} finally {
|
|
292
|
+
// we have the block from the fastest block retriever, abort any still
|
|
293
|
+
// in-flight retrieve attempts
|
|
294
|
+
controller.abort()
|
|
264
295
|
signal.clear()
|
|
265
296
|
}
|
|
266
297
|
}
|