hypercore 10.36.3 → 10.37.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/index.js CHANGED
@@ -18,7 +18,7 @@ const BlockEncryption = require('./lib/block-encryption')
18
18
  const Info = require('./lib/info')
19
19
  const Download = require('./lib/download')
20
20
  const Batch = require('./lib/batch')
21
- const { manifestHash, defaultSignerManifest, createManifest, isCompat, sign } = require('./lib/verifier')
21
+ const { manifestHash, createManifest } = require('./lib/verifier')
22
22
  const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
23
23
  const {
24
24
  ASSERTION,
@@ -85,7 +85,6 @@ module.exports = class Hypercore extends EventEmitter {
85
85
  this.closing = null
86
86
  this.opening = null
87
87
 
88
- this._clone = opts.clone || null
89
88
  this._readonly = opts.writable === false
90
89
  this._preappend = preappend.bind(this)
91
90
  this._snapshot = null
@@ -322,14 +321,6 @@ module.exports = class Hypercore extends EventEmitter {
322
321
  const s = this.sessions[i]
323
322
  if (s !== this) s._passCapabilities(this)
324
323
  }
325
-
326
- // copy state over
327
- if (this._clone) {
328
- const { from, signature } = this._clone
329
- await from.opening
330
- await this.core.copyFrom(from.core, signature)
331
- this._clone = null
332
- }
333
324
  }
334
325
  } else {
335
326
  ensureEncryption(this, opts)
@@ -501,46 +492,6 @@ module.exports = class Hypercore extends EventEmitter {
501
492
  this.emit('close', true)
502
493
  }
503
494
 
504
- clone (keyPair, storage, opts = {}) {
505
- // TODO: current limitation is no forking
506
- if ((opts.fork && opts.fork !== 0) || this.fork !== 0) {
507
- throw BAD_ARGUMENT('Cannot clone a fork')
508
- }
509
-
510
- const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
511
- const key = opts.key || (opts.compat !== false ? manifest.signers[0].publicKey : manifestHash(manifest))
512
-
513
- if (b4a.equals(key, this.key)) {
514
- throw BAD_ARGUMENT('Clone cannot share verification information')
515
- }
516
-
517
- const signature = opts.signature === undefined
518
- ? sign(createManifest(manifest), this.core.tree.batch(), keyPair, { compat: isCompat(key, manifest) })
519
- : opts.signature
520
-
521
- const sparse = opts.sparse === false ? false : this.sparse
522
- const wait = opts.wait === false ? false : this.wait
523
- const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
524
- const timeout = opts.timeout === undefined ? this.timeout : opts.timeout
525
-
526
- const Clz = this.constructor
527
-
528
- return new Clz(storage, key, {
529
- ...opts,
530
- keyPair,
531
- sparse,
532
- wait,
533
- onwait,
534
- timeout,
535
- manifest,
536
- overwrite: true,
537
- clone: {
538
- from: this,
539
- signature
540
- }
541
- })
542
- }
543
-
544
495
  replicate (isInitiator, opts = {}) {
545
496
  // Only limitation here is that ondiscoverykey doesn't work atm when passing a muxer directly,
546
497
  // because it doesn't really make a lot of sense.
package/lib/core.js CHANGED
@@ -253,7 +253,7 @@ module.exports = class Core {
253
253
  return false
254
254
  }
255
255
 
256
- async copyFrom (src, signature, { length = src.tree.length, sourceLength = src.tree.length, fork = src.tree.fork, additional = [] } = {}) {
256
+ async copyPrologue (src, { additional = [] } = {}) {
257
257
  await this._mutex.lock()
258
258
 
259
259
  try {
@@ -263,120 +263,103 @@ module.exports = class Core {
263
263
  throw err
264
264
  }
265
265
 
266
- const initialLength = this.tree.length
267
-
268
266
  try {
269
- const updates = []
270
-
271
- let pos = 0
272
- const copyLength = Math.min(src.tree.length, length)
273
-
274
- while (pos < copyLength) {
275
- const segmentStart = maximumSegmentStart(pos, src.bitfield, this.bitfield)
276
- if (segmentStart >= length || segmentStart < 0) break
267
+ const prologue = this.header.manifest && this.header.manifest.prologue
268
+ if (!prologue) throw INVALID_OPERATION('No prologue present')
277
269
 
278
- const segmentEnd = Math.min(src.tree.length, minimumSegmentEnd(segmentStart, src.bitfield, this.bitfield))
270
+ const srcLength = prologue.length - additional.length
271
+ const srcBatch = srcLength !== src.tree.length ? await src.tree.truncate(srcLength) : src.tree.batch()
272
+ const srcRoots = srcBatch.roots.slice(0)
273
+ const srcByteLength = srcBatch.byteLength
279
274
 
280
- const segment = []
275
+ for (const blk of additional) srcBatch.append(blk)
281
276
 
282
- pos = segmentStart
283
- while (pos < segmentEnd) {
284
- const val = await src.blocks.get(pos++)
285
- segment.push(val)
286
- }
277
+ if (!b4a.equals(srcBatch.hash(), prologue.hash)) throw INVALID_OPERATION('Source tree is conflicting')
287
278
 
288
- const [offset] = await src.tree.byteRange(2 * segmentStart)
289
- await this.blocks.putBatch(segmentStart, segment, offset)
279
+ // all hashes are correct, lets copy
290
280
 
291
- this._setBitfieldRange(segmentStart, segmentEnd - segmentStart, true)
292
-
293
- pos = segmentEnd + 1
294
-
295
- updates.push({
296
- drop: false,
297
- start: segmentStart,
298
- length: segmentEnd - segmentStart
299
- })
281
+ const entry = {
282
+ userData: null,
283
+ treeNodes: srcRoots,
284
+ treeUpgrade: null,
285
+ bitfield: null
300
286
  }
301
287
 
302
- for (let i = 0; i < copyLength * 2; i++) {
303
- const node = await src.tree.get(i, false)
304
- if (node === null) continue
305
-
306
- await this.tree.addNode(node)
288
+ if (additional.length) {
289
+ await this.blocks.putBatch(srcLength, additional, srcByteLength)
290
+ entry.treeNodes = entry.treeNodes.concat(srcBatch.nodes)
291
+ entry.bitfield = {
292
+ drop: false,
293
+ start: srcLength,
294
+ length: additional.length
295
+ }
307
296
  }
308
297
 
309
- await this.tree.flush()
298
+ await this.oplog.append([entry], false)
299
+ this.tree.addNodes(entry.treeNodes)
310
300
 
311
- this.tree.fork = fork
301
+ if (this.header.tree.length < srcBatch.length) {
302
+ this.header.tree.length = srcBatch.length
303
+ this.header.tree.rootHash = srcBatch.hash()
312
304
 
313
- let batch = this.tree.batch()
305
+ this.tree.length = srcBatch.length
306
+ this.tree.byteLength = srcBatch.byteLength
307
+ this.tree.roots = srcBatch.roots
308
+ this.onupdate(0b0001, null, null, null)
309
+ }
314
310
 
315
- // add additional blocks
316
- if (length > src.tree.length) {
317
- const missing = length - src.tree.length
318
- const offset = src.tree.length - sourceLength
311
+ if (entry.bitfield) {
312
+ this._setBitfieldRange(entry.bitfield.start, entry.bitfield.length, true)
313
+ this.onupdate(0, entry.bitfield, null, null)
314
+ }
319
315
 
320
- if (additional.length < missing + offset) {
321
- throw INVALID_OPERATION('Insufficient additional nodes were passed')
322
- }
316
+ await this._flushOplog()
323
317
 
324
- const source = src.tree.batch()
318
+ // no more additional blocks now and we should be consistant on disk
319
+ // copy over all existing segments...
325
320
 
326
- batch.roots = [...source.roots]
327
- batch.length = source.length
328
- batch.byteLength = source.byteLength
321
+ let segmentEnd = 0
329
322
 
330
- const blocks = additional.length === missing ? additional : additional.slice(offset, offset + missing)
323
+ while (segmentEnd < srcLength) {
324
+ const segmentStart = maximumSegmentStart(segmentEnd, src.bitfield, this.bitfield)
325
+ if (segmentStart >= srcLength || segmentStart < 0) break
331
326
 
332
- await this.blocks.putBatch(source.length, blocks, source.byteLength)
333
- this._setBitfieldRange(source.length, missing, true)
327
+ // max segment is 65536 to avoid running out of memory
328
+ segmentEnd = Math.min(segmentStart + 65536, srcLength, minimumSegmentEnd(segmentStart, src.bitfield, this.bitfield))
334
329
 
335
- updates.push({
330
+ const treeNodes = await src.tree.getNeededNodes(srcLength, segmentStart, segmentEnd)
331
+ const bitfield = {
336
332
  drop: false,
337
- start: source.length,
338
- length: missing
339
- })
340
-
341
- for (const block of blocks) await batch.append(block)
342
- } else {
343
- const source = length < src.tree.length ? await src.tree.truncate(length) : src.tree.batch()
333
+ start: segmentStart,
334
+ length: segmentEnd - segmentStart
335
+ }
344
336
 
345
- this.tree.roots = [...source.roots]
346
- this.tree.length = source.length
347
- this.tree.byteLength = source.byteLength
337
+ const segment = []
338
+ for (let i = segmentStart; i < segmentEnd; i++) {
339
+ const blk = await src.blocks.get(i)
340
+ segment.push(blk)
341
+ }
348
342
 
349
- // update batch
350
- batch = this.tree.batch()
351
- }
343
+ const offset = await src.tree.byteOffset(2 * segmentStart)
344
+ await this.blocks.putBatch(segmentStart, segment, offset)
352
345
 
353
- // verify if upgraded
354
- if (batch.length > initialLength) {
355
- try {
356
- batch.signature = signature
357
-
358
- this._verifyBatchUpgrade(batch, this.header.manifest)
359
- this.tree.signature = signature
360
- } catch (err) {
361
- this.tree.signature = null
362
- // TODO: how to handle signature failure?
363
- throw err
346
+ const entry = {
347
+ userData: null,
348
+ treeNodes,
349
+ treeUpgrade: null,
350
+ bitfield
364
351
  }
365
- }
366
352
 
367
- await batch.commit()
368
-
369
- this.header.tree.length = this.tree.length
370
- this.header.tree.fork = this.tree.fork
371
- this.header.tree.rootHash = this.tree.hash()
372
- this.header.tree.signature = this.tree.signature
353
+ await this.oplog.append([entry], false)
354
+ this.tree.addNodes(treeNodes)
355
+ this._setBitfieldRange(bitfield.start, bitfield.length, true)
356
+ this.onupdate(0, bitfield, null, null)
357
+ await this._flushOplog()
358
+ }
373
359
 
374
360
  this.header.userData = src.header.userData.slice(0)
375
- this.header.hints.contiguousLength = Math.min(src.header.hints.contiguousLength, this.header.tree.length)
376
-
377
- for (const bitfield of updates) {
378
- this.onupdate(0b0001, bitfield, null, null)
379
- }
361
+ const contig = Math.min(src.header.hints.contiguousLength, srcBatch.length)
362
+ if (this.header.hints.contiguousLength < contig) this.header.hints.contiguousLength = contig
380
363
 
381
364
  await this._flushOplog()
382
365
  } finally {
@@ -615,7 +598,7 @@ module.exports = class Core {
615
598
  }
616
599
  }
617
600
 
618
- await this.blocks.putBatch(treeLength, adding > values.length ? values.slice(0, adding) : values, byteOffset)
601
+ await this.blocks.putBatch(treeLength, adding < values.length ? values.slice(0, adding) : values, byteOffset)
619
602
  await this.oplog.append([entry], false)
620
603
 
621
604
  this._setBitfieldRange(entry.bitfield.start, entry.bitfield.length, true)
@@ -4,7 +4,7 @@ const c = require('compact-encoding')
4
4
  const Xache = require('xache')
5
5
  const b4a = require('b4a')
6
6
  const caps = require('./caps')
7
- const { INVALID_PROOF, INVALID_CHECKSUM, INVALID_OPERATION, BAD_ARGUMENT } = require('hypercore-errors')
7
+ const { INVALID_PROOF, INVALID_CHECKSUM, INVALID_OPERATION, BAD_ARGUMENT, ASSERTION } = require('hypercore-errors')
8
8
 
9
9
  const BLANK_HASH = b4a.alloc(32)
10
10
  const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
@@ -429,6 +429,37 @@ module.exports = class MerkleTree {
429
429
  this.prologue = { hash, length }
430
430
  }
431
431
 
432
+ addNodes (nodes) {
433
+ for (let i = 0; i < nodes.length; i++) {
434
+ const node = nodes[i]
435
+ this.unflushed.set(node.index, node)
436
+ }
437
+ }
438
+
439
+ getNeededNodes (length, start, end) {
440
+ const nodes = new Map()
441
+ const head = length * 2
442
+
443
+ for (let i = start; i < end; i++) {
444
+ const ite = flat.iterator(i * 2)
445
+
446
+ while (true) {
447
+ if (nodes.has(ite.index)) break
448
+ nodes.set(ite.index, this.get(ite.index, true))
449
+
450
+ const sibling = ite.sibling()
451
+
452
+ ite.parent()
453
+ if (ite.contains(head)) break
454
+
455
+ if (nodes.has(sibling)) break
456
+ nodes.set(sibling, this.get(sibling, true))
457
+ }
458
+ }
459
+
460
+ return Promise.all([...nodes.values()])
461
+ }
462
+
432
463
  async upgradeable (length) {
433
464
  const indexes = flat.fullRoots(2 * length)
434
465
  const roots = new Array(indexes.length)
@@ -767,6 +798,8 @@ async function getByteOffset (tree, index) {
767
798
 
768
799
  return offset
769
800
  }
801
+
802
+ throw ASSERTION('Failed to find offset')
770
803
  }
771
804
 
772
805
  // All the methods needed for proof verification
@@ -824,7 +857,7 @@ function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
824
857
 
825
858
  if (prologue) {
826
859
  const { start, length } = upgrade
827
- if (start < prologue.length && (start !== 0 || length !== prologue.length)) {
860
+ if (start < prologue.length && (start !== 0 || length < prologue.length)) {
828
861
  throw INVALID_PROOF('Upgrade does not satisfy prologue')
829
862
  }
830
863
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.36.3",
3
+ "version": "10.37.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {