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 +1 -50
- package/lib/core.js +72 -89
- package/lib/merkle-tree.js +35 -2
- package/package.json +1 -1
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
275
|
+
for (const blk of additional) srcBatch.append(blk)
|
|
281
276
|
|
|
282
|
-
|
|
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
|
-
|
|
289
|
-
await this.blocks.putBatch(segmentStart, segment, offset)
|
|
279
|
+
// all hashes are correct, lets copy
|
|
290
280
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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.
|
|
298
|
+
await this.oplog.append([entry], false)
|
|
299
|
+
this.tree.addNodes(entry.treeNodes)
|
|
310
300
|
|
|
311
|
-
this.tree.
|
|
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
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
321
|
-
throw INVALID_OPERATION('Insufficient additional nodes were passed')
|
|
322
|
-
}
|
|
316
|
+
await this._flushOplog()
|
|
323
317
|
|
|
324
|
-
|
|
318
|
+
// no more additional blocks now and we should be consistant on disk
|
|
319
|
+
// copy over all existing segments...
|
|
325
320
|
|
|
326
|
-
|
|
327
|
-
batch.length = source.length
|
|
328
|
-
batch.byteLength = source.byteLength
|
|
321
|
+
let segmentEnd = 0
|
|
329
322
|
|
|
330
|
-
|
|
323
|
+
while (segmentEnd < srcLength) {
|
|
324
|
+
const segmentStart = maximumSegmentStart(segmentEnd, src.bitfield, this.bitfield)
|
|
325
|
+
if (segmentStart >= srcLength || segmentStart < 0) break
|
|
331
326
|
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
330
|
+
const treeNodes = await src.tree.getNeededNodes(srcLength, segmentStart, segmentEnd)
|
|
331
|
+
const bitfield = {
|
|
336
332
|
drop: false,
|
|
337
|
-
start:
|
|
338
|
-
length:
|
|
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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
343
|
+
const offset = await src.tree.byteOffset(2 * segmentStart)
|
|
344
|
+
await this.blocks.putBatch(segmentStart, segment, offset)
|
|
352
345
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
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
|
|
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)
|
package/lib/merkle-tree.js
CHANGED
|
@@ -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
|
|
860
|
+
if (start < prologue.length && (start !== 0 || length < prologue.length)) {
|
|
828
861
|
throw INVALID_PROOF('Upgrade does not satisfy prologue')
|
|
829
862
|
}
|
|
830
863
|
}
|