hypercore 11.24.0 → 11.26.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 +70 -0
- package/lib/core.js +85 -6
- package/lib/feature-flags.js +3 -0
- package/lib/fully-remote-proof.js +9 -3
- package/lib/merkle-tree.js +17 -0
- package/lib/replicator.js +92 -38
- package/lib/session-state.js +6 -2
- package/lib/wants.js +13 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -9,6 +9,10 @@ const Protomux = require('protomux')
|
|
|
9
9
|
const id = require('hypercore-id-encoding')
|
|
10
10
|
const safetyCatch = require('safety-catch')
|
|
11
11
|
const unslab = require('unslab')
|
|
12
|
+
const flat = require('flat-tree')
|
|
13
|
+
|
|
14
|
+
const { SMALL_WANTS } = require('./lib/feature-flags')
|
|
15
|
+
const { UPDATE_COMPAT } = require('./lib/wants')
|
|
12
16
|
|
|
13
17
|
const inspect = require('./lib/inspect')
|
|
14
18
|
const Core = require('./lib/core')
|
|
@@ -20,6 +24,7 @@ const Replicator = require('./lib/replicator')
|
|
|
20
24
|
const { manifestHash, createManifest } = require('./lib/verifier')
|
|
21
25
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
22
26
|
const { MerkleTree } = require('./lib/merkle-tree')
|
|
27
|
+
const { proof, verify } = require('./lib/fully-remote-proof')
|
|
23
28
|
const {
|
|
24
29
|
ASSERTION,
|
|
25
30
|
BAD_ARGUMENT,
|
|
@@ -85,6 +90,8 @@ class Hypercore extends EventEmitter {
|
|
|
85
90
|
this._findingPeers = 0
|
|
86
91
|
this._active = opts.weak ? !!opts.active : opts.active !== false
|
|
87
92
|
|
|
93
|
+
this.waits = 0
|
|
94
|
+
|
|
88
95
|
this._sessionIndex = -1
|
|
89
96
|
this._stateIndex = -1 // maintained by session state
|
|
90
97
|
this._monitorIndex = -1 // maintained by replication state
|
|
@@ -103,6 +110,13 @@ class Hypercore extends EventEmitter {
|
|
|
103
110
|
|
|
104
111
|
static DefaultEncryption = DefaultEncryption
|
|
105
112
|
|
|
113
|
+
static SMALL_WANTS = SMALL_WANTS
|
|
114
|
+
|
|
115
|
+
static enable(flag) {
|
|
116
|
+
const enableCompat = (flag & SMALL_WANTS) === 0
|
|
117
|
+
UPDATE_COMPAT(enableCompat)
|
|
118
|
+
}
|
|
119
|
+
|
|
106
120
|
static key(manifest, { compat, version, namespace } = {}) {
|
|
107
121
|
if (b4a.isBuffer(manifest)) {
|
|
108
122
|
manifest = { version, signers: [{ publicKey: manifest, namespace }] }
|
|
@@ -284,6 +298,15 @@ class Hypercore extends EventEmitter {
|
|
|
284
298
|
throw err
|
|
285
299
|
}
|
|
286
300
|
|
|
301
|
+
// Setup automatic recovery if in repair mode
|
|
302
|
+
if (this.core._repairMode) {
|
|
303
|
+
const recoverTreeNodeFromPeersBound = this.recoverTreeNodeFromPeers.bind(this)
|
|
304
|
+
this.once('repaired', () => {
|
|
305
|
+
this.off('peer-add', recoverTreeNodeFromPeersBound)
|
|
306
|
+
})
|
|
307
|
+
this.on('peer-add', recoverTreeNodeFromPeersBound)
|
|
308
|
+
}
|
|
309
|
+
|
|
287
310
|
this.emit('ready')
|
|
288
311
|
|
|
289
312
|
// if we are a weak session the core might have closed...
|
|
@@ -858,6 +881,7 @@ class Hypercore extends EventEmitter {
|
|
|
858
881
|
|
|
859
882
|
if (!this._shouldWait(opts, this.wait)) return null
|
|
860
883
|
|
|
884
|
+
this.waits++
|
|
861
885
|
if (opts && opts.onwait) opts.onwait(index, this)
|
|
862
886
|
if (this.onwait) this.onwait(index, this)
|
|
863
887
|
|
|
@@ -1021,6 +1045,52 @@ class Hypercore extends EventEmitter {
|
|
|
1021
1045
|
return batch
|
|
1022
1046
|
}
|
|
1023
1047
|
|
|
1048
|
+
generateRemoteProofForTreeNode(treeNodeIndex) {
|
|
1049
|
+
const blockProofIndex = flat.rightSpan(treeNodeIndex) / 2
|
|
1050
|
+
return proof(this, {
|
|
1051
|
+
index: blockProofIndex,
|
|
1052
|
+
// + 1 to length so the block is included
|
|
1053
|
+
upgrade: { start: 0, length: blockProofIndex + 1 }
|
|
1054
|
+
})
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
async recoverFromRemoteProof(remoteProof) {
|
|
1058
|
+
this.core.replicator.setPushOnly(true)
|
|
1059
|
+
this.core._repairMode = true
|
|
1060
|
+
await this.core.state.mutex.lock()
|
|
1061
|
+
const p = await verify(this.core.db, remoteProof)
|
|
1062
|
+
if (!p) return false
|
|
1063
|
+
|
|
1064
|
+
const tx = this.core.storage.write()
|
|
1065
|
+
for (const node of p.proof.upgrade.nodes) {
|
|
1066
|
+
tx.putTreeNode(node)
|
|
1067
|
+
}
|
|
1068
|
+
await tx.flush()
|
|
1069
|
+
|
|
1070
|
+
this.core.state.mutex.unlock()
|
|
1071
|
+
const succeed = p.proof.upgrade.nodes.length !== 0
|
|
1072
|
+
if (succeed) {
|
|
1073
|
+
this.core.replicator.setPushOnly(false)
|
|
1074
|
+
}
|
|
1075
|
+
return succeed
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
recoverTreeNodeFromPeers() {
|
|
1079
|
+
this.core.replicator.setPushOnly(true)
|
|
1080
|
+
|
|
1081
|
+
for (const peer of this.core.replicator.peers) {
|
|
1082
|
+
const req = {
|
|
1083
|
+
id: 0,
|
|
1084
|
+
fork: this.fork,
|
|
1085
|
+
upgrade: {
|
|
1086
|
+
start: 0,
|
|
1087
|
+
length: this.length
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
peer.wireRequest.send(req)
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1024
1094
|
registerExtension(name, handlers = {}) {
|
|
1025
1095
|
if (this.extensions.has(name)) {
|
|
1026
1096
|
const ext = this.extensions.get(name)
|
package/lib/core.js
CHANGED
|
@@ -61,6 +61,7 @@ module.exports = class Core {
|
|
|
61
61
|
this._verifies = null
|
|
62
62
|
this._verifiesFlushed = null
|
|
63
63
|
this._legacy = !!opts.legacy
|
|
64
|
+
this._repairMode = false
|
|
64
65
|
|
|
65
66
|
this.opening = this._open(opts)
|
|
66
67
|
this.opening.catch(noop)
|
|
@@ -269,12 +270,16 @@ module.exports = class Core {
|
|
|
269
270
|
fork: header.tree.fork,
|
|
270
271
|
length: header.tree.length,
|
|
271
272
|
signature: header.tree.signature,
|
|
272
|
-
roots: header.tree.length
|
|
273
|
-
? await MerkleTree.getRootsFromStorage(storage, header.tree.length)
|
|
274
|
-
: [],
|
|
273
|
+
roots: await maybeGetRootsFromStorage(storage, header.tree.length),
|
|
275
274
|
prologue
|
|
276
275
|
}
|
|
277
276
|
|
|
277
|
+
this._repairMode = !treeInfo.roots.length && treeInfo.length && !overwrite
|
|
278
|
+
// Set push only mode if in repair mode
|
|
279
|
+
if (this._repairMode) {
|
|
280
|
+
this.replicator.setPushOnly(true)
|
|
281
|
+
}
|
|
282
|
+
|
|
278
283
|
if (overwrite) {
|
|
279
284
|
const tx = storage.write()
|
|
280
285
|
tx.deleteTreeNodeRange(0, -1)
|
|
@@ -573,7 +578,7 @@ module.exports = class Core {
|
|
|
573
578
|
return true
|
|
574
579
|
}
|
|
575
580
|
|
|
576
|
-
const roots = await
|
|
581
|
+
const roots = await maybeGetRootsFromStorage(this.storage, proof.upgrade.length)
|
|
577
582
|
const remoteTreeHash = crypto.tree(proof.upgrade.nodes)
|
|
578
583
|
const localTreeHash = crypto.tree(roots)
|
|
579
584
|
|
|
@@ -595,8 +600,10 @@ module.exports = class Core {
|
|
|
595
600
|
|
|
596
601
|
const verifyBatch = MerkleTree.verifyFullyRemote(this.state, await treeProof.settle())
|
|
597
602
|
this._verifyBatchUpgrade(verifyBatch, this.header.manifest)
|
|
598
|
-
} catch {
|
|
599
|
-
return true
|
|
603
|
+
} catch (err) {
|
|
604
|
+
if (err.code !== 'INVALID_OPERATION') return true
|
|
605
|
+
|
|
606
|
+
return !(await this._repairTreeNodes(proof))
|
|
600
607
|
}
|
|
601
608
|
|
|
602
609
|
// both proofs are valid, now check if they forked
|
|
@@ -637,6 +644,68 @@ module.exports = class Core {
|
|
|
637
644
|
return true
|
|
638
645
|
}
|
|
639
646
|
|
|
647
|
+
async _repairTreeNodes(proof) {
|
|
648
|
+
if (!this._repairMode) return false
|
|
649
|
+
|
|
650
|
+
await this.state.mutex.lock()
|
|
651
|
+
|
|
652
|
+
for (let i = this.monitors.length - 1; i >= 0; i--) {
|
|
653
|
+
this.monitors[i].emit('repairing', proof)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
try {
|
|
657
|
+
const batch = MerkleTree.verifyFullyRemote(this.state, proof)
|
|
658
|
+
this._verifyBatchUpgrade(batch, proof.manifest)
|
|
659
|
+
} catch {
|
|
660
|
+
this.state.mutex.unlock()
|
|
661
|
+
for (let i = this.monitors.length - 1; i >= 0; i--) {
|
|
662
|
+
this.monitors[i].emit('repair-failed')
|
|
663
|
+
}
|
|
664
|
+
return false
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Attempt to repair local merkle tree with remote roots
|
|
668
|
+
const tx = this.storage.write()
|
|
669
|
+
for (const node of proof.upgrade.nodes) {
|
|
670
|
+
tx.putTreeNode(node)
|
|
671
|
+
}
|
|
672
|
+
await tx.flush()
|
|
673
|
+
|
|
674
|
+
// Try local proof again
|
|
675
|
+
try {
|
|
676
|
+
const rx = this.state.storage.read()
|
|
677
|
+
const treeProofPromise = MerkleTree.proof(this.state, rx, {
|
|
678
|
+
block: null,
|
|
679
|
+
hash: null,
|
|
680
|
+
seek: null,
|
|
681
|
+
upgrade: {
|
|
682
|
+
start: 0,
|
|
683
|
+
length: proof.upgrade.length
|
|
684
|
+
}
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
rx.tryFlush()
|
|
688
|
+
|
|
689
|
+
const treeProof = await treeProofPromise
|
|
690
|
+
|
|
691
|
+
const verifyBatch = MerkleTree.verifyFullyRemote(this.state, await treeProof.settle())
|
|
692
|
+
this._verifyBatchUpgrade(verifyBatch, this.header.manifest)
|
|
693
|
+
} catch (err) {
|
|
694
|
+
this.state.mutex.unlock()
|
|
695
|
+
for (let i = this.monitors.length - 1; i >= 0; i--) {
|
|
696
|
+
this.monitors[i].emit('repair-failed')
|
|
697
|
+
}
|
|
698
|
+
return false
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
for (let i = this.monitors.length - 1; i >= 0; i--) {
|
|
702
|
+
this.monitors[i].emit('repaired', proof)
|
|
703
|
+
}
|
|
704
|
+
this.replicator.setPushOnly(false)
|
|
705
|
+
this.state.mutex.unlock()
|
|
706
|
+
return true
|
|
707
|
+
}
|
|
708
|
+
|
|
640
709
|
async verifyReorg(proof) {
|
|
641
710
|
const batch = new ReorgBatch(this.state)
|
|
642
711
|
await MerkleTree.reorg(this.state, proof, batch)
|
|
@@ -899,6 +968,16 @@ function parseHeader(info) {
|
|
|
899
968
|
}
|
|
900
969
|
}
|
|
901
970
|
|
|
971
|
+
async function maybeGetRootsFromStorage(storage, length) {
|
|
972
|
+
try {
|
|
973
|
+
return length ? await MerkleTree.getRootsFromStorage(storage, length) : []
|
|
974
|
+
} catch (err) {
|
|
975
|
+
if (err.code !== 'INVALID_OPERATION') throw err
|
|
976
|
+
// If INVALID_OPERATION, a root is likely missing so load empty roots to try to recover
|
|
977
|
+
return []
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
902
981
|
function noop() {}
|
|
903
982
|
|
|
904
983
|
async function getCoreInfo(storage) {
|
|
@@ -65,7 +65,9 @@ async function verify(storage, buffer, { referrer = null } = {}) {
|
|
|
65
65
|
|
|
66
66
|
rx.tryFlush()
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
let roots = await Promise.all(rootPromises)
|
|
69
|
+
if (roots.some(isNull)) roots = []
|
|
70
|
+
|
|
69
71
|
const length = head ? head.length : 0
|
|
70
72
|
|
|
71
73
|
if (!auth.manifest || !auth.manifest.signers.length) return null
|
|
@@ -100,10 +102,10 @@ async function verify(storage, buffer, { referrer = null } = {}) {
|
|
|
100
102
|
return result
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
async function proof(sender, { index, block = null } = {}) {
|
|
105
|
+
async function proof(sender, { index, block = null, upgrade = null } = {}) {
|
|
104
106
|
const proof = await sender.proof({
|
|
105
107
|
block: block ? { index, nodes: 0 } : null,
|
|
106
|
-
upgrade: { start: 0, length: sender.length }
|
|
108
|
+
upgrade: upgrade ? upgrade : { start: 0, length: sender.length }
|
|
107
109
|
})
|
|
108
110
|
|
|
109
111
|
if (block) proof.block.value = block
|
|
@@ -122,3 +124,7 @@ async function proof(sender, { index, block = null } = {}) {
|
|
|
122
124
|
|
|
123
125
|
return state.buffer
|
|
124
126
|
}
|
|
127
|
+
|
|
128
|
+
function isNull(x) {
|
|
129
|
+
return x === null
|
|
130
|
+
}
|
package/lib/merkle-tree.js
CHANGED
|
@@ -231,6 +231,11 @@ class MerkleTreeBatch {
|
|
|
231
231
|
|
|
232
232
|
commit(tx) {
|
|
233
233
|
if (tx === undefined) throw INVALID_OPERATION('No database batch was passed')
|
|
234
|
+
|
|
235
|
+
if (this.session.core._repairMode) {
|
|
236
|
+
throw ASSERTION('Cannot commit while repair mode is on')
|
|
237
|
+
}
|
|
238
|
+
|
|
234
239
|
if (!this.commitable()) {
|
|
235
240
|
throw INVALID_OPERATION('Tree was modified during batch, refusing to commit')
|
|
236
241
|
}
|
|
@@ -768,6 +773,18 @@ class MerkleTree {
|
|
|
768
773
|
rx.tryFlush()
|
|
769
774
|
return Promise.all([offset, size])
|
|
770
775
|
}
|
|
776
|
+
|
|
777
|
+
static upgradeLength(proof) {
|
|
778
|
+
const extra = proof.additionalNodes
|
|
779
|
+
|
|
780
|
+
if (extra.length) {
|
|
781
|
+
const last = extra[extra.length - 1]
|
|
782
|
+
const index = flat.rightSpan(last.index)
|
|
783
|
+
return index / 2 + 1
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
return proof.start + proof.length
|
|
787
|
+
}
|
|
771
788
|
}
|
|
772
789
|
|
|
773
790
|
module.exports = {
|
package/lib/replicator.js
CHANGED
|
@@ -31,7 +31,8 @@ const ReceiverQueue = require('./receiver-queue')
|
|
|
31
31
|
const HotswapQueue = require('./hotswap-queue')
|
|
32
32
|
const RemoteBitfield = require('./remote-bitfield')
|
|
33
33
|
const { MerkleTree } = require('./merkle-tree')
|
|
34
|
-
const
|
|
34
|
+
const w = require('./wants')
|
|
35
|
+
const { LocalWants, RemoteWants } = w
|
|
35
36
|
const {
|
|
36
37
|
REQUEST_CANCELLED,
|
|
37
38
|
REQUEST_TIMEOUT,
|
|
@@ -675,6 +676,9 @@ class Peer {
|
|
|
675
676
|
}
|
|
676
677
|
|
|
677
678
|
sendSync() {
|
|
679
|
+
// Skip syncs if in repair mode
|
|
680
|
+
if (this.core._repairMode) return
|
|
681
|
+
|
|
678
682
|
if (this.syncsProcessing !== 0) {
|
|
679
683
|
this.needsSync = true
|
|
680
684
|
return
|
|
@@ -997,55 +1001,92 @@ class Peer {
|
|
|
997
1001
|
priority: 0
|
|
998
1002
|
}
|
|
999
1003
|
|
|
1000
|
-
|
|
1004
|
+
await this.replicator._txLock.lock()
|
|
1005
|
+
try {
|
|
1006
|
+
const remoteLength = Math.max(this.remoteLength, this.pushedLength)
|
|
1001
1007
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1008
|
+
msg.block = {
|
|
1009
|
+
index,
|
|
1010
|
+
nodes: MerkleTree.maxMissingNodes(2 * index, remoteLength)
|
|
1011
|
+
}
|
|
1006
1012
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1013
|
+
if (index >= remoteLength) {
|
|
1014
|
+
msg.upgrade = {
|
|
1015
|
+
start: remoteLength,
|
|
1016
|
+
length: this.core.state.length - remoteLength
|
|
1017
|
+
}
|
|
1011
1018
|
}
|
|
1012
|
-
}
|
|
1013
1019
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1020
|
+
let req = null
|
|
1021
|
+
const batch = this.core.storage.read()
|
|
1022
|
+
try {
|
|
1023
|
+
req = await this._getProof(batch, msg, true)
|
|
1024
|
+
} catch (err) {
|
|
1025
|
+
this.replicator._oninvalidrequest(err, msg, this)
|
|
1026
|
+
return
|
|
1027
|
+
}
|
|
1022
1028
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1029
|
+
if (req === null) return
|
|
1030
|
+
batch.tryFlush()
|
|
1025
1031
|
|
|
1026
|
-
|
|
1032
|
+
await this._fulfillRequest(req, true)
|
|
1033
|
+
} finally {
|
|
1034
|
+
this.replicator._txLock.unlock()
|
|
1035
|
+
}
|
|
1027
1036
|
}
|
|
1028
1037
|
|
|
1029
1038
|
async _handleRequest(msg) {
|
|
1030
|
-
const
|
|
1039
|
+
const locked = !!msg.upgrade && this.remoteAllowPush
|
|
1031
1040
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
: new ProofRequest(msg, null, null, null)
|
|
1041
|
+
if (locked) {
|
|
1042
|
+
// ensure contig push proofs
|
|
1043
|
+
await this.replicator._txLock.lock()
|
|
1044
|
+
const end = msg.upgrade.start + msg.upgrade.length
|
|
1037
1045
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1046
|
+
if (this.pushedLength < end) {
|
|
1047
|
+
msg.upgrade.start = this.pushedLength
|
|
1048
|
+
msg.upgrade.length = end - this.pushedLength
|
|
1049
|
+
}
|
|
1041
1050
|
}
|
|
1042
1051
|
|
|
1043
|
-
|
|
1052
|
+
try {
|
|
1053
|
+
const batch = this.core.storage.read()
|
|
1044
1054
|
|
|
1045
|
-
|
|
1055
|
+
// TODO: could still be answerable if (index, fork) is an ancestor of the current fork
|
|
1056
|
+
const req =
|
|
1057
|
+
msg.fork === this.core.state.fork
|
|
1058
|
+
? await this._getProof(batch, msg, false)
|
|
1059
|
+
: new ProofRequest(msg, null, null, null)
|
|
1060
|
+
|
|
1061
|
+
if (req === null) {
|
|
1062
|
+
this.wireNoData.send({ request: msg.id, reason: INVALID_REQUEST })
|
|
1063
|
+
return
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
batch.tryFlush()
|
|
1067
|
+
|
|
1068
|
+
await this._fulfillRequest(req, false)
|
|
1069
|
+
} finally {
|
|
1070
|
+
if (locked) this.replicator._txLock.unlock()
|
|
1071
|
+
}
|
|
1046
1072
|
}
|
|
1047
1073
|
|
|
1048
1074
|
async _fulfillRequest(req, pushing) {
|
|
1075
|
+
if (this.core._repairMode) {
|
|
1076
|
+
// if cancelled do not reply
|
|
1077
|
+
if (this.remoteRequests.get(req.msg.id) !== req.msg) {
|
|
1078
|
+
return
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// sync from now on, so safe to delete from the map
|
|
1082
|
+
this.remoteRequests.delete(req.msg.id)
|
|
1083
|
+
|
|
1084
|
+
// If cutting off the fulfillment, ensure promise is caught.
|
|
1085
|
+
// While repairing merkle tree nodes can be `nil` so can throw.
|
|
1086
|
+
req.fulfill().catch(safetyCatch)
|
|
1087
|
+
return
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1049
1090
|
const proof = await req.fulfill()
|
|
1050
1091
|
|
|
1051
1092
|
if (!pushing) {
|
|
@@ -1090,7 +1131,7 @@ class Peer {
|
|
|
1090
1131
|
}
|
|
1091
1132
|
|
|
1092
1133
|
if (proof.upgrade) {
|
|
1093
|
-
const remoteLength =
|
|
1134
|
+
const remoteLength = MerkleTree.upgradeLength(proof.upgrade)
|
|
1094
1135
|
if (remoteLength > this.pushedLength) this.pushedLength = remoteLength
|
|
1095
1136
|
}
|
|
1096
1137
|
|
|
@@ -1151,13 +1192,13 @@ class Peer {
|
|
|
1151
1192
|
}
|
|
1152
1193
|
|
|
1153
1194
|
async ondata(data) {
|
|
1154
|
-
if (data.request !== 0) return this._handleData(data)
|
|
1195
|
+
if (data.request !== 0 && !data.upgrade) return this._handleData(data)
|
|
1155
1196
|
|
|
1156
|
-
await this.replicator.
|
|
1197
|
+
await this.replicator._rxLock.lock()
|
|
1157
1198
|
try {
|
|
1158
1199
|
await this._handleData(data)
|
|
1159
1200
|
} finally {
|
|
1160
|
-
this.replicator.
|
|
1201
|
+
this.replicator._rxLock.unlock()
|
|
1161
1202
|
}
|
|
1162
1203
|
}
|
|
1163
1204
|
|
|
@@ -1177,6 +1218,12 @@ class Peer {
|
|
|
1177
1218
|
return
|
|
1178
1219
|
}
|
|
1179
1220
|
|
|
1221
|
+
// Skip accepting data if in repair mode to avoid corruption
|
|
1222
|
+
if (this.core._repairMode) {
|
|
1223
|
+
this.replicator._onnodata(this, req, NOT_AVAILABLE)
|
|
1224
|
+
return
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1180
1227
|
if (req === null && reorg === false && data.block) {
|
|
1181
1228
|
// mark this as inflight to avoid parallel requests
|
|
1182
1229
|
this.replicator._markInflight(data.block.index)
|
|
@@ -1582,6 +1629,8 @@ class Peer {
|
|
|
1582
1629
|
}
|
|
1583
1630
|
|
|
1584
1631
|
_sendBlockRequest(req, b) {
|
|
1632
|
+
if (this.core._repairMode) return
|
|
1633
|
+
|
|
1585
1634
|
req.block = { index: b.index, nodes: 0 }
|
|
1586
1635
|
this.replicator._markInflight(b.index)
|
|
1587
1636
|
|
|
@@ -1682,6 +1731,7 @@ class Peer {
|
|
|
1682
1731
|
}
|
|
1683
1732
|
|
|
1684
1733
|
_addRangeBatch(r, offset, end) {
|
|
1734
|
+
const { WANT_BATCH } = w
|
|
1685
1735
|
while (offset < end) {
|
|
1686
1736
|
const next = this.core.bitfield.findFirst(false, offset)
|
|
1687
1737
|
if (next === -1 || next >= end) return false
|
|
@@ -1697,6 +1747,7 @@ class Peer {
|
|
|
1697
1747
|
}
|
|
1698
1748
|
|
|
1699
1749
|
_populateRangeBatches(r) {
|
|
1750
|
+
const { WANT_BATCH } = w
|
|
1700
1751
|
for (let i = r.batches.length - 1; i >= 0; i--) {
|
|
1701
1752
|
const b = r.batches[i]
|
|
1702
1753
|
const minStart = b.batch * WANT_BATCH
|
|
@@ -1752,6 +1803,7 @@ class Peer {
|
|
|
1752
1803
|
}
|
|
1753
1804
|
|
|
1754
1805
|
_requestRange(r) {
|
|
1806
|
+
const { WANT_BATCH } = w
|
|
1755
1807
|
if (this.syncsProcessing > 0) return false
|
|
1756
1808
|
|
|
1757
1809
|
const { length, fork } = this.core.state
|
|
@@ -1952,7 +2004,9 @@ module.exports = class Replicator {
|
|
|
1952
2004
|
this._reorgs = []
|
|
1953
2005
|
this._ranges = []
|
|
1954
2006
|
|
|
1955
|
-
this.
|
|
2007
|
+
this._rxLock = new Mutex()
|
|
2008
|
+
this._txLock = new Mutex()
|
|
2009
|
+
|
|
1956
2010
|
this._hadPeers = false
|
|
1957
2011
|
this._active = 0
|
|
1958
2012
|
this._ifAvailable = 0
|
package/lib/session-state.js
CHANGED
|
@@ -4,7 +4,7 @@ const assert = require('nanoassert')
|
|
|
4
4
|
const flat = require('flat-tree')
|
|
5
5
|
const quickbit = require('quickbit-universal')
|
|
6
6
|
|
|
7
|
-
const { INVALID_OPERATION, INVALID_SIGNATURE } = require('hypercore-errors')
|
|
7
|
+
const { INVALID_OPERATION, INVALID_SIGNATURE, ASSERTION } = require('hypercore-errors')
|
|
8
8
|
|
|
9
9
|
const Mutex = require('./mutex')
|
|
10
10
|
const Bitfield = require('./bitfield')
|
|
@@ -30,7 +30,7 @@ module.exports = class SessionState {
|
|
|
30
30
|
// merkle state
|
|
31
31
|
this.roots = treeInfo.roots.length ? treeInfo.roots : []
|
|
32
32
|
this.fork = treeInfo.fork || 0
|
|
33
|
-
this.length = MerkleTree.span(this.roots) / 2
|
|
33
|
+
this.length = this.roots.length ? MerkleTree.span(this.roots) / 2 : treeInfo.length
|
|
34
34
|
this.byteLength = MerkleTree.size(this.roots)
|
|
35
35
|
this.prologue = treeInfo.prologue || null
|
|
36
36
|
this.signature = treeInfo.signature || null
|
|
@@ -917,6 +917,10 @@ module.exports = class SessionState {
|
|
|
917
917
|
'Can only commit into default state'
|
|
918
918
|
)
|
|
919
919
|
|
|
920
|
+
if (this.core._repairMode) {
|
|
921
|
+
throw ASSERTION('Cannot commit while repair mode is on')
|
|
922
|
+
}
|
|
923
|
+
|
|
920
924
|
let srcLocked = false
|
|
921
925
|
await this.mutex.lock()
|
|
922
926
|
|
package/lib/wants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
let COMPAT = true
|
|
2
|
+
let BATCH_UINTS = COMPAT ? 65536 : 128
|
|
3
|
+
let BATCH_BYTES = BATCH_UINTS * 4
|
|
4
|
+
let BATCH = BATCH_BYTES * 8 // in bits
|
|
5
5
|
const MAX_REMOTE_BATCHES = 4
|
|
6
6
|
const MAX_RANGE = 8 * 256 * 1024
|
|
7
7
|
const MIN_RANGE = 32 // 4bit ints
|
|
@@ -295,9 +295,18 @@ class RemoteWants {
|
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
function UPDATE_COMPAT(isCompat) {
|
|
299
|
+
COMPAT = isCompat
|
|
300
|
+
BATCH_UINTS = COMPAT ? 65536 : 128
|
|
301
|
+
BATCH_BYTES = BATCH_UINTS * 4
|
|
302
|
+
BATCH = BATCH_BYTES * 8 // in bits
|
|
303
|
+
exports.WANT_BATCH = BATCH
|
|
304
|
+
}
|
|
305
|
+
|
|
298
306
|
exports.LocalWants = LocalWants
|
|
299
307
|
exports.RemoteWants = RemoteWants
|
|
300
308
|
exports.WANT_BATCH = BATCH
|
|
309
|
+
exports.UPDATE_COMPAT = UPDATE_COMPAT
|
|
301
310
|
|
|
302
311
|
function validateBatchRange(range) {
|
|
303
312
|
if (range.length > MAX_RANGE || range.length < MIN_RANGE) {
|