@fireproof/core 0.6.4 → 0.7.0-alpha.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/README.md +2 -2
- package/dist/{src/blockstore.js → blockstore.js} +7 -3
- package/dist/{src/database.js → database.js} +74 -48
- package/dist/{src/db-index.js → db-index.js} +13 -2
- package/dist/fireproof.js +92 -0
- package/dist/import.js +29 -0
- package/dist/loader.js +23 -0
- package/dist/{src/prolly.js → prolly.js} +1 -0
- package/dist/src/fireproof.d.ts +138 -137
- package/dist/src/fireproof.js +18183 -11479
- package/dist/src/fireproof.js.map +1 -1
- package/dist/src/fireproof.mjs +18184 -11479
- package/dist/src/fireproof.mjs.map +1 -1
- package/dist/storage/base.js +348 -0
- package/dist/storage/browser.js +61 -0
- package/dist/storage/filesystem.js +65 -0
- package/dist/storage/rest.js +58 -0
- package/dist/storage/ucan.js +0 -0
- package/dist/{src/sync.js → sync.js} +1 -1
- package/dist/valet.js +200 -0
- package/package.json +4 -5
- package/src/blockstore.js +6 -3
- package/src/database.js +80 -52
- package/src/db-index.js +12 -2
- package/src/fireproof.js +41 -30
- package/src/import.js +34 -0
- package/src/loader.js +26 -0
- package/src/prolly.js +2 -0
- package/src/storage/base.js +371 -0
- package/src/storage/browser.js +67 -0
- package/src/storage/filesystem.js +70 -0
- package/src/storage/rest.js +60 -0
- package/src/storage/ucan.js +0 -0
- package/src/sync.js +1 -1
- package/src/valet.js +57 -359
- package/dist/hooks/use-fireproof.js +0 -150
- package/dist/src/crypto-poly.js +0 -4
- package/dist/src/link.js +0 -1
- package/dist/src/loader.js +0 -131
- package/dist/src/valet.js +0 -476
- package/hooks/use-fireproof.js +0 -173
- package/src/listener.js +0 -119
- package/src/utils.js +0 -16
- /package/dist/{src/clock.js → clock.js} +0 -0
- /package/dist/{src/crypto.js → crypto.js} +0 -0
- /package/dist/{src/listener.js → listener.js} +0 -0
- /package/dist/{src/sha1.js → sha1.js} +0 -0
- /package/dist/{src/utils.js → utils.js} +0 -0
package/dist/src/loader.js
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
import { readFileSync
|
2
|
-
// createReadStream
|
3
|
-
} from 'node:fs';
|
4
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
5
|
-
import { openDB } from 'idb';
|
6
|
-
import { join, dirname } from 'path';
|
7
|
-
// import { parse } from '@jsonlines/core'
|
8
|
-
// import cargoQueue from 'async/cargoQueue.js'
|
9
|
-
import { homedir } from 'os';
|
10
|
-
const defaultConfig = {
|
11
|
-
dataDir: join(homedir(), '.fireproof'),
|
12
|
-
headerKeyPrefix: 'fp.'
|
13
|
-
};
|
14
|
-
const FORCE_IDB = typeof process !== 'undefined' && !!process.env?.FORCE_IDB;
|
15
|
-
/* global localStorage */
|
16
|
-
export class Loader {
|
17
|
-
constructor(name, keyId, config = defaultConfig) {
|
18
|
-
this.name = name;
|
19
|
-
this.keyId = keyId;
|
20
|
-
this.config = config;
|
21
|
-
this.isBrowser = false;
|
22
|
-
try {
|
23
|
-
this.isBrowser = window.localStorage && true;
|
24
|
-
}
|
25
|
-
catch (e) { }
|
26
|
-
}
|
27
|
-
withDB = async (dbWorkFun) => {
|
28
|
-
if (!this.idb) {
|
29
|
-
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
30
|
-
upgrade(db, oldVersion, newVersion, transaction) {
|
31
|
-
if (oldVersion < 1) {
|
32
|
-
db.createObjectStore('cars');
|
33
|
-
}
|
34
|
-
}
|
35
|
-
});
|
36
|
-
}
|
37
|
-
return await dbWorkFun(this.idb);
|
38
|
-
};
|
39
|
-
async writeCars(cars) {
|
40
|
-
// console.log('writeCars', this.config.dataDir, this.name, cars.map(c => c.cid.toString()))
|
41
|
-
// console.log('writeCars', cars.length)
|
42
|
-
if (FORCE_IDB || this.isBrowser) {
|
43
|
-
await this.writeCarsIDB(cars);
|
44
|
-
}
|
45
|
-
else {
|
46
|
-
const writes = [];
|
47
|
-
for (const { cid, bytes } of cars) {
|
48
|
-
const carFilename = join(this.config.dataDir, this.name, `${cid.toString()}.car`);
|
49
|
-
// console.log('writeCars', carFilename)
|
50
|
-
writes.push(writeSync(carFilename, bytes));
|
51
|
-
}
|
52
|
-
await Promise.all(writes);
|
53
|
-
}
|
54
|
-
}
|
55
|
-
async writeCarsIDB(cars) {
|
56
|
-
return await this.withDB(async (db) => {
|
57
|
-
const tx = db.transaction(['cars'], 'readwrite');
|
58
|
-
for (const { cid, bytes, replaces } of cars) {
|
59
|
-
await tx.objectStore('cars').put(bytes, cid.toString());
|
60
|
-
// todo remove old maps
|
61
|
-
if (replaces) {
|
62
|
-
await tx.objectStore('cars').delete(replaces.toString());
|
63
|
-
}
|
64
|
-
}
|
65
|
-
return await tx.done;
|
66
|
-
});
|
67
|
-
}
|
68
|
-
async readCar(carCid) {
|
69
|
-
if (FORCE_IDB || this.isBrowser) {
|
70
|
-
return await this.readCarIDB(carCid);
|
71
|
-
}
|
72
|
-
else {
|
73
|
-
const carFilename = join(this.config.dataDir, this.name, `${carCid.toString()}.car`);
|
74
|
-
const got = readFileSync(carFilename);
|
75
|
-
// console.log('readCar', carFilename, got.constructor.name)
|
76
|
-
return got;
|
77
|
-
}
|
78
|
-
}
|
79
|
-
async readCarIDB(carCid) {
|
80
|
-
return await this.withDB(async (db) => {
|
81
|
-
const tx = db.transaction(['cars'], 'readonly');
|
82
|
-
// console.log('getCarReader', carCid)
|
83
|
-
return await tx.objectStore('cars').get(carCid);
|
84
|
-
});
|
85
|
-
}
|
86
|
-
getHeader() {
|
87
|
-
if (this.isBrowser) {
|
88
|
-
return localStorage.getItem(this.config.headerKeyPrefix + this.name);
|
89
|
-
}
|
90
|
-
else {
|
91
|
-
return loadSync(this.headerFilename());
|
92
|
-
// return null
|
93
|
-
}
|
94
|
-
}
|
95
|
-
async saveHeader(stringValue) {
|
96
|
-
// console.log('saveHeader', this.isBrowser)
|
97
|
-
if (this.isBrowser) {
|
98
|
-
// console.log('localStorage!', this.config.headerKeyPrefix)
|
99
|
-
return localStorage.setItem(this.config.headerKeyPrefix + this.name, stringValue);
|
100
|
-
}
|
101
|
-
else {
|
102
|
-
// console.log('no localStorage', this.config.dataDir, this.name)
|
103
|
-
// console.log('saving clock to', this.headerFilename(), stringValue)
|
104
|
-
try {
|
105
|
-
await writeSync(this.headerFilename(), stringValue);
|
106
|
-
}
|
107
|
-
catch (error) {
|
108
|
-
console.log('error', error);
|
109
|
-
}
|
110
|
-
// console.log('saved clock to', this.headerFilename())
|
111
|
-
}
|
112
|
-
}
|
113
|
-
headerFilename() {
|
114
|
-
// console.log('headerFilename', this.config.dataDir, this.name)
|
115
|
-
return join(this.config.dataDir, this.name, 'header.json');
|
116
|
-
}
|
117
|
-
}
|
118
|
-
function loadSync(filename) {
|
119
|
-
try {
|
120
|
-
return readFileSync(filename, 'utf8').toString();
|
121
|
-
}
|
122
|
-
catch (error) {
|
123
|
-
// console.log('error', error)
|
124
|
-
return null;
|
125
|
-
}
|
126
|
-
}
|
127
|
-
async function writeSync(fullpath, stringValue) {
|
128
|
-
await mkdir(dirname(fullpath), { recursive: true });
|
129
|
-
// writeFileSync(fullpath, stringValue)
|
130
|
-
await writeFile(fullpath, stringValue);
|
131
|
-
}
|
package/dist/src/valet.js
DELETED
@@ -1,476 +0,0 @@
|
|
1
|
-
import { CarReader } from '@ipld/car';
|
2
|
-
import { CID } from 'multiformats/cid';
|
3
|
-
import { sha256 } from 'multiformats/hashes/sha2';
|
4
|
-
import { parse } from 'multiformats/link';
|
5
|
-
import * as CBW from '@ipld/car/buffer-writer';
|
6
|
-
import * as raw from 'multiformats/codecs/raw';
|
7
|
-
import * as Block from 'multiformats/block';
|
8
|
-
import * as dagcbor from '@ipld/dag-cbor';
|
9
|
-
import { openDB } from 'idb';
|
10
|
-
import cargoQueue from 'async/cargoQueue.js';
|
11
|
-
// @ts-ignore
|
12
|
-
// @ts-ignore
|
13
|
-
import { bf, simpleCompare as compare } from 'prolly-trees/utils';
|
14
|
-
// @ts-ignore
|
15
|
-
import { nocache as cache } from 'prolly-trees/cache';
|
16
|
-
// import { makeGetBlock } from './prolly.js'
|
17
|
-
import { encrypt, decrypt } from './crypto.js';
|
18
|
-
import { Buffer } from 'buffer';
|
19
|
-
// @ts-ignore
|
20
|
-
import * as codec from 'encrypted-block';
|
21
|
-
import { create, load } from 'ipld-hashmap';
|
22
|
-
import { rawSha1 as sha1sync } from './sha1.js';
|
23
|
-
const chunker = bf(30);
|
24
|
-
const blockOpts = { cache, chunker, codec: dagcbor, hasher: sha256, compare };
|
25
|
-
const NO_ENCRYPT = typeof process !== 'undefined' && !!process.env?.NO_ENCRYPT;
|
26
|
-
// ? process.env.NO_ENCRYPT : import.meta && import.meta.env.VITE_NO_ENCRYPT
|
27
|
-
export class Valet {
|
28
|
-
idb = null;
|
29
|
-
name = null;
|
30
|
-
uploadQueue = null;
|
31
|
-
alreadyEnqueued = new Set();
|
32
|
-
keyMaterial = null;
|
33
|
-
keyId = 'null';
|
34
|
-
valetRoot = null;
|
35
|
-
valetRootCid = null; // set by hydrate
|
36
|
-
valetRootCarCid = null; // most recent diff
|
37
|
-
valetCidBlocks = new VMemoryBlockstore();
|
38
|
-
instanceId = Math.random().toString(36).slice(2);
|
39
|
-
/**
|
40
|
-
* Function installed by the database to upload car files
|
41
|
-
* @type {null|function(string, Uint8Array):Promise<void>}
|
42
|
-
*/
|
43
|
-
uploadFunction = null;
|
44
|
-
constructor(name = 'default', keyMaterial) {
|
45
|
-
this.name = name;
|
46
|
-
this.setKeyMaterial(keyMaterial);
|
47
|
-
this.uploadQueue = cargoQueue(async (tasks, callback) => {
|
48
|
-
// console.log(
|
49
|
-
// 'queue worker',
|
50
|
-
// tasks.length,
|
51
|
-
// tasks.reduce((acc, t) => acc + t.value.length, 0)
|
52
|
-
// )
|
53
|
-
if (this.uploadFunction) {
|
54
|
-
// todo we can coalesce these into a single car file
|
55
|
-
// todo remove idb usage here
|
56
|
-
for (const task of tasks) {
|
57
|
-
await this.uploadFunction(task.carCid, task.value);
|
58
|
-
// todo update syncCidMap to say this has been synced
|
59
|
-
// const carMeta = await db.get('cidToCar', task.carCid)
|
60
|
-
// delete carMeta.pending
|
61
|
-
// await db.put('cidToCar', carMeta)
|
62
|
-
}
|
63
|
-
}
|
64
|
-
callback();
|
65
|
-
});
|
66
|
-
this.uploadQueue.drain(async () => {
|
67
|
-
// todo read syncCidMap and sync any that are still unsynced
|
68
|
-
// return await this.withDB(async db => {
|
69
|
-
// const carKeys = (await db.getAllFromIndex('cidToCar', 'pending')).map(c => c.car)
|
70
|
-
// for (const carKey of carKeys) {
|
71
|
-
// await this.uploadFunction(carKey, await db.get('cars', carKey))
|
72
|
-
// const carMeta = await db.get('cidToCar', carKey)
|
73
|
-
// delete carMeta.pending
|
74
|
-
// await db.put('cidToCar', carMeta)
|
75
|
-
// }
|
76
|
-
// })
|
77
|
-
});
|
78
|
-
}
|
79
|
-
getKeyMaterial() {
|
80
|
-
return this.keyMaterial;
|
81
|
-
}
|
82
|
-
setKeyMaterial(km) {
|
83
|
-
if (km && !NO_ENCRYPT) {
|
84
|
-
const hex = Uint8Array.from(Buffer.from(km, 'hex'));
|
85
|
-
this.keyMaterial = km;
|
86
|
-
const hash = sha1sync(hex);
|
87
|
-
this.keyId = Buffer.from(hash).toString('hex');
|
88
|
-
}
|
89
|
-
else {
|
90
|
-
this.keyMaterial = null;
|
91
|
-
this.keyId = 'null';
|
92
|
-
}
|
93
|
-
// console.trace('keyId', this.name, this.keyId)
|
94
|
-
}
|
95
|
-
/**
|
96
|
-
* Group the blocks into a car and write it to the valet.
|
97
|
-
* @param {import('./blockstore.js').InnerBlockstore} innerBlockstore
|
98
|
-
* @param {Set<string>} cids
|
99
|
-
* @returns {Promise<void>}
|
100
|
-
* @memberof Valet
|
101
|
-
*/
|
102
|
-
async writeTransaction(innerBlockstore, cids) {
|
103
|
-
if (innerBlockstore.lastCid) {
|
104
|
-
if (this.keyMaterial) {
|
105
|
-
// console.log('encrypting car', innerBlockstore.label)
|
106
|
-
// should we pass cids in instead of iterating frin innerBlockstore?
|
107
|
-
const newCar = await blocksToEncryptedCarBlock(innerBlockstore.lastCid, innerBlockstore, this.keyMaterial);
|
108
|
-
await this.parkCar(newCar.cid.toString(), newCar.bytes, cids);
|
109
|
-
}
|
110
|
-
else {
|
111
|
-
const newCar = await blocksToCarBlock(innerBlockstore.lastCid, innerBlockstore);
|
112
|
-
await this.parkCar(newCar.cid.toString(), newCar.bytes, cids);
|
113
|
-
}
|
114
|
-
}
|
115
|
-
else {
|
116
|
-
throw new Error('missing lastCid for car header');
|
117
|
-
}
|
118
|
-
}
|
119
|
-
withDB = async (dbWorkFun) => {
|
120
|
-
if (!this.idb) {
|
121
|
-
this.idb = await openDB(`fp.${this.keyId}.${this.name}.valet`, 3, {
|
122
|
-
upgrade(db, oldVersion, newVersion, transaction) {
|
123
|
-
if (oldVersion < 1) {
|
124
|
-
db.createObjectStore('cars');
|
125
|
-
}
|
126
|
-
}
|
127
|
-
});
|
128
|
-
}
|
129
|
-
return await dbWorkFun(this.idb);
|
130
|
-
};
|
131
|
-
/**
|
132
|
-
* Iterate over all blocks in the store.
|
133
|
-
*
|
134
|
-
* @yields {{cid: string, value: Uint8Array}}
|
135
|
-
* @returns {AsyncGenerator<any, any, any>}
|
136
|
-
*/
|
137
|
-
async *cids() {
|
138
|
-
// console.log('valet cids')
|
139
|
-
// todo use cidMap
|
140
|
-
// while (cursor) {
|
141
|
-
// yield { cid: cursor.key, car: cursor.value.car }
|
142
|
-
// cursor = await cursor.continue()
|
143
|
-
// }
|
144
|
-
}
|
145
|
-
setRootCarCid(cid) {
|
146
|
-
this.valetRootCarCid = cid;
|
147
|
-
this.valetRoot = null;
|
148
|
-
this.valetRootCid = null;
|
149
|
-
}
|
150
|
-
// todo memoize this
|
151
|
-
async getCarCIDForCID(cid) {
|
152
|
-
// make a car reader for this.valetRootCarCid
|
153
|
-
if (!this.valetRootCarCid)
|
154
|
-
return { result: null };
|
155
|
-
let indexNode;
|
156
|
-
if (this.valetRoot) {
|
157
|
-
indexNode = this.valetRoot;
|
158
|
-
}
|
159
|
-
else {
|
160
|
-
const combinedReader = await this.getCombinedReader(this.valetRootCarCid);
|
161
|
-
if (!this.valetRootCid) {
|
162
|
-
const root = combinedReader.root.cid;
|
163
|
-
// console.log('roots', this.instanceId, this.name, root, this.valetRootCarCid, this.valetRootCid)
|
164
|
-
this.valetRootCid = root;
|
165
|
-
}
|
166
|
-
indexNode = await load(combinedReader, this.valetRootCid, {
|
167
|
-
blockHasher: blockOpts.hasher,
|
168
|
-
blockCodec: blockOpts.codec
|
169
|
-
});
|
170
|
-
}
|
171
|
-
const got = await indexNode.get(cid);
|
172
|
-
// console.log('getCarCIDForCID', cid, got)
|
173
|
-
return { result: got };
|
174
|
-
}
|
175
|
-
async getCombinedReader(carCid) {
|
176
|
-
let carMapReader;
|
177
|
-
if (this.valetRootCarCid) {
|
178
|
-
// todo only need this if we are cold starting
|
179
|
-
carMapReader = await this.getCarReader(this.valetRootCarCid);
|
180
|
-
}
|
181
|
-
const theseValetCidBlocks = this.valetCidBlocks;
|
182
|
-
// console.log('theseValetCidBlocks', theseValetCidBlocks)
|
183
|
-
const combinedReader = {
|
184
|
-
root: carMapReader?.root,
|
185
|
-
put: async (cid, bytes) => {
|
186
|
-
// console.log('mapPut', cid, bytes.length)
|
187
|
-
return await theseValetCidBlocks.put(cid, bytes);
|
188
|
-
},
|
189
|
-
get: async (cid) => {
|
190
|
-
// console.log('mapGet', cid)
|
191
|
-
try {
|
192
|
-
const got = await theseValetCidBlocks.get(cid);
|
193
|
-
return got.bytes;
|
194
|
-
}
|
195
|
-
catch (e) {
|
196
|
-
// console.log('get from car', cid, carMapReader)
|
197
|
-
if (!carMapReader)
|
198
|
-
throw e;
|
199
|
-
const bytes = await carMapReader.get(cid);
|
200
|
-
await theseValetCidBlocks.put(cid, bytes);
|
201
|
-
// console.log('mapGet', cid, bytes.length, bytes.constructor.name)
|
202
|
-
return bytes;
|
203
|
-
}
|
204
|
-
}
|
205
|
-
};
|
206
|
-
return combinedReader;
|
207
|
-
}
|
208
|
-
/**
|
209
|
-
*
|
210
|
-
* @param {string} carCid
|
211
|
-
* @param {*} value
|
212
|
-
*/
|
213
|
-
async parkCar(carCid, value, cids) {
|
214
|
-
// console.log('parkCar', this.instanceId, this.name, carCid, cids)
|
215
|
-
const combinedReader = await this.getCombinedReader(carCid);
|
216
|
-
const mapNode = await addCidsToCarIndex(combinedReader, this.valetRoot, this.valetRootCid, Array.from(cids).map(cid => ({ key: cid.toString(), value: carCid.toString() })));
|
217
|
-
this.valetRoot = mapNode;
|
218
|
-
this.valetRootCid = mapNode.cid;
|
219
|
-
// make a block set with all the cids of the map
|
220
|
-
const saveValetBlocks = new VMemoryBlockstore(); // todo this blockstore should read from the last valetCid car also
|
221
|
-
for await (const cidx of mapNode.cids()) {
|
222
|
-
const bytes = await combinedReader.get(cidx);
|
223
|
-
saveValetBlocks.put(cidx, bytes);
|
224
|
-
}
|
225
|
-
let newValetCidCar;
|
226
|
-
if (this.keyMaterial) {
|
227
|
-
newValetCidCar = await blocksToEncryptedCarBlock(this.valetRootCid, saveValetBlocks, this.keyMaterial);
|
228
|
-
}
|
229
|
-
else {
|
230
|
-
newValetCidCar = await blocksToCarBlock(this.valetRootCid, saveValetBlocks);
|
231
|
-
}
|
232
|
-
// console.log('newValetCidCar', this.name, Math.floor(newValetCidCar.bytes.length / 1024))
|
233
|
-
await this.writeCars([
|
234
|
-
{
|
235
|
-
cid: carCid,
|
236
|
-
bytes: value,
|
237
|
-
replaces: null
|
238
|
-
},
|
239
|
-
{
|
240
|
-
cid: newValetCidCar.cid,
|
241
|
-
bytes: newValetCidCar.bytes,
|
242
|
-
replaces: null
|
243
|
-
// replaces: this.valetRootCarCid // todo
|
244
|
-
}
|
245
|
-
]);
|
246
|
-
this.valetRootCarCid = newValetCidCar.cid; // goes to clock
|
247
|
-
// console.log('parked car', carCid, value.length, Array.from(cids))
|
248
|
-
// upload to web3.storage if we have credentials
|
249
|
-
if (this.uploadFunction) {
|
250
|
-
if (this.alreadyEnqueued.has(carCid)) {
|
251
|
-
// console.log('already enqueued', carCid)
|
252
|
-
return;
|
253
|
-
}
|
254
|
-
// don't await this, it will be done in the queue
|
255
|
-
// console.log('add to queue', carCid, value.length)
|
256
|
-
this.uploadQueue.push({ carCid, value });
|
257
|
-
this.alreadyEnqueued.add(carCid);
|
258
|
-
}
|
259
|
-
else {
|
260
|
-
// console.log('no upload function', carCid, value.length, this.uploadFunction)
|
261
|
-
}
|
262
|
-
}
|
263
|
-
async writeCars(cars) {
|
264
|
-
return await this.withDB(async (db) => {
|
265
|
-
const tx = db.transaction(['cars'], 'readwrite');
|
266
|
-
for (const { cid, bytes, replaces } of cars) {
|
267
|
-
await tx.objectStore('cars').put(bytes, cid.toString());
|
268
|
-
// todo remove old maps
|
269
|
-
if (replaces) {
|
270
|
-
await tx.objectStore('cars').delete(replaces.toString());
|
271
|
-
}
|
272
|
-
}
|
273
|
-
return await tx.done;
|
274
|
-
});
|
275
|
-
}
|
276
|
-
remoteBlockFunction = null;
|
277
|
-
async getCarReader(carCid) {
|
278
|
-
carCid = carCid.toString();
|
279
|
-
const carBytes = await this.withDB(async (db) => {
|
280
|
-
const tx = db.transaction(['cars'], 'readonly');
|
281
|
-
// console.log('getCarReader', carCid)
|
282
|
-
return await tx.objectStore('cars').get(carCid);
|
283
|
-
});
|
284
|
-
const reader = await CarReader.fromBytes(carBytes);
|
285
|
-
if (this.keyMaterial) {
|
286
|
-
const roots = await reader.getRoots();
|
287
|
-
const readerGetWithCodec = async (cid) => {
|
288
|
-
const got = await reader.get(cid);
|
289
|
-
// console.log('got.', cid.toString())
|
290
|
-
let useCodec = codec;
|
291
|
-
if (cid.toString().indexOf('bafy') === 0) {
|
292
|
-
// todo cleanup types
|
293
|
-
useCodec = dagcbor;
|
294
|
-
}
|
295
|
-
const decoded = await Block.decode({
|
296
|
-
...got,
|
297
|
-
codec: useCodec,
|
298
|
-
hasher: sha256
|
299
|
-
});
|
300
|
-
// console.log('decoded', decoded.value)
|
301
|
-
return decoded;
|
302
|
-
};
|
303
|
-
const { blocks } = await blocksFromEncryptedCarBlock(roots[0], readerGetWithCodec, this.keyMaterial);
|
304
|
-
// last block is the root ??? todo
|
305
|
-
const rootBlock = blocks[blocks.length - 1];
|
306
|
-
return {
|
307
|
-
root: rootBlock,
|
308
|
-
get: async (dataCID) => {
|
309
|
-
// console.log('getCarReader dataCID', dataCID)
|
310
|
-
dataCID = dataCID.toString();
|
311
|
-
const block = blocks.find(b => b.cid.toString() === dataCID);
|
312
|
-
// console.log('getCarReader block', block)
|
313
|
-
if (block) {
|
314
|
-
return block.bytes;
|
315
|
-
}
|
316
|
-
}
|
317
|
-
};
|
318
|
-
}
|
319
|
-
else {
|
320
|
-
return {
|
321
|
-
root: reader.getRoots()[0],
|
322
|
-
get: async (dataCID) => {
|
323
|
-
const gotBlock = await reader.get(CID.parse(dataCID));
|
324
|
-
if (gotBlock) {
|
325
|
-
return gotBlock.bytes;
|
326
|
-
}
|
327
|
-
}
|
328
|
-
};
|
329
|
-
}
|
330
|
-
}
|
331
|
-
// todo memoize this
|
332
|
-
async getValetBlock(dataCID) {
|
333
|
-
// console.log('get valet block', dataCID)
|
334
|
-
const { result: carCid } = await this.getCarCIDForCID(dataCID);
|
335
|
-
if (!carCid) {
|
336
|
-
throw new Error('Missing block: ' + dataCID);
|
337
|
-
}
|
338
|
-
const reader = await this.getCarReader(carCid);
|
339
|
-
return await reader.get(dataCID);
|
340
|
-
}
|
341
|
-
}
|
342
|
-
export const blocksToCarBlock = async (rootCids, blocks) => {
|
343
|
-
// console.log('blocksToCarBlock', rootCids, blocks.constructor.name)
|
344
|
-
let size = 0;
|
345
|
-
if (!Array.isArray(rootCids)) {
|
346
|
-
rootCids = [rootCids];
|
347
|
-
}
|
348
|
-
const headerSize = CBW.headerLength({ roots: rootCids });
|
349
|
-
size += headerSize;
|
350
|
-
if (!Array.isArray(blocks)) {
|
351
|
-
blocks = Array.from(blocks.entries());
|
352
|
-
}
|
353
|
-
for (const { cid, bytes } of blocks) {
|
354
|
-
// console.log(cid, bytes)
|
355
|
-
size += CBW.blockLength({ cid, bytes });
|
356
|
-
}
|
357
|
-
const buffer = new Uint8Array(size);
|
358
|
-
const writer = await CBW.createWriter(buffer, { headerSize });
|
359
|
-
for (const cid of rootCids) {
|
360
|
-
writer.addRoot(cid);
|
361
|
-
}
|
362
|
-
for (const { cid, bytes } of blocks) {
|
363
|
-
writer.write({ cid, bytes });
|
364
|
-
}
|
365
|
-
await writer.close();
|
366
|
-
return await Block.encode({ value: writer.bytes, hasher: sha256, codec: raw });
|
367
|
-
};
|
368
|
-
export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blocks, keyMaterial) => {
|
369
|
-
const encryptionKey = Buffer.from(keyMaterial, 'hex');
|
370
|
-
const encryptedBlocks = [];
|
371
|
-
const theCids = [];
|
372
|
-
for (const { cid } of blocks.entries()) {
|
373
|
-
theCids.push(cid.toString());
|
374
|
-
}
|
375
|
-
// console.log('encrypting', theCids.length, 'blocks', theCids.includes(innerBlockStoreClockRootCid.toString()))
|
376
|
-
// console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
|
377
|
-
let last;
|
378
|
-
for await (const block of encrypt({
|
379
|
-
cids: theCids,
|
380
|
-
get: async (cid) => blocks.get(cid),
|
381
|
-
key: encryptionKey,
|
382
|
-
hasher: sha256,
|
383
|
-
chunker,
|
384
|
-
cache,
|
385
|
-
// codec: dagcbor, // should be crypto?
|
386
|
-
root: innerBlockStoreClockRootCid
|
387
|
-
})) {
|
388
|
-
encryptedBlocks.push(block);
|
389
|
-
last = block;
|
390
|
-
}
|
391
|
-
// console.log('last', last.cid.toString(), 'for clock', innerBlockStoreClockRootCid.toString())
|
392
|
-
const encryptedCar = await blocksToCarBlock(last.cid, encryptedBlocks);
|
393
|
-
return encryptedCar;
|
394
|
-
};
|
395
|
-
// { root, get, key, cache, chunker, hasher }
|
396
|
-
const memoizeDecryptedCarBlocks = new Map();
|
397
|
-
const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
|
398
|
-
if (memoizeDecryptedCarBlocks.has(cid.toString())) {
|
399
|
-
return memoizeDecryptedCarBlocks.get(cid.toString());
|
400
|
-
}
|
401
|
-
else {
|
402
|
-
const blocksPromise = (async () => {
|
403
|
-
const decryptionKey = Buffer.from(keyMaterial, 'hex');
|
404
|
-
// console.log('decrypting', keyMaterial, cid.toString())
|
405
|
-
const cids = new Set();
|
406
|
-
const decryptedBlocks = [];
|
407
|
-
for await (const block of decrypt({
|
408
|
-
root: cid,
|
409
|
-
get,
|
410
|
-
key: decryptionKey,
|
411
|
-
chunker,
|
412
|
-
hasher: sha256,
|
413
|
-
cache
|
414
|
-
// codec: dagcbor
|
415
|
-
})) {
|
416
|
-
decryptedBlocks.push(block);
|
417
|
-
cids.add(block.cid.toString());
|
418
|
-
}
|
419
|
-
return { blocks: decryptedBlocks, cids };
|
420
|
-
})();
|
421
|
-
memoizeDecryptedCarBlocks.set(cid.toString(), blocksPromise);
|
422
|
-
return blocksPromise;
|
423
|
-
}
|
424
|
-
};
|
425
|
-
const addCidsToCarIndex = async (blockstore, valetRoot, valetRootCid, bulkOperations) => {
|
426
|
-
let indexNode;
|
427
|
-
if (valetRootCid) {
|
428
|
-
if (valetRoot) {
|
429
|
-
indexNode = valetRoot;
|
430
|
-
}
|
431
|
-
else {
|
432
|
-
indexNode = await load(blockstore, valetRootCid, { blockHasher: blockOpts.hasher, blockCodec: blockOpts.codec });
|
433
|
-
}
|
434
|
-
}
|
435
|
-
else {
|
436
|
-
indexNode = await create(blockstore, {
|
437
|
-
bitWidth: 4,
|
438
|
-
bucketSize: 2,
|
439
|
-
blockHasher: blockOpts.hasher,
|
440
|
-
blockCodec: blockOpts.codec
|
441
|
-
});
|
442
|
-
}
|
443
|
-
// console.log('adding', bulkOperations.length, 'cids to index')
|
444
|
-
for (const { key, value } of bulkOperations) {
|
445
|
-
// console.log('adding', key, value)
|
446
|
-
await indexNode.set(key, value);
|
447
|
-
}
|
448
|
-
return indexNode;
|
449
|
-
};
|
450
|
-
export class VMemoryBlockstore {
|
451
|
-
/** @type {Map<string, Uint8Array>} */
|
452
|
-
blocks = new Map();
|
453
|
-
instanceId = Math.random().toString(36).slice(2);
|
454
|
-
async get(cid) {
|
455
|
-
const bytes = this.blocks.get(cid.toString());
|
456
|
-
// console.log('getvm', bytes.constructor.name, this.instanceId, cid, bytes && bytes.length)
|
457
|
-
if (bytes.length === 253) {
|
458
|
-
// console.log('getvm', bytes.())
|
459
|
-
}
|
460
|
-
if (!bytes)
|
461
|
-
throw new Error('block not found ' + cid.toString());
|
462
|
-
return { cid, bytes };
|
463
|
-
}
|
464
|
-
/**
|
465
|
-
* @param {import('../src/link').AnyLink} cid
|
466
|
-
* @param {Uint8Array} bytes
|
467
|
-
*/
|
468
|
-
async put(cid, bytes) {
|
469
|
-
this.blocks.set(cid.toString(), bytes);
|
470
|
-
}
|
471
|
-
*entries() {
|
472
|
-
for (const [str, bytes] of this.blocks) {
|
473
|
-
yield { cid: parse(str), bytes };
|
474
|
-
}
|
475
|
-
}
|
476
|
-
}
|