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