hypercore 10.0.0-alpha.5 → 10.0.0-alpha.52
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 +68 -5
- package/index.js +594 -173
- package/lib/bitfield.js +9 -5
- package/lib/block-encryption.js +68 -0
- package/lib/block-store.js +3 -1
- package/lib/caps.js +32 -0
- package/lib/core.js +88 -27
- package/lib/errors.js +50 -0
- package/lib/merkle-tree.js +201 -118
- package/lib/messages.js +249 -168
- package/lib/oplog.js +4 -3
- package/lib/replicator.js +1385 -616
- package/lib/streams.js +56 -0
- package/package.json +19 -10
- package/.github/workflows/test-node.yml +0 -23
- package/CHANGELOG.md +0 -37
- package/UPGRADE.md +0 -9
- package/examples/announce.js +0 -19
- package/examples/basic.js +0 -10
- package/examples/http.js +0 -123
- package/examples/lookup.js +0 -20
- package/lib/extensions.js +0 -76
- package/lib/protocol.js +0 -524
- package/lib/random-iterator.js +0 -46
- package/test/basic.js +0 -78
- package/test/bitfield.js +0 -71
- package/test/core.js +0 -290
- package/test/encodings.js +0 -18
- package/test/extension.js +0 -71
- package/test/helpers/index.js +0 -23
- package/test/merkle-tree.js +0 -518
- package/test/mutex.js +0 -137
- package/test/oplog.js +0 -399
- package/test/preload.js +0 -72
- package/test/replicate.js +0 -333
- package/test/sessions.js +0 -173
- package/test/user-data.js +0 -47
package/lib/merkle-tree.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
const flat = require('flat-tree')
|
|
2
2
|
const crypto = require('hypercore-crypto')
|
|
3
|
-
const
|
|
3
|
+
const c = require('compact-encoding')
|
|
4
|
+
const Xache = require('xache')
|
|
5
|
+
const b4a = require('b4a')
|
|
6
|
+
const caps = require('./caps')
|
|
4
7
|
|
|
5
|
-
const BLANK_HASH =
|
|
6
|
-
const OLD_TREE =
|
|
8
|
+
const BLANK_HASH = b4a.alloc(32)
|
|
9
|
+
const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
|
|
10
|
+
const TREE_CACHE = 128 // speeds up linear scans by A LOT
|
|
7
11
|
|
|
8
12
|
class NodeQueue {
|
|
9
13
|
constructor (nodes, extra = null) {
|
|
@@ -56,11 +60,11 @@ class MerkleTreeBatch {
|
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
signable (hash = this.hash()) {
|
|
59
|
-
return
|
|
63
|
+
return caps.treeSignable(hash, this.length, this.fork)
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
return
|
|
66
|
+
signableLegacy (hash = this.hash()) {
|
|
67
|
+
return caps.treeSignableLegacy(hash, this.length, this.fork)
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
append (buf) {
|
|
@@ -138,6 +142,7 @@ class MerkleTreeBatch {
|
|
|
138
142
|
: this.ancestors
|
|
139
143
|
|
|
140
144
|
this.tree.truncated = true
|
|
145
|
+
this.tree.cache = new Xache({ maxSize: this.tree.cache.maxSize })
|
|
141
146
|
truncateMap(this.tree.unflushed, this.ancestors)
|
|
142
147
|
if (this.tree.flushing !== null) truncateMap(this.tree.flushing, this.ancestors)
|
|
143
148
|
}
|
|
@@ -212,9 +217,9 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
212
217
|
if (this.want === null) return true
|
|
213
218
|
|
|
214
219
|
const nodes = []
|
|
215
|
-
const root =
|
|
220
|
+
const root = verifyTree(proof, this.tree.crypto, nodes)
|
|
216
221
|
|
|
217
|
-
if (root === null || !root.hash
|
|
222
|
+
if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false
|
|
218
223
|
|
|
219
224
|
this.nodes.push(...nodes)
|
|
220
225
|
return this._update(nodes)
|
|
@@ -226,13 +231,14 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
226
231
|
|
|
227
232
|
let diff = null
|
|
228
233
|
const ite = flat.iterator(this.diff.index)
|
|
234
|
+
const startingDiff = this.diff
|
|
229
235
|
|
|
230
236
|
while ((ite.index & 1) !== 0) {
|
|
231
237
|
const left = n.get(ite.leftChild())
|
|
232
238
|
if (!left) break
|
|
233
239
|
|
|
234
240
|
const existing = await this.tree.get(left.index, false)
|
|
235
|
-
if (!existing || !existing.hash
|
|
241
|
+
if (!existing || !b4a.equals(existing.hash, left.hash)) {
|
|
236
242
|
diff = left
|
|
237
243
|
} else {
|
|
238
244
|
diff = n.get(ite.sibling())
|
|
@@ -241,20 +247,19 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
241
247
|
|
|
242
248
|
if ((this.diff.index & 1) === 0) return true
|
|
243
249
|
if (diff === null) return false
|
|
250
|
+
if (startingDiff !== this.diff) return false
|
|
244
251
|
|
|
245
252
|
return this._updateDiffRoot(diff)
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
_updateDiffRoot (diff) {
|
|
256
|
+
if (this.want === null) return true
|
|
257
|
+
|
|
249
258
|
const spans = flat.spans(diff.index)
|
|
250
259
|
const start = spans[0] / 2
|
|
251
260
|
const end = Math.min(this.treeLength, spans[1] / 2 + 1)
|
|
252
261
|
const len = end - start
|
|
253
262
|
|
|
254
|
-
if (this.diff !== null && len >= this.want.end - this.want.start) {
|
|
255
|
-
return false
|
|
256
|
-
}
|
|
257
|
-
|
|
258
263
|
this.ancestors = start
|
|
259
264
|
this.diff = diff
|
|
260
265
|
|
|
@@ -272,11 +277,15 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
class ByteSeeker {
|
|
275
|
-
constructor (tree, bytes) {
|
|
280
|
+
constructor (tree, bytes, padding = 0) {
|
|
276
281
|
this.tree = tree
|
|
277
282
|
this.bytes = bytes
|
|
278
|
-
this.
|
|
279
|
-
|
|
283
|
+
this.padding = padding
|
|
284
|
+
|
|
285
|
+
const size = tree.byteLength - (tree.length * padding)
|
|
286
|
+
|
|
287
|
+
this.start = bytes >= size ? tree.length : 0
|
|
288
|
+
this.end = bytes < size ? tree.length : 0
|
|
280
289
|
}
|
|
281
290
|
|
|
282
291
|
nodes () {
|
|
@@ -287,12 +296,12 @@ class ByteSeeker {
|
|
|
287
296
|
if (!bytes) return [0, 0]
|
|
288
297
|
|
|
289
298
|
for (const node of this.tree.roots) { // all async ticks happen once we find the root so safe
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
299
|
+
let size = node.size
|
|
300
|
+
if (this.padding > 0) size -= this.padding * flat.countLeaves(node.index)
|
|
293
301
|
|
|
294
|
-
if (bytes
|
|
295
|
-
|
|
302
|
+
if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]
|
|
303
|
+
if (bytes > size) {
|
|
304
|
+
bytes -= size
|
|
296
305
|
continue
|
|
297
306
|
}
|
|
298
307
|
|
|
@@ -301,9 +310,12 @@ class ByteSeeker {
|
|
|
301
310
|
while ((ite.index & 1) !== 0) {
|
|
302
311
|
const l = await this.tree.get(ite.leftChild(), false)
|
|
303
312
|
if (l) {
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
|
|
313
|
+
let size = l.size
|
|
314
|
+
if (this.padding > 0) size -= this.padding * ite.countLeaves()
|
|
315
|
+
|
|
316
|
+
if (size === bytes) return [ite.rightSpan() + 2, 0]
|
|
317
|
+
if (size > bytes) continue
|
|
318
|
+
bytes -= size
|
|
307
319
|
ite.sibling()
|
|
308
320
|
} else {
|
|
309
321
|
ite.parent()
|
|
@@ -341,13 +353,14 @@ module.exports = class MerkleTree {
|
|
|
341
353
|
|
|
342
354
|
this.storage = storage
|
|
343
355
|
this.unflushed = new Map()
|
|
356
|
+
this.cache = new Xache({ maxSize: TREE_CACHE })
|
|
344
357
|
this.flushing = null
|
|
345
358
|
this.truncated = false
|
|
346
359
|
this.truncateTo = 0
|
|
347
360
|
}
|
|
348
361
|
|
|
349
362
|
addNode (node) {
|
|
350
|
-
if (node.size === 0 && node.hash
|
|
363
|
+
if (node.size === 0 && b4a.equals(node.hash, BLANK_HASH)) node = blankNode(node.index)
|
|
351
364
|
this.unflushed.set(node.index, node)
|
|
352
365
|
}
|
|
353
366
|
|
|
@@ -355,8 +368,8 @@ module.exports = class MerkleTree {
|
|
|
355
368
|
return new MerkleTreeBatch(this)
|
|
356
369
|
}
|
|
357
370
|
|
|
358
|
-
seek (bytes) {
|
|
359
|
-
return new ByteSeeker(this, bytes)
|
|
371
|
+
seek (bytes, padding) {
|
|
372
|
+
return new ByteSeeker(this, bytes, padding)
|
|
360
373
|
}
|
|
361
374
|
|
|
362
375
|
hash () {
|
|
@@ -364,14 +377,39 @@ module.exports = class MerkleTree {
|
|
|
364
377
|
}
|
|
365
378
|
|
|
366
379
|
signable (hash = this.hash()) {
|
|
367
|
-
return
|
|
380
|
+
return caps.treeSignable(hash, this.length, this.fork)
|
|
368
381
|
}
|
|
369
382
|
|
|
370
|
-
|
|
371
|
-
|
|
383
|
+
getRoots (length) {
|
|
384
|
+
const indexes = flat.fullRoots(2 * length)
|
|
385
|
+
const roots = new Array(indexes.length)
|
|
386
|
+
|
|
387
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
388
|
+
roots[i] = this.get(indexes[i], true)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return Promise.all(roots)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async upgradeable (length) {
|
|
395
|
+
const indexes = flat.fullRoots(2 * length)
|
|
396
|
+
const roots = new Array(indexes.length)
|
|
397
|
+
|
|
398
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
399
|
+
roots[i] = this.get(indexes[i], false)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (const node of await Promise.all(roots)) {
|
|
403
|
+
if (node === null) return false
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return true
|
|
372
407
|
}
|
|
373
408
|
|
|
374
409
|
get (index, error = true) {
|
|
410
|
+
const c = this.cache.get(index)
|
|
411
|
+
if (c) return c
|
|
412
|
+
|
|
375
413
|
let node = this.unflushed.get(index)
|
|
376
414
|
|
|
377
415
|
if (this.flushing !== null && node === undefined) {
|
|
@@ -391,7 +429,7 @@ module.exports = class MerkleTree {
|
|
|
391
429
|
return Promise.resolve(node)
|
|
392
430
|
}
|
|
393
431
|
|
|
394
|
-
return getStoredNode(this.storage, index, error)
|
|
432
|
+
return getStoredNode(this.storage, index, this.cache, error)
|
|
395
433
|
}
|
|
396
434
|
|
|
397
435
|
async flush () {
|
|
@@ -433,17 +471,23 @@ module.exports = class MerkleTree {
|
|
|
433
471
|
// TODO: write neighbors together etc etc
|
|
434
472
|
// TODO: bench loading a full disk page and copy to that instead
|
|
435
473
|
return new Promise((resolve, reject) => {
|
|
436
|
-
const slab =
|
|
474
|
+
const slab = b4a.allocUnsafe(40 * this.flushing.size)
|
|
437
475
|
|
|
438
476
|
let error = null
|
|
439
477
|
let missing = this.flushing.size + 1
|
|
440
478
|
let offset = 0
|
|
441
479
|
|
|
442
480
|
for (const node of this.flushing.values()) {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
481
|
+
const state = {
|
|
482
|
+
start: 0,
|
|
483
|
+
end: 40,
|
|
484
|
+
buffer: slab.subarray(offset, offset += 40)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
c.uint64.encode(state, node.size)
|
|
488
|
+
c.raw.encode(state, node.hash)
|
|
489
|
+
|
|
490
|
+
this.storage.write(node.index * 40, state.buffer, done)
|
|
447
491
|
}
|
|
448
492
|
|
|
449
493
|
done(null)
|
|
@@ -458,6 +502,7 @@ module.exports = class MerkleTree {
|
|
|
458
502
|
}
|
|
459
503
|
|
|
460
504
|
clear () {
|
|
505
|
+
this.cache = new Xache({ maxSize: this.cache.maxSize })
|
|
461
506
|
this.truncated = true
|
|
462
507
|
this.truncateTo = 0
|
|
463
508
|
this.roots = []
|
|
@@ -510,8 +555,8 @@ module.exports = class MerkleTree {
|
|
|
510
555
|
|
|
511
556
|
let unverified = null
|
|
512
557
|
|
|
513
|
-
if (proof.block || proof.seek) {
|
|
514
|
-
unverified =
|
|
558
|
+
if (proof.block || proof.hash || proof.seek) {
|
|
559
|
+
unverified = verifyTree(proof, this.crypto, batch.nodes)
|
|
515
560
|
}
|
|
516
561
|
|
|
517
562
|
if (!verifyUpgrade(proof, unverified, batch)) {
|
|
@@ -520,7 +565,7 @@ module.exports = class MerkleTree {
|
|
|
520
565
|
|
|
521
566
|
for (const root of batch.roots) {
|
|
522
567
|
const existing = await this.get(root.index, false)
|
|
523
|
-
if (existing && existing.hash
|
|
568
|
+
if (existing && b4a.equals(existing.hash, root.hash)) continue
|
|
524
569
|
batch._updateDiffRoot(root)
|
|
525
570
|
break
|
|
526
571
|
}
|
|
@@ -538,7 +583,7 @@ module.exports = class MerkleTree {
|
|
|
538
583
|
async verify (proof) {
|
|
539
584
|
const batch = new MerkleTreeBatch(this)
|
|
540
585
|
|
|
541
|
-
let unverified =
|
|
586
|
+
let unverified = verifyTree(proof, this.crypto, batch.nodes)
|
|
542
587
|
|
|
543
588
|
if (proof.upgrade) {
|
|
544
589
|
if (verifyUpgrade(proof, unverified, batch)) {
|
|
@@ -548,7 +593,7 @@ module.exports = class MerkleTree {
|
|
|
548
593
|
|
|
549
594
|
if (unverified) {
|
|
550
595
|
const verified = await this.get(unverified.index)
|
|
551
|
-
if (!verified.hash
|
|
596
|
+
if (!b4a.equals(verified.hash, unverified.hash)) {
|
|
552
597
|
throw new Error('Invalid checksum at node ' + unverified.index)
|
|
553
598
|
}
|
|
554
599
|
}
|
|
@@ -556,89 +601,102 @@ module.exports = class MerkleTree {
|
|
|
556
601
|
return batch
|
|
557
602
|
}
|
|
558
603
|
|
|
559
|
-
async proof ({ block, seek, upgrade }) {
|
|
604
|
+
async proof ({ block, hash, seek, upgrade }) {
|
|
560
605
|
// Important that this does not throw inbetween making the promise arrays
|
|
561
606
|
// and finalise being called, otherwise there will be lingering promises in the background
|
|
562
607
|
|
|
563
|
-
const signature = this.signature
|
|
564
608
|
const fork = this.fork
|
|
609
|
+
const signature = this.signature
|
|
565
610
|
const head = 2 * this.length
|
|
566
611
|
const from = upgrade ? upgrade.start * 2 : 0
|
|
567
612
|
const to = upgrade ? from + upgrade.length * 2 : head
|
|
613
|
+
const node = normalizeIndexed(block, hash)
|
|
568
614
|
|
|
569
615
|
if (from >= to || to > head) {
|
|
570
616
|
throw new Error('Invalid upgrade')
|
|
571
617
|
}
|
|
572
|
-
if (seek &&
|
|
573
|
-
throw new Error('Cannot both do a seek and block request when upgrading')
|
|
618
|
+
if (seek && upgrade && node !== null && node.index >= from) {
|
|
619
|
+
throw new Error('Cannot both do a seek and block/hash request when upgrading')
|
|
574
620
|
}
|
|
575
621
|
|
|
576
622
|
let subTree = head
|
|
577
623
|
|
|
578
624
|
const p = {
|
|
579
|
-
|
|
625
|
+
node: null,
|
|
580
626
|
seek: null,
|
|
581
627
|
upgrade: null,
|
|
582
628
|
additionalUpgrade: null
|
|
583
629
|
}
|
|
584
630
|
|
|
585
|
-
if (
|
|
586
|
-
subTree = nodesToRoot(
|
|
631
|
+
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
632
|
+
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
587
633
|
const seekRoot = seek ? await seekUntrustedTree(this, subTree, seek.bytes) : head
|
|
588
|
-
blockAndSeekProof(this,
|
|
589
|
-
} else if ((
|
|
590
|
-
subTree = seek ? await seekFromHead(this, to, seek.bytes) :
|
|
634
|
+
blockAndSeekProof(this, node, seek, seekRoot, subTree, p)
|
|
635
|
+
} else if ((node || seek) && upgrade) {
|
|
636
|
+
subTree = seek ? await seekFromHead(this, to, seek.bytes) : node.index
|
|
591
637
|
}
|
|
592
638
|
|
|
593
639
|
if (upgrade) {
|
|
594
|
-
upgradeProof(this,
|
|
640
|
+
upgradeProof(this, node, seek, from, to, subTree, p)
|
|
595
641
|
if (head > to) additionalUpgradeProof(this, to, head, p)
|
|
596
642
|
}
|
|
597
643
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if (block) {
|
|
602
|
-
const nodes = await Promise.all(p.block)
|
|
644
|
+
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p)
|
|
645
|
+
const result = { fork, block: null, hash: null, seek: null, upgrade: null }
|
|
603
646
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
647
|
+
if (block) {
|
|
648
|
+
result.block = {
|
|
649
|
+
index: block.index,
|
|
650
|
+
value: null, // populated upstream, alloc it here for simplicity
|
|
651
|
+
nodes: pNode
|
|
609
652
|
}
|
|
610
|
-
|
|
611
|
-
|
|
653
|
+
} else if (hash) {
|
|
654
|
+
result.hash = {
|
|
655
|
+
index: hash.index,
|
|
656
|
+
nodes: pNode
|
|
657
|
+
}
|
|
658
|
+
}
|
|
612
659
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
660
|
+
if (seek && pSeek !== null) {
|
|
661
|
+
result.seek = {
|
|
662
|
+
bytes: seek.bytes,
|
|
663
|
+
nodes: pSeek
|
|
617
664
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
signature
|
|
628
|
-
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (upgrade) {
|
|
668
|
+
result.upgrade = {
|
|
669
|
+
start: upgrade.start,
|
|
670
|
+
length: upgrade.length,
|
|
671
|
+
nodes: pUpgrade,
|
|
672
|
+
additionalNodes: pAdditional || [],
|
|
673
|
+
signature
|
|
629
674
|
}
|
|
675
|
+
}
|
|
630
676
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
677
|
+
return result
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Successor to .nodes()
|
|
681
|
+
async missingNodes (index) {
|
|
682
|
+
const head = 2 * this.length
|
|
683
|
+
const ite = flat.iterator(index)
|
|
684
|
+
|
|
685
|
+
// See iterator.rightSpan()
|
|
686
|
+
const iteRightSpan = ite.index + ite.factor / 2 - 1
|
|
687
|
+
// If the index is not in the current tree, we do not know how many missing nodes there are...
|
|
688
|
+
if (iteRightSpan >= head) return 0
|
|
689
|
+
|
|
690
|
+
let cnt = 0
|
|
691
|
+
while (!ite.contains(head) && (await this.get(ite.index, false)) === null) {
|
|
692
|
+
cnt++
|
|
693
|
+
ite.parent()
|
|
639
694
|
}
|
|
695
|
+
|
|
696
|
+
return cnt
|
|
640
697
|
}
|
|
641
698
|
|
|
699
|
+
// Deprecated
|
|
642
700
|
async nodes (index) {
|
|
643
701
|
const head = 2 * this.length
|
|
644
702
|
const ite = flat.iterator(index)
|
|
@@ -693,7 +751,7 @@ module.exports = class MerkleTree {
|
|
|
693
751
|
await new Promise((resolve, reject) => {
|
|
694
752
|
storage.read(0, OLD_TREE.length, (err, buf) => {
|
|
695
753
|
if (err) return resolve()
|
|
696
|
-
if (
|
|
754
|
+
if (b4a.equals(buf, OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
|
|
697
755
|
resolve()
|
|
698
756
|
})
|
|
699
757
|
})
|
|
@@ -704,7 +762,7 @@ module.exports = class MerkleTree {
|
|
|
704
762
|
|
|
705
763
|
const roots = []
|
|
706
764
|
for (const index of flat.fullRoots(2 * length)) {
|
|
707
|
-
roots.push(await getStoredNode(storage, index, true))
|
|
765
|
+
roots.push(await getStoredNode(storage, index, null, true))
|
|
708
766
|
}
|
|
709
767
|
|
|
710
768
|
return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null)
|
|
@@ -713,8 +771,14 @@ module.exports = class MerkleTree {
|
|
|
713
771
|
|
|
714
772
|
// All the methods needed for proof verification
|
|
715
773
|
|
|
716
|
-
function
|
|
717
|
-
|
|
774
|
+
function verifyTree ({ block, hash, seek }, crypto, nodes) {
|
|
775
|
+
const untrustedNode = block
|
|
776
|
+
? { index: 2 * block.index, value: block.value, nodes: block.nodes }
|
|
777
|
+
: hash
|
|
778
|
+
? { index: hash.index, value: null, nodes: hash.nodes }
|
|
779
|
+
: null
|
|
780
|
+
|
|
781
|
+
if (untrustedNode === null && (!seek || !seek.nodes.length)) return null
|
|
718
782
|
|
|
719
783
|
let root = null
|
|
720
784
|
|
|
@@ -734,12 +798,12 @@ function verifyBlock ({ block, seek }, crypto, nodes) {
|
|
|
734
798
|
}
|
|
735
799
|
}
|
|
736
800
|
|
|
737
|
-
if (
|
|
801
|
+
if (untrustedNode === null) return root
|
|
738
802
|
|
|
739
|
-
const ite = flat.iterator(
|
|
740
|
-
const blockHash =
|
|
803
|
+
const ite = flat.iterator(untrustedNode.index)
|
|
804
|
+
const blockHash = untrustedNode.value && blockNode(crypto, ite.index, untrustedNode.value)
|
|
741
805
|
|
|
742
|
-
const q = new NodeQueue(
|
|
806
|
+
const q = new NodeQueue(untrustedNode.nodes, root)
|
|
743
807
|
|
|
744
808
|
root = blockHash || q.shift(ite.index)
|
|
745
809
|
nodes.push(root)
|
|
@@ -888,13 +952,13 @@ function seekProof (tree, seekRoot, root, p) {
|
|
|
888
952
|
}
|
|
889
953
|
}
|
|
890
954
|
|
|
891
|
-
function blockAndSeekProof (tree,
|
|
892
|
-
if (!
|
|
955
|
+
function blockAndSeekProof (tree, node, seek, seekRoot, root, p) {
|
|
956
|
+
if (!node) return seekProof(tree, seekRoot, root, p)
|
|
893
957
|
|
|
894
|
-
const ite = flat.iterator(
|
|
958
|
+
const ite = flat.iterator(node.index)
|
|
895
959
|
|
|
896
|
-
p.
|
|
897
|
-
if (!
|
|
960
|
+
p.node = []
|
|
961
|
+
if (!node.value) p.node.push(tree.get(ite.index))
|
|
898
962
|
|
|
899
963
|
while (ite.index !== root) {
|
|
900
964
|
ite.sibling()
|
|
@@ -902,14 +966,14 @@ function blockAndSeekProof (tree, block, seek, seekRoot, root, p) {
|
|
|
902
966
|
if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {
|
|
903
967
|
seekProof(tree, seekRoot, ite.index, p)
|
|
904
968
|
} else {
|
|
905
|
-
p.
|
|
969
|
+
p.node.push(tree.get(ite.index))
|
|
906
970
|
}
|
|
907
971
|
|
|
908
972
|
ite.parent()
|
|
909
973
|
}
|
|
910
974
|
}
|
|
911
975
|
|
|
912
|
-
function upgradeProof (tree,
|
|
976
|
+
function upgradeProof (tree, node, seek, from, to, subTree, p) {
|
|
913
977
|
if (from === 0) p.upgrade = []
|
|
914
978
|
|
|
915
979
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -928,8 +992,8 @@ function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
|
928
992
|
while (ite.index !== root) {
|
|
929
993
|
ite.sibling()
|
|
930
994
|
if (ite.index > target) {
|
|
931
|
-
if (p.
|
|
932
|
-
blockAndSeekProof(tree,
|
|
995
|
+
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
996
|
+
blockAndSeekProof(tree, node, seek, subTree, ite.index, p)
|
|
933
997
|
} else {
|
|
934
998
|
p.upgrade.push(tree.get(ite.index))
|
|
935
999
|
}
|
|
@@ -946,8 +1010,8 @@ function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
|
946
1010
|
|
|
947
1011
|
// if the subtree included is a child of this tree, include that one
|
|
948
1012
|
// instead of a dup node
|
|
949
|
-
if (p.
|
|
950
|
-
blockAndSeekProof(tree,
|
|
1013
|
+
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
1014
|
+
blockAndSeekProof(tree, node, seek, subTree, ite.index, p)
|
|
951
1015
|
continue
|
|
952
1016
|
}
|
|
953
1017
|
|
|
@@ -1029,7 +1093,7 @@ function blankNode (index) {
|
|
|
1029
1093
|
|
|
1030
1094
|
// Storage methods
|
|
1031
1095
|
|
|
1032
|
-
function getStoredNode (storage, index, error) {
|
|
1096
|
+
function getStoredNode (storage, index, cache, error) {
|
|
1033
1097
|
return new Promise((resolve, reject) => {
|
|
1034
1098
|
storage.read(40 * index, 40, (err, data) => {
|
|
1035
1099
|
if (err) {
|
|
@@ -1038,16 +1102,18 @@ function getStoredNode (storage, index, error) {
|
|
|
1038
1102
|
return
|
|
1039
1103
|
}
|
|
1040
1104
|
|
|
1041
|
-
const hash = data.
|
|
1042
|
-
const size =
|
|
1105
|
+
const hash = data.subarray(8)
|
|
1106
|
+
const size = c.decode(c.uint64, data)
|
|
1043
1107
|
|
|
1044
|
-
if (size === 0 &&
|
|
1108
|
+
if (size === 0 && b4a.compare(hash, BLANK_HASH) === 0) {
|
|
1045
1109
|
if (error) reject(new Error('Could not load node: ' + index))
|
|
1046
1110
|
else resolve(null)
|
|
1047
1111
|
return
|
|
1048
1112
|
}
|
|
1049
1113
|
|
|
1050
|
-
|
|
1114
|
+
const node = { index, size, hash }
|
|
1115
|
+
if (cache !== null) cache.set(index, node)
|
|
1116
|
+
resolve(node)
|
|
1051
1117
|
})
|
|
1052
1118
|
})
|
|
1053
1119
|
}
|
|
@@ -1066,7 +1132,7 @@ async function autoLength (storage) {
|
|
|
1066
1132
|
if (!nodes) return 0
|
|
1067
1133
|
const ite = flat.iterator(nodes - 1)
|
|
1068
1134
|
let index = nodes - 1
|
|
1069
|
-
while (await getStoredNode(storage, ite.parent(), false)) index = ite.index
|
|
1135
|
+
while (await getStoredNode(storage, ite.parent(), null, false)) index = ite.index
|
|
1070
1136
|
return flat.rightSpan(index) / 2 + 1
|
|
1071
1137
|
}
|
|
1072
1138
|
|
|
@@ -1087,10 +1153,27 @@ function log2 (n) {
|
|
|
1087
1153
|
return res
|
|
1088
1154
|
}
|
|
1089
1155
|
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1092
|
-
hash.
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1156
|
+
function normalizeIndexed (block, hash) {
|
|
1157
|
+
if (block) return { value: true, index: block.index * 2, nodes: block.nodes, lastIndex: block.index }
|
|
1158
|
+
if (hash) return { value: false, index: hash.index, nodes: hash.nodes, lastIndex: flat.rightSpan(hash.index) / 2 }
|
|
1159
|
+
return null
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
async function settleProof (p) {
|
|
1163
|
+
const result = [
|
|
1164
|
+
p.node && Promise.all(p.node),
|
|
1165
|
+
p.seek && Promise.all(p.seek),
|
|
1166
|
+
p.upgrade && Promise.all(p.upgrade),
|
|
1167
|
+
p.additionalUpgrade && Promise.all(p.additionalUpgrade)
|
|
1168
|
+
]
|
|
1169
|
+
|
|
1170
|
+
try {
|
|
1171
|
+
return await Promise.all(result)
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
if (p.node) await Promise.allSettled(p.node)
|
|
1174
|
+
if (p.seek) await Promise.allSettled(p.seek)
|
|
1175
|
+
if (p.upgrade) await Promise.allSettled(p.upgrade)
|
|
1176
|
+
if (p.additionalUpgrade) await Promise.allSettled(p.additionalUpgrade)
|
|
1177
|
+
throw err
|
|
1178
|
+
}
|
|
1096
1179
|
}
|