hypercore 10.22.2 → 10.23.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/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, m.manifest ? 1 : 0) // flags for the future
172
+ c.uint.encode(state, 0) // flags
173
173
  c.fixed32.encode(state, m.capability)
174
174
  },
175
175
  decode (state) {
176
- const flags = c.uint.decode(state)
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
+ }
@@ -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 (core, signer, from, to = core.core.tree.length, signature = core.core.tree.signature) {
28
+ if (from > core.core.tree.length) return null
29
+ const patch = to <= from ? null : await upgrade(core, from, to)
30
+
31
+ return {
32
+ signer,
33
+ signature,
34
+ patch
35
+ }
36
+ }
37
+
38
+ async function upgrade (core, from, to) {
39
+ const p = await core.core.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.remoteManifest = false
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 ({ manifest, capability }) {
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 !== null) {
591
- if (proof.block !== null) {
592
- this.replicator.onupload(proof.block.index, proof.block.value, 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.wireData.send({
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
- this.wireNoData.send({
608
- request: msg.id
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: needsUpgrade && !this.core.header.manifest && this.remoteManifest && !this.core.compat,
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.22.2",
3
+ "version": "10.23.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {