@helia/utils 0.1.0-ecf5394 → 0.2.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.
- package/dist/index.min.js +6 -1
- package/dist/src/abstract-session.d.ts +53 -0
- package/dist/src/abstract-session.d.ts.map +1 -0
- package/dist/src/abstract-session.js +205 -0
- package/dist/src/abstract-session.js.map +1 -0
- package/dist/src/bloom-filter.d.ts +33 -0
- package/dist/src/bloom-filter.d.ts.map +1 -0
- package/dist/src/bloom-filter.js +113 -0
- package/dist/src/bloom-filter.js.map +1 -0
- package/dist/src/index.d.ts +19 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +15 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/routing.d.ts +4 -1
- package/dist/src/routing.d.ts.map +1 -1
- package/dist/src/routing.js +46 -14
- package/dist/src/routing.js.map +1 -1
- package/dist/src/storage.d.ts +3 -2
- package/dist/src/storage.d.ts.map +1 -1
- package/dist/src/storage.js +12 -0
- package/dist/src/storage.js.map +1 -1
- package/dist/src/utils/networked-storage.d.ts +30 -21
- package/dist/src/utils/networked-storage.d.ts.map +1 -1
- package/dist/src/utils/networked-storage.js +219 -59
- package/dist/src/utils/networked-storage.js.map +1 -1
- package/dist/typedoc-urls.json +9 -0
- package/package.json +12 -5
- package/src/abstract-session.ts +287 -0
- package/src/bloom-filter.ts +141 -0
- package/src/index.ts +37 -14
- package/src/routing.ts +52 -17
- package/src/storage.ts +16 -3
- package/src/utils/networked-storage.ts +285 -91
package/src/storage.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { start, stop } from '@libp2p/interface'
|
|
2
2
|
import createMortice from 'mortice'
|
|
3
|
-
import type { Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions } from '@helia/interface/blocks'
|
|
3
|
+
import type { Blocks, Pair, DeleteManyBlocksProgressEvents, DeleteBlockProgressEvents, GetBlockProgressEvents, GetManyBlocksProgressEvents, PutManyBlocksProgressEvents, PutBlockProgressEvents, GetAllBlocksProgressEvents, GetOfflineOptions, SessionBlockstore } from '@helia/interface/blocks'
|
|
4
4
|
import type { Pins } from '@helia/interface/pins'
|
|
5
5
|
import type { AbortOptions, Startable } from '@libp2p/interface'
|
|
6
6
|
import type { Blockstore } from 'interface-blockstore'
|
|
@@ -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({
|
|
@@ -62,6 +62,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
62
62
|
* Put a block to the underlying datastore
|
|
63
63
|
*/
|
|
64
64
|
async put (cid: CID, block: Uint8Array, options: AbortOptions & ProgressOptions<PutBlockProgressEvents> = {}): Promise<CID> {
|
|
65
|
+
options?.signal?.throwIfAborted()
|
|
65
66
|
const releaseLock = await this.lock.readLock()
|
|
66
67
|
|
|
67
68
|
try {
|
|
@@ -75,6 +76,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
75
76
|
* Put a multiple blocks to the underlying datastore
|
|
76
77
|
*/
|
|
77
78
|
async * putMany (blocks: AwaitIterable<{ cid: CID, block: Uint8Array }>, options: AbortOptions & ProgressOptions<PutManyBlocksProgressEvents> = {}): AsyncIterable<CID> {
|
|
79
|
+
options?.signal?.throwIfAborted()
|
|
78
80
|
const releaseLock = await this.lock.readLock()
|
|
79
81
|
|
|
80
82
|
try {
|
|
@@ -88,6 +90,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
88
90
|
* Get a block by cid
|
|
89
91
|
*/
|
|
90
92
|
async get (cid: CID, options: GetOfflineOptions & AbortOptions & ProgressOptions<GetBlockProgressEvents> = {}): Promise<Uint8Array> {
|
|
93
|
+
options?.signal?.throwIfAborted()
|
|
91
94
|
const releaseLock = await this.lock.readLock()
|
|
92
95
|
|
|
93
96
|
try {
|
|
@@ -101,6 +104,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
101
104
|
* Get multiple blocks back from an (async) iterable of cids
|
|
102
105
|
*/
|
|
103
106
|
async * getMany (cids: AwaitIterable<CID>, options: GetOfflineOptions & AbortOptions & ProgressOptions<GetManyBlocksProgressEvents> = {}): AsyncIterable<Pair> {
|
|
107
|
+
options?.signal?.throwIfAborted()
|
|
104
108
|
const releaseLock = await this.lock.readLock()
|
|
105
109
|
|
|
106
110
|
try {
|
|
@@ -114,6 +118,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
114
118
|
* Delete a block from the blockstore
|
|
115
119
|
*/
|
|
116
120
|
async delete (cid: CID, options: AbortOptions & ProgressOptions<DeleteBlockProgressEvents> = {}): Promise<void> {
|
|
121
|
+
options?.signal?.throwIfAborted()
|
|
117
122
|
const releaseLock = await this.lock.writeLock()
|
|
118
123
|
|
|
119
124
|
try {
|
|
@@ -131,6 +136,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
131
136
|
* Delete multiple blocks from the blockstore
|
|
132
137
|
*/
|
|
133
138
|
async * deleteMany (cids: AwaitIterable<CID>, options: AbortOptions & ProgressOptions<DeleteManyBlocksProgressEvents> = {}): AsyncIterable<CID> {
|
|
139
|
+
options?.signal?.throwIfAborted()
|
|
134
140
|
const releaseLock = await this.lock.writeLock()
|
|
135
141
|
|
|
136
142
|
try {
|
|
@@ -151,6 +157,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
async has (cid: CID, options: AbortOptions = {}): Promise<boolean> {
|
|
160
|
+
options?.signal?.throwIfAborted()
|
|
154
161
|
const releaseLock = await this.lock.readLock()
|
|
155
162
|
|
|
156
163
|
try {
|
|
@@ -161,6 +168,7 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
async * getAll (options: AbortOptions & ProgressOptions<GetAllBlocksProgressEvents> = {}): AsyncIterable<Pair> {
|
|
171
|
+
options?.signal?.throwIfAborted()
|
|
164
172
|
const releaseLock = await this.lock.readLock()
|
|
165
173
|
|
|
166
174
|
try {
|
|
@@ -169,4 +177,9 @@ export class BlockStorage implements Blocks, Startable {
|
|
|
169
177
|
releaseLock()
|
|
170
178
|
}
|
|
171
179
|
}
|
|
180
|
+
|
|
181
|
+
createSession (root: CID, options?: AbortOptions): SessionBlockstore {
|
|
182
|
+
options?.signal?.throwIfAborted()
|
|
183
|
+
return this.child.createSession(root, options)
|
|
184
|
+
}
|
|
172
185
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
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, CreateSessionOptions, SessionBlockstore } 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'
|
|
@@ -17,80 +16,45 @@ export interface GetOptions extends AbortOptions {
|
|
|
17
16
|
progress?(evt: Event): void
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
return typeof b.retrieve === 'function'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function isBlockAnnouncer (b: any): b is BlockAnnouncer {
|
|
25
|
-
return typeof b.announce === 'function'
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface NetworkedStorageComponents {
|
|
19
|
+
export interface StorageComponents {
|
|
29
20
|
blockstore: Blockstore
|
|
30
21
|
logger: ComponentLogger
|
|
31
|
-
blockBrokers
|
|
32
|
-
hashers
|
|
22
|
+
blockBrokers: BlockBroker[]
|
|
23
|
+
hashers: Record<number, MultihashHasher>
|
|
33
24
|
}
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
private readonly blockRetrievers: BlockRetriever[]
|
|
42
|
-
private readonly blockAnnouncers: BlockAnnouncer[]
|
|
43
|
-
private readonly hashers: Record<number, MultihashHasher>
|
|
44
|
-
private started: boolean
|
|
45
|
-
private readonly log: Logger
|
|
26
|
+
class Storage implements Blockstore {
|
|
27
|
+
protected readonly child: Blockstore
|
|
28
|
+
protected readonly hashers: Record<number, MultihashHasher>
|
|
29
|
+
protected log: Logger
|
|
30
|
+
protected readonly logger: ComponentLogger
|
|
31
|
+
protected readonly components: StorageComponents
|
|
46
32
|
|
|
47
33
|
/**
|
|
48
34
|
* Create a new BlockStorage
|
|
49
35
|
*/
|
|
50
|
-
constructor (components:
|
|
36
|
+
constructor (components: StorageComponents) {
|
|
51
37
|
this.log = components.logger.forComponent('helia:networked-storage')
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
])
|
|
56
|
-
this.blockRetrievers = (components.blockBrokers ?? []).filter(isBlockRetriever)
|
|
57
|
-
this.blockAnnouncers = (components.blockBrokers ?? []).filter(isBlockAnnouncer)
|
|
38
|
+
this.logger = components.logger
|
|
39
|
+
this.components = components
|
|
40
|
+
this.child = new IdentityBlockstore(components.blockstore)
|
|
58
41
|
this.hashers = components.hashers ?? {}
|
|
59
|
-
this.started = false
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
isStarted (): boolean {
|
|
63
|
-
return this.started
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async start (): Promise<void> {
|
|
67
|
-
await start(this.child, ...new Set([...this.blockRetrievers, ...this.blockAnnouncers]))
|
|
68
|
-
this.started = true
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async stop (): Promise<void> {
|
|
72
|
-
await stop(this.child, ...new Set([...this.blockRetrievers, ...this.blockAnnouncers]))
|
|
73
|
-
this.started = false
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
unwrap (): Blockstore {
|
|
77
|
-
return this.child
|
|
78
42
|
}
|
|
79
43
|
|
|
80
44
|
/**
|
|
81
45
|
* Put a block to the underlying datastore
|
|
82
46
|
*/
|
|
83
47
|
async put (cid: CID, block: Uint8Array, options: AbortOptions & ProgressOptions<PutBlockProgressEvents> = {}): Promise<CID> {
|
|
84
|
-
if (await this.child.has(cid)) {
|
|
48
|
+
if (await this.child.has(cid, options)) {
|
|
85
49
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:duplicate', cid))
|
|
86
50
|
return cid
|
|
87
51
|
}
|
|
88
52
|
|
|
89
53
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:providers:notify', cid))
|
|
90
54
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
55
|
+
await Promise.all(
|
|
56
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
57
|
+
)
|
|
94
58
|
|
|
95
59
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put:blockstore:put', cid))
|
|
96
60
|
|
|
@@ -102,7 +66,7 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
102
66
|
*/
|
|
103
67
|
async * putMany (blocks: AwaitIterable<{ cid: CID, block: Uint8Array }>, options: AbortOptions & ProgressOptions<PutManyBlocksProgressEvents> = {}): AsyncIterable<CID> {
|
|
104
68
|
const missingBlocks = filter(blocks, async ({ cid }): Promise<boolean> => {
|
|
105
|
-
const has = await this.child.has(cid)
|
|
69
|
+
const has = await this.child.has(cid, options)
|
|
106
70
|
|
|
107
71
|
if (has) {
|
|
108
72
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put-many:duplicate', cid))
|
|
@@ -111,11 +75,11 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
111
75
|
return !has
|
|
112
76
|
})
|
|
113
77
|
|
|
114
|
-
const notifyEach = forEach(missingBlocks, ({ cid, block }): void => {
|
|
78
|
+
const notifyEach = forEach(missingBlocks, async ({ cid, block }): Promise<void> => {
|
|
115
79
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:put-many:providers:notify', cid))
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
80
|
+
await Promise.all(
|
|
81
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
82
|
+
)
|
|
119
83
|
})
|
|
120
84
|
|
|
121
85
|
options.onProgress?.(new CustomProgressEvent('blocks:put-many:blockstore:put-many'))
|
|
@@ -126,10 +90,10 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
126
90
|
* Get a block by cid
|
|
127
91
|
*/
|
|
128
92
|
async get (cid: CID, options: GetOfflineOptions & AbortOptions & ProgressOptions<GetBlockProgressEvents> = {}): Promise<Uint8Array> {
|
|
129
|
-
if (options.offline !== true && !(await this.child.has(cid))) {
|
|
93
|
+
if (options.offline !== true && !(await this.child.has(cid, options))) {
|
|
130
94
|
// we do not have the block locally, get it from a block provider
|
|
131
95
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:get', cid))
|
|
132
|
-
const block = await raceBlockRetrievers(cid, this.
|
|
96
|
+
const block = await raceBlockRetrievers(cid, this.components.blockBrokers, this.hashers[cid.multihash.code], {
|
|
133
97
|
...options,
|
|
134
98
|
log: this.log
|
|
135
99
|
})
|
|
@@ -138,9 +102,9 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
138
102
|
|
|
139
103
|
// notify other block providers of the new block
|
|
140
104
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get:providers:notify', cid))
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
105
|
+
await Promise.all(
|
|
106
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
107
|
+
)
|
|
144
108
|
|
|
145
109
|
return block
|
|
146
110
|
}
|
|
@@ -157,10 +121,10 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
157
121
|
options.onProgress?.(new CustomProgressEvent('blocks:get-many:blockstore:get-many'))
|
|
158
122
|
|
|
159
123
|
yield * this.child.getMany(forEach(cids, async (cid): Promise<void> => {
|
|
160
|
-
if (options.offline !== true && !(await this.child.has(cid))) {
|
|
124
|
+
if (options.offline !== true && !(await this.child.has(cid, options))) {
|
|
161
125
|
// we do not have the block locally, get it from a block provider
|
|
162
126
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:get', cid))
|
|
163
|
-
const block = await raceBlockRetrievers(cid, this.
|
|
127
|
+
const block = await raceBlockRetrievers(cid, this.components.blockBrokers, this.hashers[cid.multihash.code], {
|
|
164
128
|
...options,
|
|
165
129
|
log: this.log
|
|
166
130
|
})
|
|
@@ -169,9 +133,9 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
169
133
|
|
|
170
134
|
// notify other block providers of the new block
|
|
171
135
|
options.onProgress?.(new CustomProgressEvent<CID>('blocks:get-many:providers:notify', cid))
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
136
|
+
await Promise.all(
|
|
137
|
+
this.components.blockBrokers.map(async broker => broker.announce?.(cid, block, options))
|
|
138
|
+
)
|
|
175
139
|
}
|
|
176
140
|
}))
|
|
177
141
|
}
|
|
@@ -207,6 +171,223 @@ export class NetworkedStorage implements Blocks, Startable {
|
|
|
207
171
|
}
|
|
208
172
|
}
|
|
209
173
|
|
|
174
|
+
export type NetworkedStorageComponents = StorageComponents
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Networked storage wraps a regular blockstore - when getting blocks if the
|
|
178
|
+
* blocks are not present, the configured BlockBrokers will be used to fetch them.
|
|
179
|
+
*/
|
|
180
|
+
export class NetworkedStorage extends Storage implements Blocks, Startable {
|
|
181
|
+
private started: boolean
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create a new BlockStorage
|
|
185
|
+
*/
|
|
186
|
+
constructor (components: NetworkedStorageComponents) {
|
|
187
|
+
super(components)
|
|
188
|
+
|
|
189
|
+
this.started = false
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
isStarted (): boolean {
|
|
193
|
+
return this.started
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async start (): Promise<void> {
|
|
197
|
+
await start(this.child, ...this.components.blockBrokers)
|
|
198
|
+
this.started = true
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async stop (): Promise<void> {
|
|
202
|
+
await stop(this.child, ...this.components.blockBrokers)
|
|
203
|
+
this.started = false
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
unwrap (): Blockstore {
|
|
207
|
+
return this.child
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
createSession (root: CID, options?: CreateSessionOptions): SessionBlockstore {
|
|
211
|
+
const blockBrokers = this.components.blockBrokers.map(broker => {
|
|
212
|
+
if (broker.createSession == null) {
|
|
213
|
+
return broker
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return broker.createSession(options)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
return new SessionStorage({
|
|
220
|
+
blockstore: this.child,
|
|
221
|
+
blockBrokers,
|
|
222
|
+
hashers: this.hashers,
|
|
223
|
+
logger: this.logger
|
|
224
|
+
}, {
|
|
225
|
+
root
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
interface SessionStorageInit {
|
|
231
|
+
root: CID
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Storage subclass that can cancel any ongoing operation at any point.
|
|
236
|
+
*/
|
|
237
|
+
class SessionStorage extends Storage implements SessionBlockstore {
|
|
238
|
+
private readonly closeController: AbortController
|
|
239
|
+
|
|
240
|
+
constructor (components: StorageComponents, init: SessionStorageInit) {
|
|
241
|
+
super(components)
|
|
242
|
+
|
|
243
|
+
// because brokers are allowed to continue searching for providers after the
|
|
244
|
+
// session has been created, we need a way to tell them that the user has
|
|
245
|
+
// finished using the session any in-flight requests should be cancelled
|
|
246
|
+
this.closeController = new AbortController()
|
|
247
|
+
setMaxListeners(Infinity, this.closeController.signal)
|
|
248
|
+
|
|
249
|
+
this.log = components.logger.forComponent(`helia:session-storage${init.root}`)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
close (): void {
|
|
253
|
+
this.closeController.abort()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Put a block to the underlying datastore
|
|
258
|
+
*/
|
|
259
|
+
async put (cid: CID, block: Uint8Array, options: AbortOptions & ProgressOptions<PutBlockProgressEvents> = {}): Promise<CID> {
|
|
260
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
261
|
+
setMaxListeners(Infinity, signal)
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
return await super.put(cid, block, {
|
|
265
|
+
...options,
|
|
266
|
+
signal
|
|
267
|
+
})
|
|
268
|
+
} finally {
|
|
269
|
+
signal.clear()
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Put a multiple blocks to the underlying datastore
|
|
275
|
+
*/
|
|
276
|
+
async * putMany (blocks: AwaitIterable<{ cid: CID, block: Uint8Array }>, options: AbortOptions & ProgressOptions<PutManyBlocksProgressEvents> = {}): AsyncIterable<CID> {
|
|
277
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
278
|
+
setMaxListeners(Infinity, signal)
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
yield * super.putMany(blocks, {
|
|
282
|
+
...options,
|
|
283
|
+
signal
|
|
284
|
+
})
|
|
285
|
+
} finally {
|
|
286
|
+
signal.clear()
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get a block by cid
|
|
292
|
+
*/
|
|
293
|
+
async get (cid: CID, options: GetOfflineOptions & AbortOptions & ProgressOptions<GetBlockProgressEvents> = {}): Promise<Uint8Array> {
|
|
294
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
295
|
+
setMaxListeners(Infinity, signal)
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
return await super.get(cid, {
|
|
299
|
+
...options,
|
|
300
|
+
signal
|
|
301
|
+
})
|
|
302
|
+
} finally {
|
|
303
|
+
signal.clear()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get multiple blocks back from an (async) iterable of cids
|
|
309
|
+
*/
|
|
310
|
+
async * getMany (cids: AwaitIterable<CID>, options: GetOfflineOptions & AbortOptions & ProgressOptions<GetManyBlocksProgressEvents> = {}): AsyncIterable<Pair> {
|
|
311
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
312
|
+
setMaxListeners(Infinity, signal)
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
yield * super.getMany(cids, {
|
|
316
|
+
...options,
|
|
317
|
+
signal
|
|
318
|
+
})
|
|
319
|
+
} finally {
|
|
320
|
+
signal.clear()
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Delete a block from the blockstore
|
|
326
|
+
*/
|
|
327
|
+
async delete (cid: CID, options: AbortOptions & ProgressOptions<DeleteBlockProgressEvents> = {}): Promise<void> {
|
|
328
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
329
|
+
setMaxListeners(Infinity, signal)
|
|
330
|
+
|
|
331
|
+
try {
|
|
332
|
+
await super.delete(cid, {
|
|
333
|
+
...options,
|
|
334
|
+
signal
|
|
335
|
+
})
|
|
336
|
+
} finally {
|
|
337
|
+
signal.clear()
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Delete multiple blocks from the blockstore
|
|
343
|
+
*/
|
|
344
|
+
async * deleteMany (cids: AwaitIterable<CID>, options: AbortOptions & ProgressOptions<DeleteManyBlocksProgressEvents> = {}): AsyncIterable<CID> {
|
|
345
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
346
|
+
setMaxListeners(Infinity, signal)
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
yield * super.deleteMany(cids, {
|
|
350
|
+
...options,
|
|
351
|
+
signal
|
|
352
|
+
})
|
|
353
|
+
} finally {
|
|
354
|
+
signal.clear()
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async has (cid: CID, options: AbortOptions = {}): Promise<boolean> {
|
|
359
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
360
|
+
setMaxListeners(Infinity, signal)
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
return await super.has(cid, {
|
|
364
|
+
...options,
|
|
365
|
+
signal
|
|
366
|
+
})
|
|
367
|
+
} finally {
|
|
368
|
+
signal.clear()
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async * getAll (options: AbortOptions & ProgressOptions<GetAllBlocksProgressEvents> = {}): AwaitIterable<Pair> {
|
|
373
|
+
const signal = anySignal([this.closeController.signal, options.signal])
|
|
374
|
+
setMaxListeners(Infinity, signal)
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
yield * super.getAll({
|
|
378
|
+
...options,
|
|
379
|
+
signal
|
|
380
|
+
})
|
|
381
|
+
} finally {
|
|
382
|
+
signal.clear()
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function isRetrievingBlockBroker (broker: BlockBroker): broker is Required<Pick<BlockBroker, 'retrieve'>> {
|
|
388
|
+
return typeof broker.retrieve === 'function'
|
|
389
|
+
}
|
|
390
|
+
|
|
210
391
|
export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher): Required<BlockRetrievalOptions>['validateFn'] => {
|
|
211
392
|
if (hasher == null) {
|
|
212
393
|
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 +408,53 @@ export const getCidBlockVerifierFunction = (cid: CID, hasher: MultihashHasher):
|
|
|
227
408
|
* Race block providers cancelling any pending requests once the block has been
|
|
228
409
|
* found.
|
|
229
410
|
*/
|
|
230
|
-
async function raceBlockRetrievers (cid: CID,
|
|
411
|
+
async function raceBlockRetrievers (cid: CID, blockBrokers: BlockBroker[], hasher: MultihashHasher, options: AbortOptions & LoggerOptions): Promise<Uint8Array> {
|
|
231
412
|
const validateFn = getCidBlockVerifierFunction(cid, hasher)
|
|
232
413
|
|
|
233
414
|
const controller = new AbortController()
|
|
234
415
|
const signal = anySignal([controller.signal, options.signal])
|
|
416
|
+
setMaxListeners(Infinity, controller.signal, signal)
|
|
417
|
+
|
|
418
|
+
const retrievers: Array<Required<Pick<BlockBroker, 'retrieve'>>> = []
|
|
419
|
+
|
|
420
|
+
for (const broker of blockBrokers) {
|
|
421
|
+
if (isRetrievingBlockBroker(broker)) {
|
|
422
|
+
retrievers.push(broker)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
235
425
|
|
|
236
426
|
try {
|
|
237
427
|
return await Promise.any(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
428
|
+
retrievers
|
|
429
|
+
.map(async retriever => {
|
|
430
|
+
try {
|
|
431
|
+
let blocksWereValidated = false
|
|
432
|
+
const block = await retriever.retrieve(cid, {
|
|
433
|
+
...options,
|
|
434
|
+
signal,
|
|
435
|
+
validateFn: async (block: Uint8Array): Promise<void> => {
|
|
436
|
+
await validateFn(block)
|
|
437
|
+
blocksWereValidated = true
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
if (!blocksWereValidated) {
|
|
442
|
+
// the blockBroker either did not throw an error when attempting to validate the block
|
|
443
|
+
// or did not call the validateFn at all. We should validate the block ourselves
|
|
245
444
|
await validateFn(block)
|
|
246
|
-
blocksWereValidated = true
|
|
247
445
|
}
|
|
248
|
-
})
|
|
249
446
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
447
|
+
return block
|
|
448
|
+
} catch (err) {
|
|
449
|
+
options.log.error('could not retrieve verified block for %c', cid, err)
|
|
450
|
+
throw err
|
|
254
451
|
}
|
|
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
|
-
})
|
|
452
|
+
})
|
|
262
453
|
)
|
|
263
454
|
} finally {
|
|
455
|
+
// we have the block from the fastest block retriever, abort any still
|
|
456
|
+
// in-flight retrieve attempts
|
|
457
|
+
controller.abort()
|
|
264
458
|
signal.clear()
|
|
265
459
|
}
|
|
266
460
|
}
|