@fireproof/core 0.7.3-dev.2 → 0.8.0-dev.2

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.
@@ -1,20 +1,5 @@
1
- // import { sha256 } from 'multiformats/hashes/sha2'
2
- import * as CBW from '@ipld/car/buffer-writer'
3
- import * as raw from 'multiformats/codecs/raw'
4
- // import * as Block from 'multiformats/block'
5
- // @ts-ignore
6
- // import { bf } from 'prolly-trees/utils'
7
- // @ts-ignore
8
- // import { nocache as cache } from 'prolly-trees/cache'
9
- import { encrypt, decrypt } from '../crypto.js'
10
- // import { Buffer } from 'buffer'
11
-
12
- // const chunker = bf(30)
13
-
14
1
  import randomBytes from 'randombytes'
15
2
  // import { randomBytes } from 'crypto'
16
- import { create, load } from 'ipld-hashmap'
17
- import { parse } from 'multiformats/link'
18
3
  import { CarReader } from '@ipld/car'
19
4
  import { CID } from 'multiformats/cid'
20
5
  import { sha256 } from 'multiformats/hashes/sha2'
@@ -28,6 +13,12 @@ import { Buffer } from 'buffer'
28
13
  import { rawSha1 as sha1sync } from '../sha1.js'
29
14
  // @ts-ignore
30
15
  import * as codec from '../encrypted-block.js'
16
+ import {
17
+ blocksToEncryptedCarBlock,
18
+ blocksToCarBlock,
19
+ blocksFromEncryptedCarBlock,
20
+ VMemoryBlockstore
21
+ } from './utils.js'
31
22
 
32
23
  const chunker = bf(30)
33
24
  const blockOpts = { cache, chunker, codec: dagcbor, hasher: sha256, compare }
@@ -36,13 +27,14 @@ const NO_ENCRYPT = typeof process !== 'undefined' && !!process.env?.NO_ENCRYPT
36
27
  const NOT_IMPL = true
37
28
 
38
29
  export class Base {
39
- valetRootCarCid = null // used on initial hydrate, if you change this, set this.valetCarCidMap = null
30
+ lastCar = null
31
+ carLog = []
40
32
  keyMaterial = null
41
33
  keyId = 'null'
42
34
  readonly = false
35
+ instanceId = Math.random().toString(36).slice(2)
43
36
 
44
37
  constructor (name, config = {}) {
45
- this.instanceId = Math.random().toString(36).slice(2)
46
38
  this.name = name
47
39
  this.config = config
48
40
 
@@ -52,23 +44,15 @@ export class Base {
52
44
  }
53
45
  }
54
46
 
55
- // console.log('this.config', this.instanceId, this.name, this.config)
56
- // if there is config.key and config.car,
57
- // then we could skip loading the headers if we want.
58
- // currently we don't do that, because we only use
59
- // the config for first run, and then we use the headers
60
- // once they exist
61
47
  this.ready = this.getHeaders().then(blocksReady => {
62
48
  // console.log('blocksReady base', this.name, blocksReady)
63
49
  return blocksReady
64
50
  })
65
51
  }
66
52
 
67
- setCarCidMapCarCid (carCid) {
68
- // console.trace('setCarCidMapCarCid', carCid)
69
- if (!carCid) return
70
- this.valetRootCarCid = parse(carCid)
71
- this.valetCarCidMap = null
53
+ setLastCar (car) {
54
+ this.lastCar = car
55
+ this.carLog.unshift(car)
72
56
  }
73
57
 
74
58
  setKeyMaterial (km) {
@@ -77,9 +61,7 @@ export class Base {
77
61
  this.keyMaterial = km
78
62
  const hash = sha1sync(hex)
79
63
  this.keyId = Buffer.from(hash).toString('hex')
80
- // console.log('setKeyMaterial', this.instanceId, this.name, km)
81
64
  } else {
82
- // console.log('setKeyMaterial', this.instanceId, this.name, km)
83
65
  this.keyMaterial = null
84
66
  this.keyId = 'null'
85
67
  }
@@ -93,24 +75,40 @@ export class Base {
93
75
  )
94
76
  }
95
77
  const cidMap = await this.getCidCarMap()
96
- const dataCids = [...cidMap.keys()]
78
+ const dataCids = new Set([...cidMap.keys()].filter(cid => cid[0] !== '_').map(cid => CID.parse(cid)))
97
79
  const allBlocks = new Map()
98
80
  for (const cid of dataCids) {
99
81
  const block = await this.getLoaderBlock(cid)
100
- allBlocks.set(cid, block)
82
+ allBlocks.set(cid, block.block)
101
83
  }
102
84
  cidMap.clear()
85
+ let lastCid = clock[0]
103
86
  const blocks = {
104
- lastCid: clock[0],
105
- get: cid => allBlocks.get(cid.toString())
87
+ head: clock,
88
+ lastCid,
89
+ get: cid => allBlocks.get(cid.toString()),
90
+ put: async (cid, block) => {
91
+ allBlocks.set(cid.toString(), block)
92
+ lastCid = cid.toString()
93
+ }
106
94
  }
107
- console.log('compact', this.instanceId, this.name, blocks.lastCid.toString(), dataCids.length)
108
- await this.parkCar(blocks, dataCids)
95
+ // const lastCompactCar =
96
+ await this.parkCar(blocks, dataCids) // todo this should remove old cars from the cid car map
97
+ // console.log('compact', this.name, lastCompactCar.cid, blocks.lastCid.toString(), dataCids.length)
98
+ // await this.setLastCompact(lastCompactCar.cid)
109
99
  }
110
100
 
111
101
  async parkCar (innerBlockstore, cids) {
112
102
  // console.log('parkCar', this.instanceId, this.name, this.readonly)
113
103
  if (this.readonly) return
104
+
105
+ // console.log('parkCar', this.name, this.carLog, innerBlockstore.head)
106
+
107
+ const value = { fp: { last: innerBlockstore.lastCid, clock: innerBlockstore.head, cars: this.carLog } }
108
+ const header = await Block.encode({ value, hasher: blockOpts.hasher, codec: blockOpts.codec })
109
+ await innerBlockstore.put(header.cid, header.bytes)
110
+ cids.add(header.cid.toString())
111
+
114
112
  let newCar
115
113
  if (this.keyMaterial) {
116
114
  // console.log('encrypting car', innerBlockstore.label)
@@ -119,34 +117,23 @@ export class Base {
119
117
  // todo should we pass cids in instead of iterating innerBlockstore?
120
118
  newCar = await blocksToCarBlock(innerBlockstore.lastCid, innerBlockstore)
121
119
  }
122
- // console.log('new car', newCar.cid.toString())
123
- return await this.saveCar(newCar.cid.toString(), newCar.bytes, cids)
120
+ return await this.saveCar(newCar.cid.toString(), newCar.bytes)
124
121
  }
125
122
 
126
- async saveCar (carCid, value, cids) {
127
- const newValetCidCar = await this.updateCarCidMap(carCid, cids)
128
- // console.log('writeCars', carCid.toString(), newValetCidCar.cid.toString())
129
- const carList = [
123
+ async saveCar (carCid, value) {
124
+ // add the car cid to our in memory car list
125
+ this.carLog.unshift(carCid)
126
+ this.lastCar = carCid
127
+ // console.log('saveCar', this.name, carCid, this.carLog.length)
128
+ await this.writeCars([
130
129
  {
131
130
  cid: carCid,
132
- bytes: value,
133
- replaces: null
134
- },
135
- {
136
- cid: newValetCidCar.cid,
137
- bytes: newValetCidCar.bytes,
138
- replaces: null
139
- // replaces: this.valetRootCarCid // todo
131
+ bytes: value
140
132
  }
141
- ]
142
-
143
- await this.writeCars(carList)
144
- this.valetRootCarCid = newValetCidCar.cid
145
- // console.trace('saved car', this.instanceId, this.name, newValetCidCar.cid.toString())
146
- return newValetCidCar
133
+ ])
147
134
  }
148
135
 
149
- applyHeaders (headers) {
136
+ async applyHeaders (headers) {
150
137
  // console.log('applyHeaders', headers.index)
151
138
  this.headers = headers
152
139
  // console.log('before applied', this.instanceId, this.name, this.keyMaterial, this.valetRootCarCid)
@@ -154,12 +141,20 @@ export class Base {
154
141
  if (header) {
155
142
  // console.log('applyHeaders', this.instanceId, this.name, header.key, header.car)
156
143
  header.key && this.setKeyMaterial(header.key)
157
- this.setCarCidMapCarCid(header.car)
144
+ // this.setCarCidMapCarCid(header.car) // instead we should just extract the list of cars from the car
145
+ const carHeader = await this.readHeaderCar(header.car)
146
+ this.carLog = carHeader.cars
147
+ // console.log('stored carHeader', this.name, this.config.type, this.carLog)
148
+
149
+ // this.lastCar = header.car // ?
150
+ if (header.car) {
151
+ // console.log('header.car', header.car, this.blocks.valet.primary.carLog)
152
+ this.setLastCar(header.car)
153
+ }
154
+ header.clock = carHeader.clock.map(c => c.toString())
158
155
  }
159
156
  }
160
- if (!this.valetRootCarCid) {
161
- this.setCarCidMapCarCid(this.config.car)
162
- }
157
+
163
158
  if (!this.keyMaterial) {
164
159
  const nullKey = this.config.key === null
165
160
  if (nullKey || this.config.key) {
@@ -175,33 +170,34 @@ export class Base {
175
170
  const headers = {}
176
171
  for (const [branch] of Object.entries(this.config.branches)) {
177
172
  const got = await this.loadHeader(branch)
178
- // console.log('getHeaders', this.name, branch, got)
179
173
  headers[branch] = got
180
174
  }
181
- this.applyHeaders(headers)
175
+ await this.applyHeaders(headers)
182
176
  return headers
183
177
  }
184
178
 
185
179
  loadHeader (branch = 'main') {
186
- throw new Error('not implemented')
180
+ if (NOT_IMPL) throw new Error('not implemented')
181
+ return {}
187
182
  }
188
183
 
189
184
  async saveHeader (header) {
185
+ // this.clock = header.clock
190
186
  // for each branch, save the header
191
- // console.log('saveHeader', this.config.branches, header)
187
+ // console.log('saveHeader', this.config.branches)
192
188
  // for (const branch of this.branches) {
193
189
  // await this.saveBranchHeader(branch)
194
190
  // }
195
191
  for (const [branch, { readonly }] of Object.entries(this.config.branches)) {
196
192
  if (readonly) continue
197
193
  // console.log('saveHeader', this.instanceId, this.name, branch, header)
198
- await this.writeHeader(branch, header)
194
+ await this.writeHeader(branch, this.prepareHeader(header))
199
195
  }
200
196
  }
201
197
 
202
198
  prepareHeader (header, json = true) {
203
199
  header.key = this.keyMaterial
204
- header.car = this.valetRootCarCid.toString()
200
+ header.car = this.lastCar?.toString()
205
201
  // console.log('prepareHeader', this.instanceId, this.name, header)
206
202
  return json ? JSON.stringify(header) : header
207
203
  }
@@ -210,13 +206,35 @@ export class Base {
210
206
  throw new Error('not implemented')
211
207
  }
212
208
 
213
- async getCarCIDForCID (cid) {
214
- const cidMap = await this.getCidCarMap()
215
- const carCid = cidMap.get(cid.toString())
216
- if (carCid) {
217
- return { result: carCid }
209
+ async getCarCIDForCID (cid) { // todo combine with getLoaderBlock for one fetch not many
210
+ // console.log('getCarCIDForCID', cid, this.carLog, this.config.type)
211
+ // for each car in the car log
212
+ for (const carCid of this.carLog) {
213
+ const reader = await this.getCarReader(carCid)
214
+ // console.log('getCarCIDForCID', carCid, cid)
215
+ // if (reader.has(cid)) {
216
+ // console.log('getCarCIDForCID found', cid)
217
+ // return { result: carCid }
218
+ // }
219
+
220
+ for await (const block of reader.entries()) {
221
+ // console.log('getCarCIDForCID', cid, block.cid.toString())
222
+ // console.log('getCarCIDForCID', cid, block.cid.toString())
223
+ if (block.cid.toString() === cid.toString()) {
224
+ // console.log('getCarCIDForCID found', cid)
225
+ return { result: carCid }
226
+ }
227
+ }
218
228
  }
229
+ // console.log('getCarCIDForCID not found', cid, this.config.type)
219
230
  return { result: null }
231
+ // return this.carLog[0]
232
+ // const cidMap = await this.getCidCarMap()
233
+ // const carCid = cidMap.get(cid.toString())
234
+ // if (carCid) {
235
+ // return { result: carCid }
236
+ // }
237
+ // return { result: null }
220
238
  }
221
239
 
222
240
  async readCar (carCid) {
@@ -225,62 +243,81 @@ export class Base {
225
243
  }
226
244
 
227
245
  async getLoaderBlock (dataCID) {
246
+ // console.log('getLoaderBlock', dataCID, this.config, this.carLog)
228
247
  const { result: carCid } = await this.getCarCIDForCID(dataCID)
248
+ // console.log('gotLoaderBlock', dataCID, carCid)
229
249
  if (!carCid) {
230
250
  throw new Error('Missing car for: ' + dataCID)
231
251
  }
232
- // console.log('getLoaderBlock', dataCID, carCid)
233
252
  const reader = await this.getCarReader(carCid)
234
- return { block: await reader.get(dataCID), reader, carCid }
235
- }
253
+ const block = await reader.get(dataCID)
254
+ // console.log('gotLoaderBlock', dataCID, block.length)
255
+ return { block, reader, carCid }
256
+ }
257
+
258
+ // async getLastSynced () {
259
+ // const metadata = await this.getCidCarMap()
260
+ // if (metadata.has('_last_sync_head')) {
261
+ // return JSON.parse(metadata.get('_last_sync_head'))
262
+ // } else {
263
+ // return []
264
+ // }
265
+ // }
266
+
267
+ // async setLastSynced (lastSynced) {
268
+ // const metadata = await this.getCidCarMap()
269
+ // metadata.set('_last_sync_head', JSON.stringify(lastSynced))
270
+ // // await this.writeMetadata(metadata)
271
+ // }
272
+
273
+ // async getCompactSince (sinceHead) {
274
+ // // get the car for the head
275
+ // // find the location of the car in the metadata car sequence
276
+ // }
236
277
 
237
278
  /** Private - internal **/
238
279
 
239
280
  async getCidCarMap () {
240
- // console.log('getCidCarMap', this.constructor.name, this.name, this.valetRootCarCid, typeof this.valetCarCidMap)
241
281
  if (this.valetCarCidMap) return this.valetCarCidMap
242
- if (this.valetRootCarCid) {
243
- this.valetCarCidMap = await this.mapForIPLDHashmapCarCid(this.valetRootCarCid)
244
- return this.valetCarCidMap
245
- } else {
246
- this.valetCarCidMap = new Map()
247
- return this.valetCarCidMap
248
- }
282
+ this.valetCarCidMap = new Map()
283
+ return this.valetCarCidMap
249
284
  }
250
285
 
251
- async mapForIPLDHashmapCarCid (carCid) {
252
- // console.log('mapForIPLDHashmapCarCid', carCid)
253
- // todo why is this writeable?
254
- const carMapReader = await this.getWriteableCarReader(carCid)
255
- const indexNode = await load(carMapReader, carMapReader.root.cid, {
256
- blockHasher: blockOpts.hasher,
257
- blockCodec: blockOpts.codec
258
- })
259
- const theCarMap = new Map()
260
- for await (const [key, value] of indexNode.entries()) {
261
- // console.log('mapForIPLDHashmapCarCid', key, value)
262
- theCarMap.set(key, value)
263
- }
264
- return theCarMap
265
- }
266
-
267
- async getWriteableCarReader (carCid) {
268
- // console.log('getWriteableCarReader', carCid)
286
+ async readHeaderCar (carCid) {
269
287
  const carMapReader = await this.getCarReader(carCid)
288
+ // await this.getWriteableCarReader(carCid)
289
+ // console.log('readHeaderCar', carCid, carMapReader)
290
+ // now when we load the root cid from the car, we get our new custom root node
291
+ const bytes = await carMapReader.get(carMapReader.root.cid)
292
+ const decoded = await Block.decode({ bytes, hasher: blockOpts.hasher, codec: blockOpts.codec })
293
+ // @ts-ignore
294
+ const { fp: { cars, clock } } = decoded.value
295
+ return { cars, clock, reader: carMapReader }
296
+ }
297
+
298
+ // todo this is only because parkCar wants a writable reader to put the metadata block
299
+ // parkCar should handle it's own writeable wrapper, and it should love to be called with
300
+ // a read only car reader
301
+ async getWriteableCarReader (carReader) {
302
+ // console.log('getWriteableCarReader')
303
+ // const carReader = await this.getCarReader(carCid)
304
+ // console.log('getWriteableCarReader', carCid, carReader)
270
305
  const theseWriteableBlocks = new VMemoryBlockstore()
271
306
  const combinedReader = {
272
307
  blocks: theseWriteableBlocks,
273
- root: carMapReader?.root,
308
+ root: carReader?.root,
274
309
  put: async (cid, bytes) => {
275
310
  return await theseWriteableBlocks.put(cid, bytes)
276
311
  },
277
312
  get: async cid => {
313
+ // console.log('getWriteableCarReader get', cid)
278
314
  try {
279
315
  const got = await theseWriteableBlocks.get(cid)
280
316
  return got.bytes
281
317
  } catch (e) {
282
- if (!carMapReader) throw e
283
- const bytes = await carMapReader.get(cid)
318
+ if (!carReader) throw e
319
+ const bytes = await carReader.get(cid)
320
+ // console.log('getWriteableCarReader', cid, bytes)
284
321
  await theseWriteableBlocks.put(cid, bytes)
285
322
  return bytes
286
323
  }
@@ -289,10 +326,6 @@ export class Base {
289
326
  return combinedReader
290
327
  }
291
328
 
292
- async xgetCarReader (carCid) {
293
- return this.getCarReaderImpl(carCid)
294
- }
295
-
296
329
  carReaderCache = new Map()
297
330
  async getCarReader (carCid) {
298
331
  if (!this.carReaderCache.has(carCid)) {
@@ -306,6 +339,7 @@ export class Base {
306
339
  const carBytes = await this.readCar(carCid)
307
340
  // console.log('getCarReader', this.constructor.name, carCid, carBytes.length)
308
341
  const reader = await CarReader.fromBytes(carBytes)
342
+ // console.log('getCarReader', carCid, reader._header)
309
343
  if (this.keyMaterial) {
310
344
  const roots = await reader.getRoots()
311
345
  const readerGetWithCodec = async cid => {
@@ -365,176 +399,61 @@ export class Base {
365
399
 
366
400
  writeCars (cars) {}
367
401
 
368
- async updateCarCidMap (carCid, cids) {
402
+ // sequenceCarMapAppend (theCarMap, carCid) {
403
+ // // _last_compact
404
+ // // _last_sync (map per clock? you can find this by looking at a clocks car and it's position in the map)
405
+ // const oldMark = theCarMap.get('_last_compact') // todo we can track _next_seq directly
406
+ // // console.log('sequenceCarMapAppend oldMark', oldMark)
407
+ // const lastCompact = oldMark ? charwise.decode(oldMark) : 0
408
+ // // start iterating from the last compact point and find the first missing entry.
409
+ // // then write the carCid to that entry
410
+ // let next = lastCompact
411
+ // while (true) {
412
+ // const key = `_${charwise.encode(next)}`
413
+ // if (!theCarMap.has(key)) {
414
+ // console.log('sequenceCarMapAppend', next, key, carCid)
415
+ // theCarMap.set(key, carCid.toString())
416
+ // break
417
+ // } else {
418
+ // // const got = theCarMap.get(key)
419
+ // next++
420
+ // }
421
+ // }
422
+ // }
423
+
424
+ // async setLastCompact (lastCompactCarCid) {
425
+ // console.log('setLastCompact', lastCompactCarCid)
426
+ // const theCarMap = await this.getCidCarMap()
427
+ // const oldMark = theCarMap.get('_last_compact')
428
+ // const lastCompact = oldMark ? charwise.decode(oldMark) : 0
429
+
430
+ // let next = lastCompact
431
+ // while (true) {
432
+ // const key = `_${charwise.encode(next)}`
433
+ // if (!theCarMap.has(key)) {
434
+ // if (next === 0) {
435
+ // theCarMap.set('_last_compact', charwise.encode(next))
436
+ // break
437
+ // } else {
438
+ // throw new Error(`last compact point not found ${next} ${key}`)
439
+ // }
440
+ // } else {
441
+ // const got = theCarMap.get(key)
442
+ // // console.log('setLastCompact', key, got)
443
+ // if (got === lastCompactCarCid) {
444
+ // theCarMap.set('_last_compact', charwise.encode(next))
445
+ // break
446
+ // }
447
+ // next++
448
+ // }
449
+ // }
450
+ // }
451
+
452
+ async updateCarCidMap (dataCarCid, cids, head) {
369
453
  // this hydrates the map if it has not been hydrated
370
454
  const theCarMap = await this.getCidCarMap()
371
455
  for (const cid of cids) {
372
- theCarMap.set(cid, carCid)
456
+ theCarMap.set(cid, dataCarCid)
373
457
  }
374
- // todo can we debounce this? -- maybe put it into a queue so we can batch it
375
- return await this.persistCarMap(theCarMap)
376
- }
377
-
378
- async persistCarMap (theCarMap) {
379
- const ipldLoader = await getEmptyLoader()
380
- const indexNode = await create(ipldLoader, {
381
- bitWidth: 4,
382
- bucketSize: 2,
383
- blockHasher: blockOpts.hasher,
384
- blockCodec: blockOpts.codec
385
- })
386
-
387
- for (const [key, value] of theCarMap.entries()) {
388
- await indexNode.set(key, value)
389
- }
390
-
391
- let newValetCidCar
392
- if (this.keyMaterial) {
393
- const cids = [...ipldLoader.blocks.blocks.keys()]
394
- // console.log('persistCarMap', cids)
395
- newValetCidCar = await blocksToEncryptedCarBlock(indexNode.cid, ipldLoader.blocks, this.keyMaterial, cids)
396
- } else {
397
- newValetCidCar = await blocksToCarBlock(indexNode.cid, ipldLoader.blocks)
398
- }
399
- return newValetCidCar
400
- }
401
- }
402
-
403
- async function getEmptyLoader () {
404
- const theseWriteableBlocks = new VMemoryBlockstore()
405
- return {
406
- blocks: theseWriteableBlocks,
407
- put: async (cid, bytes) => {
408
- return await theseWriteableBlocks.put(cid, bytes)
409
- },
410
- get: async cid => {
411
- const got = await theseWriteableBlocks.get(cid)
412
- return got.bytes
413
- }
414
- }
415
- }
416
-
417
- export class VMemoryBlockstore {
418
- /** @type {Map<string, Uint8Array>} */
419
- blocks = new Map()
420
- instanceId = Math.random().toString(36).slice(2)
421
-
422
- async get (cid) {
423
- const bytes = this.blocks.get(cid.toString())
424
- if (!bytes) throw new Error('block not found ' + cid.toString())
425
- return { cid, bytes }
426
- }
427
-
428
- /**
429
- * @param {any} cid
430
- * @param {Uint8Array} bytes
431
- */
432
- async put (cid, bytes) {
433
- this.blocks.set(cid.toString(), bytes)
434
- }
435
-
436
- * entries () {
437
- for (const [str, bytes] of this.blocks) {
438
- yield { cid: parse(str), bytes }
439
- }
440
- }
441
- }
442
-
443
- export const blocksToCarBlock = async (rootCids, blocks) => {
444
- // console.log('blocksToCarBlock', rootCids, blocks.constructor.name)
445
- let size = 0
446
- if (!Array.isArray(rootCids)) {
447
- rootCids = [rootCids]
448
- }
449
- const headerSize = CBW.headerLength({ roots: rootCids })
450
- size += headerSize
451
- if (!Array.isArray(blocks)) {
452
- blocks = Array.from(blocks.entries())
453
- }
454
- for (const { cid, bytes } of blocks) {
455
- // console.log(cid, bytes)
456
- size += CBW.blockLength({ cid, bytes })
457
- }
458
- const buffer = new Uint8Array(size)
459
- const writer = await CBW.createWriter(buffer, { headerSize })
460
-
461
- for (const cid of rootCids) {
462
- writer.addRoot(cid)
463
- }
464
-
465
- for (const { cid, bytes } of blocks) {
466
- writer.write({ cid, bytes })
467
- }
468
- await writer.close()
469
- return await Block.encode({ value: writer.bytes, hasher: sha256, codec: raw })
470
- }
471
-
472
- export const blocksToEncryptedCarBlock = async (innerBlockStoreClockRootCid, blocks, keyMaterial, cids) => {
473
- const encryptionKey = Buffer.from(keyMaterial, 'hex')
474
- const encryptedBlocks = []
475
- const theCids = cids
476
- // console.trace('blocksToEncryptedCarBlock', blocks)
477
- // for (const { cid } of blocks.entries()) {
478
- // theCids.push(cid.toString())
479
- // }
480
- // console.log(
481
- // 'encrypting',
482
- // theCids.length,
483
- // 'blocks',
484
- // theCids.includes(innerBlockStoreClockRootCid.toString()),
485
- // keyMaterial
486
- // )
487
- // console.log('cids', theCids, innerBlockStoreClockRootCid.toString())
488
- let last
489
- for await (const block of encrypt({
490
- cids: theCids,
491
- get: async cid => {
492
- // console.log('getencrypt', cid)
493
- const got = blocks.get(cid)
494
- // console.log('got', got)
495
- return got.block ? ({ cid, bytes: got.block }) : got
496
- }, // maybe we can just use blocks.get
497
- key: encryptionKey,
498
- hasher: sha256,
499
- chunker,
500
- cache,
501
- // codec: dagcbor, // should be crypto?
502
- root: innerBlockStoreClockRootCid
503
- })) {
504
- encryptedBlocks.push(block)
505
- last = block
506
- }
507
- // console.log('last', last.cid.toString(), 'for clock', innerBlockStoreClockRootCid.toString())
508
- const encryptedCar = await blocksToCarBlock(last.cid, encryptedBlocks)
509
- return encryptedCar
510
- }
511
- // { root, get, key, cache, chunker, hasher }
512
-
513
- const memoizeDecryptedCarBlocks = new Map()
514
- export const blocksFromEncryptedCarBlock = async (cid, get, keyMaterial) => {
515
- if (memoizeDecryptedCarBlocks.has(cid.toString())) {
516
- return memoizeDecryptedCarBlocks.get(cid.toString())
517
- } else {
518
- const blocksPromise = (async () => {
519
- const decryptionKey = Buffer.from(keyMaterial, 'hex')
520
- // console.log('decrypting', keyMaterial, cid.toString())
521
- const cids = new Set()
522
- const decryptedBlocks = []
523
- for await (const block of decrypt({
524
- root: cid,
525
- get,
526
- key: decryptionKey,
527
- chunker,
528
- hasher: sha256,
529
- cache
530
- // codec: dagcbor
531
- })) {
532
- decryptedBlocks.push(block)
533
- cids.add(block.cid.toString())
534
- }
535
- return { blocks: decryptedBlocks, cids }
536
- })()
537
- memoizeDecryptedCarBlocks.set(cid.toString(), blocksPromise)
538
- return blocksPromise
539
458
  }
540
459
  }
@@ -57,7 +57,7 @@ export class Browser extends Base {
57
57
  async writeHeader (branch, header) {
58
58
  if (this.config.readonly) return
59
59
  try {
60
- return localStorage.setItem(this.headerKey(branch), this.prepareHeader(header))
60
+ return localStorage.setItem(this.headerKey(branch), header)
61
61
  } catch (e) {}
62
62
  }
63
63
 
@@ -45,11 +45,10 @@ export class Filesystem extends Base {
45
45
  }
46
46
 
47
47
  async writeHeader (branch, header) {
48
- // console.log('saveHeader', this.isBrowser)
48
+ // console.log('saveHeader fs', header)
49
49
  if (this.config.readonly) return
50
- const pHeader = this.prepareHeader(header)
51
50
  // console.log('writeHeader fs', branch, pHeader)
52
- await writeSync(this.headerFilename(branch), pHeader)
51
+ await writeSync(this.headerFilename(branch), header)
53
52
  }
54
53
 
55
54
  headerFilename (branch = 'main') {
@@ -47,12 +47,11 @@ export class Rest extends Base {
47
47
 
48
48
  async writeHeader (branch, header) {
49
49
  if (this.config.readonly) return
50
- const pHeader = this.prepareHeader(header)
51
50
  // console.log('writeHeader rt', branch, pHeader)
52
51
 
53
52
  const response = await fetch(this.headerURL(branch), {
54
53
  method: 'PUT',
55
- body: pHeader,
54
+ body: header,
56
55
  headers: { 'Content-Type': 'application/json' }
57
56
  })
58
57
  if (!response.ok) throw new Error(`An error occurred: ${response.statusText}`)