hypercore 10.22.3 → 10.24.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 +69 -51
- package/lib/batch.js +14 -15
- package/lib/caps.js +8 -14
- package/lib/core.js +95 -68
- package/lib/manifest.js +218 -0
- package/lib/merkle-tree.js +6 -6
- package/lib/messages.js +91 -5
- package/lib/multisig.js +123 -0
- package/lib/replicator.js +47 -21
- package/package.json +2 -1
package/lib/messages.js
CHANGED
|
@@ -169,13 +169,12 @@ wire.handshake = {
|
|
|
169
169
|
c.fixed32.preencode(state, m.capability)
|
|
170
170
|
},
|
|
171
171
|
encode (state, m) {
|
|
172
|
-
c.uint.encode(state,
|
|
172
|
+
c.uint.encode(state, 0) // flags
|
|
173
173
|
c.fixed32.encode(state, m.capability)
|
|
174
174
|
},
|
|
175
175
|
decode (state) {
|
|
176
|
-
|
|
176
|
+
c.uint.decode(state) // flags
|
|
177
177
|
return {
|
|
178
|
-
manifest: (flags & 1) !== 0,
|
|
179
178
|
capability: c.fixed32.decode(state)
|
|
180
179
|
}
|
|
181
180
|
}
|
|
@@ -499,7 +498,7 @@ wire.sync = {
|
|
|
499
498
|
c.uint.preencode(state, m.remoteLength)
|
|
500
499
|
},
|
|
501
500
|
encode (state, m) {
|
|
502
|
-
c.uint.encode(state, (m.canUpgrade ? 1 : 0) | (m.uploading ? 2 : 0) | (m.downloading ? 4 : 0))
|
|
501
|
+
c.uint.encode(state, (m.canUpgrade ? 1 : 0) | (m.uploading ? 2 : 0) | (m.downloading ? 4 : 0) | (m.hasManifest ? 8 : 0))
|
|
503
502
|
c.uint.encode(state, m.fork)
|
|
504
503
|
c.uint.encode(state, m.length)
|
|
505
504
|
c.uint.encode(state, m.remoteLength)
|
|
@@ -513,7 +512,8 @@ wire.sync = {
|
|
|
513
512
|
remoteLength: c.uint.decode(state),
|
|
514
513
|
canUpgrade: (flags & 1) !== 0,
|
|
515
514
|
uploading: (flags & 2) !== 0,
|
|
516
|
-
downloading: (flags & 4) !== 0
|
|
515
|
+
downloading: (flags & 4) !== 0,
|
|
516
|
+
hasManifest: (flags & 8) !== 0
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
519
|
}
|
|
@@ -868,3 +868,89 @@ oplog.header = {
|
|
|
868
868
|
}
|
|
869
869
|
}
|
|
870
870
|
}
|
|
871
|
+
|
|
872
|
+
const uintArray = c.array(c.uint)
|
|
873
|
+
|
|
874
|
+
const patchEncoding = {
|
|
875
|
+
preencode (state, n) {
|
|
876
|
+
c.uint.preencode(state, n.start)
|
|
877
|
+
c.uint.preencode(state, n.length)
|
|
878
|
+
uintArray.preencode(state, n.nodes)
|
|
879
|
+
},
|
|
880
|
+
encode (state, n) {
|
|
881
|
+
c.uint.encode(state, n.start)
|
|
882
|
+
c.uint.encode(state, n.length)
|
|
883
|
+
uintArray.encode(state, n.nodes)
|
|
884
|
+
},
|
|
885
|
+
decode (state) {
|
|
886
|
+
return {
|
|
887
|
+
start: c.uint.decode(state),
|
|
888
|
+
length: c.uint.decode(state),
|
|
889
|
+
nodes: uintArray.decode(state)
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const multisigInput = {
|
|
895
|
+
preencode (state, n) {
|
|
896
|
+
state.end++
|
|
897
|
+
c.uint.preencode(state, n.signer)
|
|
898
|
+
c.fixed64.preencode(state, n.signature)
|
|
899
|
+
if (n.patch) patchEncoding.preencode(state, n.patch)
|
|
900
|
+
},
|
|
901
|
+
encode (state, n) {
|
|
902
|
+
c.uint.encode(state, n.patch ? 1 : 0)
|
|
903
|
+
c.uint.encode(state, n.signer)
|
|
904
|
+
c.fixed64.encode(state, n.signature)
|
|
905
|
+
if (n.patch) patchEncoding.encode(state, n.patch)
|
|
906
|
+
},
|
|
907
|
+
decode (state) {
|
|
908
|
+
const flags = c.uint.decode(state)
|
|
909
|
+
return {
|
|
910
|
+
signer: c.uint.decode(state),
|
|
911
|
+
signature: c.fixed64.decode(state),
|
|
912
|
+
patch: (flags & 1) ? patchEncoding.decode(state) : null
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const multisigInputArray = c.array(multisigInput)
|
|
918
|
+
|
|
919
|
+
const compactNode = {
|
|
920
|
+
preencode (state, n) {
|
|
921
|
+
c.uint.preencode(state, n.index)
|
|
922
|
+
c.uint.preencode(state, n.size)
|
|
923
|
+
c.fixed32.preencode(state, n.hash)
|
|
924
|
+
},
|
|
925
|
+
encode (state, n) {
|
|
926
|
+
c.uint.encode(state, n.index)
|
|
927
|
+
c.uint.encode(state, n.size)
|
|
928
|
+
c.fixed32.encode(state, n.hash)
|
|
929
|
+
},
|
|
930
|
+
decode (state) {
|
|
931
|
+
return {
|
|
932
|
+
index: c.uint.decode(state),
|
|
933
|
+
size: c.uint.decode(state),
|
|
934
|
+
hash: c.fixed32.decode(state)
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const compactNodeArray = c.array(compactNode)
|
|
940
|
+
|
|
941
|
+
exports.multiSignature = {
|
|
942
|
+
preencode (state, s) {
|
|
943
|
+
multisigInputArray.preencode(state, s.proofs)
|
|
944
|
+
compactNodeArray.preencode(state, s.nodes)
|
|
945
|
+
},
|
|
946
|
+
encode (state, s) {
|
|
947
|
+
multisigInputArray.encode(state, s.proofs)
|
|
948
|
+
compactNodeArray.encode(state, s.nodes)
|
|
949
|
+
},
|
|
950
|
+
decode (state) {
|
|
951
|
+
return {
|
|
952
|
+
proofs: multisigInputArray.decode(state),
|
|
953
|
+
nodes: compactNodeArray.decode(state)
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
package/lib/multisig.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const c = require('compact-encoding')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
const encoding = require('./messages').multiSignature
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
assemble,
|
|
7
|
+
inflate,
|
|
8
|
+
partialSignature,
|
|
9
|
+
signableLength
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function inflate (data) {
|
|
13
|
+
const compressedInputs = c.decode(encoding, data)
|
|
14
|
+
const inputs = []
|
|
15
|
+
|
|
16
|
+
for (const proof of compressedInputs.proofs) {
|
|
17
|
+
inputs.push({
|
|
18
|
+
signer: proof.signer,
|
|
19
|
+
signature: proof.signature,
|
|
20
|
+
patch: inflateUpgrade(proof.patch, compressedInputs.nodes)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return inputs
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function partialSignature (tree, signer, from, to = tree.length, signature = tree.signature) {
|
|
28
|
+
if (from > tree.length) return null
|
|
29
|
+
const patch = to <= from ? null : await upgrade(tree, from, to)
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
signer,
|
|
33
|
+
signature,
|
|
34
|
+
patch
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function upgrade (tree, from, to) {
|
|
39
|
+
const p = await tree.proof({ upgrade: { start: from, length: to - from } })
|
|
40
|
+
p.upgrade.additionalNodes = []
|
|
41
|
+
p.upgrade.signature = null
|
|
42
|
+
return p.upgrade
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function signableLength (lengths, quorum) {
|
|
46
|
+
if (quorum <= 0) quorum = 1
|
|
47
|
+
if (quorum > lengths.length) return 0
|
|
48
|
+
|
|
49
|
+
return lengths.sort(cmp)[quorum - 1]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function cmp (a, b) {
|
|
53
|
+
return b - a
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function assemble (inputs) {
|
|
57
|
+
const proofs = []
|
|
58
|
+
const nodes = []
|
|
59
|
+
|
|
60
|
+
for (const u of inputs) {
|
|
61
|
+
proofs.push(compressProof(u, nodes))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return c.encode(encoding, { proofs, nodes })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function compareNode (a, b) {
|
|
68
|
+
if (a.index !== b.index) return false
|
|
69
|
+
if (a.size !== b.size) return false
|
|
70
|
+
return b4a.equals(a.hash, b.hash)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function compressProof (proof, nodes) {
|
|
74
|
+
return {
|
|
75
|
+
signer: proof.signer,
|
|
76
|
+
signature: proof.signature,
|
|
77
|
+
patch: compressUpgrade(proof.patch, nodes)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function compressUpgrade (p, nodes) {
|
|
82
|
+
if (!p) return null
|
|
83
|
+
|
|
84
|
+
const u = {
|
|
85
|
+
start: p.start,
|
|
86
|
+
length: p.length,
|
|
87
|
+
nodes: []
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const node of p.nodes) {
|
|
91
|
+
let present = false
|
|
92
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
93
|
+
if (!compareNode(nodes[i], node)) continue
|
|
94
|
+
|
|
95
|
+
u.nodes.push(i)
|
|
96
|
+
present = true
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (present) continue
|
|
101
|
+
u.nodes.push(nodes.push(node) - 1)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return u
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function inflateUpgrade (s, nodes) {
|
|
108
|
+
if (!s) return null
|
|
109
|
+
|
|
110
|
+
const upgrade = {
|
|
111
|
+
start: s.start,
|
|
112
|
+
length: s.length,
|
|
113
|
+
nodes: [],
|
|
114
|
+
additionalNodes: [],
|
|
115
|
+
signature: null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const i of s.nodes) {
|
|
119
|
+
upgrade.nodes.push(nodes[i])
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return upgrade
|
|
123
|
+
}
|
package/lib/replicator.js
CHANGED
|
@@ -314,7 +314,7 @@ class Peer {
|
|
|
314
314
|
this.remoteUploading = true
|
|
315
315
|
this.remoteDownloading = true
|
|
316
316
|
this.remoteSynced = false
|
|
317
|
-
this.
|
|
317
|
+
this.remoteHasManifest = false
|
|
318
318
|
|
|
319
319
|
this.segmentsWanted = new Set()
|
|
320
320
|
this.broadcastedNonSparse = false
|
|
@@ -384,11 +384,12 @@ class Peer {
|
|
|
384
384
|
remoteLength: this.core.tree.fork === this.remoteFork ? this.remoteLength : 0,
|
|
385
385
|
canUpgrade: this.canUpgrade,
|
|
386
386
|
uploading: true,
|
|
387
|
-
downloading: this.replicator.downloading
|
|
387
|
+
downloading: this.replicator.downloading,
|
|
388
|
+
hasManifest: !!this.core.header.manifest && this.core.compat === false
|
|
388
389
|
})
|
|
389
390
|
}
|
|
390
391
|
|
|
391
|
-
onopen ({
|
|
392
|
+
onopen ({ capability }) {
|
|
392
393
|
const expected = caps.replicate(this.stream.isInitiator === false, this.replicator.key, this.stream.handshakeHash)
|
|
393
394
|
|
|
394
395
|
if (b4a.equals(capability, expected) !== true) { // TODO: change this to a rejection instead, less leakage
|
|
@@ -397,7 +398,6 @@ class Peer {
|
|
|
397
398
|
|
|
398
399
|
if (this.remoteOpened === true) return
|
|
399
400
|
this.remoteOpened = true
|
|
400
|
-
this.remoteManifest = manifest
|
|
401
401
|
|
|
402
402
|
this.protomux.cork()
|
|
403
403
|
|
|
@@ -440,7 +440,7 @@ class Peer {
|
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
async onsync ({ fork, length, remoteLength, canUpgrade, uploading, downloading }) {
|
|
443
|
+
async onsync ({ fork, length, remoteLength, canUpgrade, uploading, downloading, hasManifest }) {
|
|
444
444
|
const lengthChanged = length !== this.remoteLength
|
|
445
445
|
const sameFork = fork === this.core.tree.fork
|
|
446
446
|
|
|
@@ -450,6 +450,7 @@ class Peer {
|
|
|
450
450
|
this.remoteCanUpgrade = canUpgrade
|
|
451
451
|
this.remoteUploading = uploading
|
|
452
452
|
this.remoteDownloading = downloading
|
|
453
|
+
this.remoteHasManifest = hasManifest
|
|
453
454
|
|
|
454
455
|
if (this.remoteDownloading === false && this.replicator.downloading === false) {
|
|
455
456
|
// idling, shut it down...
|
|
@@ -587,25 +588,29 @@ class Peer {
|
|
|
587
588
|
}
|
|
588
589
|
}
|
|
589
590
|
|
|
590
|
-
if (proof
|
|
591
|
-
if (
|
|
592
|
-
this.
|
|
591
|
+
if (proof === null) {
|
|
592
|
+
if (msg.manifest && this.core.header.manifest) {
|
|
593
|
+
const manifest = this.core.header.manifest
|
|
594
|
+
this.wireData.send({ request: msg.id, fork: this.core.tree.fork, block: null, hash: null, seek: null, upgrade: null, manifest })
|
|
595
|
+
return
|
|
593
596
|
}
|
|
594
597
|
|
|
595
|
-
this.
|
|
596
|
-
request: msg.id,
|
|
597
|
-
fork: msg.fork,
|
|
598
|
-
block: proof.block,
|
|
599
|
-
hash: proof.hash,
|
|
600
|
-
seek: proof.seek,
|
|
601
|
-
upgrade: proof.upgrade,
|
|
602
|
-
manifest: proof.manifest
|
|
603
|
-
})
|
|
598
|
+
this.wireNoData.send({ request: msg.id })
|
|
604
599
|
return
|
|
605
600
|
}
|
|
606
601
|
|
|
607
|
-
|
|
608
|
-
|
|
602
|
+
if (proof.block !== null) {
|
|
603
|
+
this.replicator.onupload(proof.block.index, proof.block.value, this)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
this.wireData.send({
|
|
607
|
+
request: msg.id,
|
|
608
|
+
fork: msg.fork,
|
|
609
|
+
block: proof.block,
|
|
610
|
+
hash: proof.hash,
|
|
611
|
+
seek: proof.seek,
|
|
612
|
+
upgrade: proof.upgrade,
|
|
613
|
+
manifest: proof.manifest
|
|
609
614
|
})
|
|
610
615
|
}
|
|
611
616
|
|
|
@@ -777,11 +782,16 @@ class Peer {
|
|
|
777
782
|
? null
|
|
778
783
|
: { start: this.core.tree.length, length: this.remoteLength - this.core.tree.length },
|
|
779
784
|
// remote manifest check can be removed eventually...
|
|
780
|
-
manifest:
|
|
785
|
+
manifest: this.core.header.manifest === null && this.remoteHasManifest === true,
|
|
781
786
|
priority
|
|
782
787
|
}
|
|
783
788
|
}
|
|
784
789
|
|
|
790
|
+
_requestManifest () {
|
|
791
|
+
const req = this._makeRequest(false, 0)
|
|
792
|
+
this._send(req)
|
|
793
|
+
}
|
|
794
|
+
|
|
785
795
|
_requestUpgrade (u) {
|
|
786
796
|
const req = this._makeRequest(true, 0)
|
|
787
797
|
if (req === null) return false
|
|
@@ -1052,6 +1062,7 @@ module.exports = class Replicator {
|
|
|
1052
1062
|
this._ifAvailable = 0
|
|
1053
1063
|
this._updatesPending = 0
|
|
1054
1064
|
this._applyingReorg = null
|
|
1065
|
+
this._manifestPeer = null
|
|
1055
1066
|
|
|
1056
1067
|
const self = this
|
|
1057
1068
|
this._onstreamclose = onstreamclose
|
|
@@ -1311,6 +1322,8 @@ module.exports = class Replicator {
|
|
|
1311
1322
|
_removePeer (peer) {
|
|
1312
1323
|
this.peers.splice(this.peers.indexOf(peer), 1)
|
|
1313
1324
|
|
|
1325
|
+
if (this._manifestPeer === peer) this._manifestPeer = null
|
|
1326
|
+
|
|
1314
1327
|
for (const req of this._inflight) {
|
|
1315
1328
|
if (req.peer !== peer) continue
|
|
1316
1329
|
this._inflight.remove(req.id)
|
|
@@ -1544,6 +1557,10 @@ module.exports = class Replicator {
|
|
|
1544
1557
|
this._clearInflightReorgs(req)
|
|
1545
1558
|
}
|
|
1546
1559
|
|
|
1560
|
+
if (this._manifestPeer === peer && this.core.header.manifest !== null) {
|
|
1561
|
+
this._manifestPeer = null
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1547
1564
|
if (this._seeks.length > 0 || this._ranges.length > 0) this._updateNonPrimary()
|
|
1548
1565
|
else this.updatePeer(peer)
|
|
1549
1566
|
}
|
|
@@ -1629,6 +1646,10 @@ module.exports = class Replicator {
|
|
|
1629
1646
|
return this._upgrade !== null && this._upgrade.inflight.length === 0
|
|
1630
1647
|
}
|
|
1631
1648
|
|
|
1649
|
+
_maybeRequestManifest () {
|
|
1650
|
+
return this.core.header.manifest === null && this._manifestPeer === null
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1632
1653
|
_updateFork (peer) {
|
|
1633
1654
|
if (this._applyingReorg !== null || this.allowFork === false || peer.remoteFork <= this.core.tree.fork) {
|
|
1634
1655
|
return false
|
|
@@ -1649,6 +1670,12 @@ module.exports = class Replicator {
|
|
|
1649
1670
|
return false
|
|
1650
1671
|
}
|
|
1651
1672
|
|
|
1673
|
+
// Eagerly request the manifest even if the remote length is 0. If not 0 we'll get as part of the upgrade request...
|
|
1674
|
+
if (this._maybeRequestManifest() === true && peer.remoteLength === 0 && peer.remoteHasManifest === true) {
|
|
1675
|
+
this._manifestPeer = peer
|
|
1676
|
+
peer._requestManifest()
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1652
1679
|
for (const s of this._seeks) {
|
|
1653
1680
|
if (s.inflight.length > 0) continue // TODO: one per peer is better
|
|
1654
1681
|
if (peer._requestSeek(s) === true) {
|
|
@@ -1812,7 +1839,6 @@ module.exports = class Replicator {
|
|
|
1812
1839
|
const stream = protomux.stream
|
|
1813
1840
|
|
|
1814
1841
|
peer.channel.open({
|
|
1815
|
-
manifest: true,
|
|
1816
1842
|
capability: caps.replicate(stream.isInitiator, this.key, stream.handshakeHash)
|
|
1817
1843
|
})
|
|
1818
1844
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.24.0",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"flat-tree": "^1.9.0",
|
|
46
46
|
"hypercore-crypto": "^3.2.1",
|
|
47
47
|
"hypercore-errors": "^1.1.0",
|
|
48
|
+
"hypercore-id-encoding": "^1.2.0",
|
|
48
49
|
"is-options": "^1.0.1",
|
|
49
50
|
"protomux": "^3.5.0",
|
|
50
51
|
"quickbit-universal": "^2.1.1",
|