hypercore 10.0.0-alpha.23 → 10.0.0-alpha.26
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 +11 -1
- package/index.js +180 -81
- package/lib/bitfield.js +6 -3
- package/lib/caps.js +34 -0
- package/lib/core.js +26 -10
- package/lib/merkle-tree.js +129 -81
- package/lib/messages.js +245 -167
- package/lib/replicator.js +1254 -574
- package/package.json +5 -3
- package/lib/extensions.js +0 -76
- package/lib/protocol.js +0 -583
- package/lib/random-iterator.js +0 -46
package/lib/merkle-tree.js
CHANGED
|
@@ -2,6 +2,7 @@ const flat = require('flat-tree')
|
|
|
2
2
|
const crypto = require('hypercore-crypto')
|
|
3
3
|
const c = require('compact-encoding')
|
|
4
4
|
const b4a = require('b4a')
|
|
5
|
+
const caps = require('./caps')
|
|
5
6
|
|
|
6
7
|
const BLANK_HASH = b4a.alloc(32)
|
|
7
8
|
const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
|
|
@@ -57,11 +58,11 @@ class MerkleTreeBatch {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
signable (hash = this.hash()) {
|
|
60
|
-
return
|
|
61
|
+
return caps.treeSignable(hash, this.length, this.fork)
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
return
|
|
64
|
+
signableLegacy (hash = this.hash()) {
|
|
65
|
+
return caps.treeSignableLegacy(hash, this.length, this.fork)
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
append (buf) {
|
|
@@ -213,7 +214,7 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
213
214
|
if (this.want === null) return true
|
|
214
215
|
|
|
215
216
|
const nodes = []
|
|
216
|
-
const root =
|
|
217
|
+
const root = verifyTree(proof, this.tree.crypto, nodes)
|
|
217
218
|
|
|
218
219
|
if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false
|
|
219
220
|
|
|
@@ -374,11 +375,7 @@ module.exports = class MerkleTree {
|
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
signable (hash = this.hash()) {
|
|
377
|
-
return
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
signedBy (key) {
|
|
381
|
-
return this.signature !== null && this.crypto.verify(this.signable(), this.signature, key)
|
|
378
|
+
return caps.treeSignable(hash, this.length, this.fork)
|
|
382
379
|
}
|
|
383
380
|
|
|
384
381
|
getRoots (length) {
|
|
@@ -392,6 +389,21 @@ module.exports = class MerkleTree {
|
|
|
392
389
|
return Promise.all(roots)
|
|
393
390
|
}
|
|
394
391
|
|
|
392
|
+
async upgradeable (length) {
|
|
393
|
+
const indexes = flat.fullRoots(2 * length)
|
|
394
|
+
const roots = new Array(indexes.length)
|
|
395
|
+
|
|
396
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
397
|
+
roots[i] = this.get(indexes[i], false)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
for (const node of await Promise.all(roots)) {
|
|
401
|
+
if (node === null) return false
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return true
|
|
405
|
+
}
|
|
406
|
+
|
|
395
407
|
get (index, error = true) {
|
|
396
408
|
let node = this.unflushed.get(index)
|
|
397
409
|
|
|
@@ -537,8 +549,8 @@ module.exports = class MerkleTree {
|
|
|
537
549
|
|
|
538
550
|
let unverified = null
|
|
539
551
|
|
|
540
|
-
if (proof.block || proof.seek) {
|
|
541
|
-
unverified =
|
|
552
|
+
if (proof.block || proof.hash || proof.seek) {
|
|
553
|
+
unverified = verifyTree(proof, this.crypto, batch.nodes)
|
|
542
554
|
}
|
|
543
555
|
|
|
544
556
|
if (!verifyUpgrade(proof, unverified, batch)) {
|
|
@@ -565,7 +577,7 @@ module.exports = class MerkleTree {
|
|
|
565
577
|
async verify (proof) {
|
|
566
578
|
const batch = new MerkleTreeBatch(this)
|
|
567
579
|
|
|
568
|
-
let unverified =
|
|
580
|
+
let unverified = verifyTree(proof, this.crypto, batch.nodes)
|
|
569
581
|
|
|
570
582
|
if (proof.upgrade) {
|
|
571
583
|
if (verifyUpgrade(proof, unverified, batch)) {
|
|
@@ -583,89 +595,102 @@ module.exports = class MerkleTree {
|
|
|
583
595
|
return batch
|
|
584
596
|
}
|
|
585
597
|
|
|
586
|
-
async proof ({ block, seek, upgrade }) {
|
|
598
|
+
async proof ({ block, hash, seek, upgrade }) {
|
|
587
599
|
// Important that this does not throw inbetween making the promise arrays
|
|
588
600
|
// and finalise being called, otherwise there will be lingering promises in the background
|
|
589
601
|
|
|
590
|
-
const signature = this.signature
|
|
591
602
|
const fork = this.fork
|
|
603
|
+
const signature = this.signature
|
|
592
604
|
const head = 2 * this.length
|
|
593
605
|
const from = upgrade ? upgrade.start * 2 : 0
|
|
594
606
|
const to = upgrade ? from + upgrade.length * 2 : head
|
|
607
|
+
const node = normalizeIndexed(block, hash)
|
|
595
608
|
|
|
596
609
|
if (from >= to || to > head) {
|
|
597
610
|
throw new Error('Invalid upgrade')
|
|
598
611
|
}
|
|
599
|
-
if (seek &&
|
|
600
|
-
throw new Error('Cannot both do a seek and block request when upgrading')
|
|
612
|
+
if (seek && upgrade && node !== null && node.index >= from) {
|
|
613
|
+
throw new Error('Cannot both do a seek and block/hash request when upgrading')
|
|
601
614
|
}
|
|
602
615
|
|
|
603
616
|
let subTree = head
|
|
604
617
|
|
|
605
618
|
const p = {
|
|
606
|
-
|
|
619
|
+
node: null,
|
|
607
620
|
seek: null,
|
|
608
621
|
upgrade: null,
|
|
609
622
|
additionalUpgrade: null
|
|
610
623
|
}
|
|
611
624
|
|
|
612
|
-
if (
|
|
613
|
-
subTree = nodesToRoot(
|
|
625
|
+
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
626
|
+
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
614
627
|
const seekRoot = seek ? await seekUntrustedTree(this, subTree, seek.bytes) : head
|
|
615
|
-
blockAndSeekProof(this,
|
|
616
|
-
} else if ((
|
|
617
|
-
subTree = seek ? await seekFromHead(this, to, seek.bytes) :
|
|
628
|
+
blockAndSeekProof(this, node, seek, seekRoot, subTree, p)
|
|
629
|
+
} else if ((node || seek) && upgrade) {
|
|
630
|
+
subTree = seek ? await seekFromHead(this, to, seek.bytes) : node.index
|
|
618
631
|
}
|
|
619
632
|
|
|
620
633
|
if (upgrade) {
|
|
621
|
-
upgradeProof(this,
|
|
634
|
+
upgradeProof(this, node, seek, from, to, subTree, p)
|
|
622
635
|
if (head > to) additionalUpgradeProof(this, to, head, p)
|
|
623
636
|
}
|
|
624
637
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
if (block) {
|
|
629
|
-
const nodes = await Promise.all(p.block)
|
|
638
|
+
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p)
|
|
639
|
+
const result = { fork, block: null, hash: null, seek: null, upgrade: null }
|
|
630
640
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
641
|
+
if (block) {
|
|
642
|
+
result.block = {
|
|
643
|
+
index: block.index,
|
|
644
|
+
value: null, // populated upstream, alloc it here for simplicity
|
|
645
|
+
nodes: pNode
|
|
636
646
|
}
|
|
637
|
-
|
|
638
|
-
|
|
647
|
+
} else if (hash) {
|
|
648
|
+
result.hash = {
|
|
649
|
+
index: hash.index,
|
|
650
|
+
nodes: pNode
|
|
651
|
+
}
|
|
652
|
+
}
|
|
639
653
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
654
|
+
if (seek && pSeek !== null) {
|
|
655
|
+
result.seek = {
|
|
656
|
+
bytes: seek.bytes,
|
|
657
|
+
nodes: pSeek
|
|
644
658
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
signature
|
|
655
|
-
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (upgrade) {
|
|
662
|
+
result.upgrade = {
|
|
663
|
+
start: upgrade.start,
|
|
664
|
+
length: upgrade.length,
|
|
665
|
+
nodes: pUpgrade,
|
|
666
|
+
additionalNodes: pAdditional || [],
|
|
667
|
+
signature
|
|
656
668
|
}
|
|
669
|
+
}
|
|
657
670
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
671
|
+
return result
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Successor to .nodes()
|
|
675
|
+
async missingNodes (index) {
|
|
676
|
+
const head = 2 * this.length
|
|
677
|
+
const ite = flat.iterator(index)
|
|
678
|
+
|
|
679
|
+
// See iterator.rightSpan()
|
|
680
|
+
const iteRightSpan = ite.index + ite.factor / 2 - 1
|
|
681
|
+
// If the index is not in the current tree, we do not know how many missing nodes there are...
|
|
682
|
+
if (iteRightSpan >= head) return 0
|
|
683
|
+
|
|
684
|
+
let cnt = 0
|
|
685
|
+
while (!ite.contains(head) && (await this.get(ite.index, false)) === null) {
|
|
686
|
+
cnt++
|
|
687
|
+
ite.parent()
|
|
666
688
|
}
|
|
689
|
+
|
|
690
|
+
return cnt
|
|
667
691
|
}
|
|
668
692
|
|
|
693
|
+
// Deprecated
|
|
669
694
|
async nodes (index) {
|
|
670
695
|
const head = 2 * this.length
|
|
671
696
|
const ite = flat.iterator(index)
|
|
@@ -740,8 +765,14 @@ module.exports = class MerkleTree {
|
|
|
740
765
|
|
|
741
766
|
// All the methods needed for proof verification
|
|
742
767
|
|
|
743
|
-
function
|
|
744
|
-
|
|
768
|
+
function verifyTree ({ block, hash, seek }, crypto, nodes) {
|
|
769
|
+
const untrustedNode = block
|
|
770
|
+
? { index: 2 * block.index, value: block.value, nodes: block.nodes }
|
|
771
|
+
: hash
|
|
772
|
+
? { index: hash.index, value: null, nodes: hash.nodes }
|
|
773
|
+
: null
|
|
774
|
+
|
|
775
|
+
if (untrustedNode === null && (!seek || !seek.nodes.length)) return null
|
|
745
776
|
|
|
746
777
|
let root = null
|
|
747
778
|
|
|
@@ -761,12 +792,12 @@ function verifyBlock ({ block, seek }, crypto, nodes) {
|
|
|
761
792
|
}
|
|
762
793
|
}
|
|
763
794
|
|
|
764
|
-
if (
|
|
795
|
+
if (untrustedNode === null) return root
|
|
765
796
|
|
|
766
|
-
const ite = flat.iterator(
|
|
767
|
-
const blockHash =
|
|
797
|
+
const ite = flat.iterator(untrustedNode.index)
|
|
798
|
+
const blockHash = untrustedNode.value && blockNode(crypto, ite.index, untrustedNode.value)
|
|
768
799
|
|
|
769
|
-
const q = new NodeQueue(
|
|
800
|
+
const q = new NodeQueue(untrustedNode.nodes, root)
|
|
770
801
|
|
|
771
802
|
root = blockHash || q.shift(ite.index)
|
|
772
803
|
nodes.push(root)
|
|
@@ -915,13 +946,13 @@ function seekProof (tree, seekRoot, root, p) {
|
|
|
915
946
|
}
|
|
916
947
|
}
|
|
917
948
|
|
|
918
|
-
function blockAndSeekProof (tree,
|
|
919
|
-
if (!
|
|
949
|
+
function blockAndSeekProof (tree, node, seek, seekRoot, root, p) {
|
|
950
|
+
if (!node) return seekProof(tree, seekRoot, root, p)
|
|
920
951
|
|
|
921
|
-
const ite = flat.iterator(
|
|
952
|
+
const ite = flat.iterator(node.index)
|
|
922
953
|
|
|
923
|
-
p.
|
|
924
|
-
if (!
|
|
954
|
+
p.node = []
|
|
955
|
+
if (!node.value) p.node.push(tree.get(ite.index))
|
|
925
956
|
|
|
926
957
|
while (ite.index !== root) {
|
|
927
958
|
ite.sibling()
|
|
@@ -929,14 +960,14 @@ function blockAndSeekProof (tree, block, seek, seekRoot, root, p) {
|
|
|
929
960
|
if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {
|
|
930
961
|
seekProof(tree, seekRoot, ite.index, p)
|
|
931
962
|
} else {
|
|
932
|
-
p.
|
|
963
|
+
p.node.push(tree.get(ite.index))
|
|
933
964
|
}
|
|
934
965
|
|
|
935
966
|
ite.parent()
|
|
936
967
|
}
|
|
937
968
|
}
|
|
938
969
|
|
|
939
|
-
function upgradeProof (tree,
|
|
970
|
+
function upgradeProof (tree, node, seek, from, to, subTree, p) {
|
|
940
971
|
if (from === 0) p.upgrade = []
|
|
941
972
|
|
|
942
973
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -955,8 +986,8 @@ function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
|
955
986
|
while (ite.index !== root) {
|
|
956
987
|
ite.sibling()
|
|
957
988
|
if (ite.index > target) {
|
|
958
|
-
if (p.
|
|
959
|
-
blockAndSeekProof(tree,
|
|
989
|
+
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
990
|
+
blockAndSeekProof(tree, node, seek, subTree, ite.index, p)
|
|
960
991
|
} else {
|
|
961
992
|
p.upgrade.push(tree.get(ite.index))
|
|
962
993
|
}
|
|
@@ -973,8 +1004,8 @@ function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
|
973
1004
|
|
|
974
1005
|
// if the subtree included is a child of this tree, include that one
|
|
975
1006
|
// instead of a dup node
|
|
976
|
-
if (p.
|
|
977
|
-
blockAndSeekProof(tree,
|
|
1007
|
+
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
1008
|
+
blockAndSeekProof(tree, node, seek, subTree, ite.index, p)
|
|
978
1009
|
continue
|
|
979
1010
|
}
|
|
980
1011
|
|
|
@@ -1114,10 +1145,27 @@ function log2 (n) {
|
|
|
1114
1145
|
return res
|
|
1115
1146
|
}
|
|
1116
1147
|
|
|
1117
|
-
function
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1148
|
+
function normalizeIndexed (block, hash) {
|
|
1149
|
+
if (block) return { value: true, index: block.index * 2, nodes: block.nodes, lastIndex: block.index }
|
|
1150
|
+
if (hash) return { value: false, index: hash.index, nodes: hash.nodes, lastIndex: flat.rightSpan(hash.index) / 2 }
|
|
1151
|
+
return null
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
async function settleProof (p) {
|
|
1155
|
+
const result = [
|
|
1156
|
+
p.node && Promise.all(p.node),
|
|
1157
|
+
p.seek && Promise.all(p.seek),
|
|
1158
|
+
p.upgrade && Promise.all(p.upgrade),
|
|
1159
|
+
p.additionalUpgrade && Promise.all(p.additionalUpgrade)
|
|
1160
|
+
]
|
|
1161
|
+
|
|
1162
|
+
try {
|
|
1163
|
+
return await Promise.all(result)
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
if (p.node) await Promise.allSettled(p.node)
|
|
1166
|
+
if (p.seek) await Promise.allSettled(p.seek)
|
|
1167
|
+
if (p.upgrade) await Promise.allSettled(p.upgrade)
|
|
1168
|
+
if (p.additionalUpgrade) await Promise.allSettled(p.additionalUpgrade)
|
|
1169
|
+
throw err
|
|
1170
|
+
}
|
|
1123
1171
|
}
|