hypercore 11.24.0 → 11.25.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 +60 -0
- package/lib/core.js +85 -6
- package/lib/fully-remote-proof.js +9 -3
- package/lib/merkle-tree.js +17 -0
- package/lib/replicator.js +87 -37
- package/lib/session-state.js +6 -2
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -9,6 +9,7 @@ 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')
|
|
12
13
|
|
|
13
14
|
const inspect = require('./lib/inspect')
|
|
14
15
|
const Core = require('./lib/core')
|
|
@@ -20,6 +21,7 @@ const Replicator = require('./lib/replicator')
|
|
|
20
21
|
const { manifestHash, createManifest } = require('./lib/verifier')
|
|
21
22
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
22
23
|
const { MerkleTree } = require('./lib/merkle-tree')
|
|
24
|
+
const { proof, verify } = require('./lib/fully-remote-proof')
|
|
23
25
|
const {
|
|
24
26
|
ASSERTION,
|
|
25
27
|
BAD_ARGUMENT,
|
|
@@ -85,6 +87,8 @@ class Hypercore extends EventEmitter {
|
|
|
85
87
|
this._findingPeers = 0
|
|
86
88
|
this._active = opts.weak ? !!opts.active : opts.active !== false
|
|
87
89
|
|
|
90
|
+
this.waits = 0
|
|
91
|
+
|
|
88
92
|
this._sessionIndex = -1
|
|
89
93
|
this._stateIndex = -1 // maintained by session state
|
|
90
94
|
this._monitorIndex = -1 // maintained by replication state
|
|
@@ -284,6 +288,15 @@ class Hypercore extends EventEmitter {
|
|
|
284
288
|
throw err
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
// Setup automatic recovery if in repair mode
|
|
292
|
+
if (this.core._repairMode) {
|
|
293
|
+
const recoverTreeNodeFromPeersBound = this.recoverTreeNodeFromPeers.bind(this)
|
|
294
|
+
this.once('repaired', () => {
|
|
295
|
+
this.off('peer-add', recoverTreeNodeFromPeersBound)
|
|
296
|
+
})
|
|
297
|
+
this.on('peer-add', recoverTreeNodeFromPeersBound)
|
|
298
|
+
}
|
|
299
|
+
|
|
287
300
|
this.emit('ready')
|
|
288
301
|
|
|
289
302
|
// if we are a weak session the core might have closed...
|
|
@@ -858,6 +871,7 @@ class Hypercore extends EventEmitter {
|
|
|
858
871
|
|
|
859
872
|
if (!this._shouldWait(opts, this.wait)) return null
|
|
860
873
|
|
|
874
|
+
this.waits++
|
|
861
875
|
if (opts && opts.onwait) opts.onwait(index, this)
|
|
862
876
|
if (this.onwait) this.onwait(index, this)
|
|
863
877
|
|
|
@@ -1021,6 +1035,52 @@ class Hypercore extends EventEmitter {
|
|
|
1021
1035
|
return batch
|
|
1022
1036
|
}
|
|
1023
1037
|
|
|
1038
|
+
generateRemoteProofForTreeNode(treeNodeIndex) {
|
|
1039
|
+
const blockProofIndex = flat.rightSpan(treeNodeIndex) / 2
|
|
1040
|
+
return proof(this, {
|
|
1041
|
+
index: blockProofIndex,
|
|
1042
|
+
// + 1 to length so the block is included
|
|
1043
|
+
upgrade: { start: 0, length: blockProofIndex + 1 }
|
|
1044
|
+
})
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
async recoverFromRemoteProof(remoteProof) {
|
|
1048
|
+
this.core.replicator.setPushOnly(true)
|
|
1049
|
+
this.core._repairMode = true
|
|
1050
|
+
await this.core.state.mutex.lock()
|
|
1051
|
+
const p = await verify(this.core.db, remoteProof)
|
|
1052
|
+
if (!p) return false
|
|
1053
|
+
|
|
1054
|
+
const tx = this.core.storage.write()
|
|
1055
|
+
for (const node of p.proof.upgrade.nodes) {
|
|
1056
|
+
tx.putTreeNode(node)
|
|
1057
|
+
}
|
|
1058
|
+
await tx.flush()
|
|
1059
|
+
|
|
1060
|
+
this.core.state.mutex.unlock()
|
|
1061
|
+
const succeed = p.proof.upgrade.nodes.length !== 0
|
|
1062
|
+
if (succeed) {
|
|
1063
|
+
this.core.replicator.setPushOnly(false)
|
|
1064
|
+
}
|
|
1065
|
+
return succeed
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
recoverTreeNodeFromPeers() {
|
|
1069
|
+
this.core.replicator.setPushOnly(true)
|
|
1070
|
+
|
|
1071
|
+
for (const peer of this.core.replicator.peers) {
|
|
1072
|
+
const req = {
|
|
1073
|
+
id: 0,
|
|
1074
|
+
fork: this.fork,
|
|
1075
|
+
upgrade: {
|
|
1076
|
+
start: 0,
|
|
1077
|
+
length: this.length
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
peer.wireRequest.send(req)
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1024
1084
|
registerExtension(name, handlers = {}) {
|
|
1025
1085
|
if (this.extensions.has(name)) {
|
|
1026
1086
|
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
|
@@ -675,6 +675,9 @@ class Peer {
|
|
|
675
675
|
}
|
|
676
676
|
|
|
677
677
|
sendSync() {
|
|
678
|
+
// Skip syncs if in repair mode
|
|
679
|
+
if (this.core._repairMode) return
|
|
680
|
+
|
|
678
681
|
if (this.syncsProcessing !== 0) {
|
|
679
682
|
this.needsSync = true
|
|
680
683
|
return
|
|
@@ -997,55 +1000,92 @@ class Peer {
|
|
|
997
1000
|
priority: 0
|
|
998
1001
|
}
|
|
999
1002
|
|
|
1000
|
-
|
|
1003
|
+
await this.replicator._txLock.lock()
|
|
1004
|
+
try {
|
|
1005
|
+
const remoteLength = Math.max(this.remoteLength, this.pushedLength)
|
|
1001
1006
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1007
|
+
msg.block = {
|
|
1008
|
+
index,
|
|
1009
|
+
nodes: MerkleTree.maxMissingNodes(2 * index, remoteLength)
|
|
1010
|
+
}
|
|
1006
1011
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1012
|
+
if (index >= remoteLength) {
|
|
1013
|
+
msg.upgrade = {
|
|
1014
|
+
start: remoteLength,
|
|
1015
|
+
length: this.core.state.length - remoteLength
|
|
1016
|
+
}
|
|
1011
1017
|
}
|
|
1012
|
-
}
|
|
1013
1018
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1019
|
+
let req = null
|
|
1020
|
+
const batch = this.core.storage.read()
|
|
1021
|
+
try {
|
|
1022
|
+
req = await this._getProof(batch, msg, true)
|
|
1023
|
+
} catch (err) {
|
|
1024
|
+
this.replicator._oninvalidrequest(err, msg, this)
|
|
1025
|
+
return
|
|
1026
|
+
}
|
|
1022
1027
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1028
|
+
if (req === null) return
|
|
1029
|
+
batch.tryFlush()
|
|
1025
1030
|
|
|
1026
|
-
|
|
1031
|
+
await this._fulfillRequest(req, true)
|
|
1032
|
+
} finally {
|
|
1033
|
+
this.replicator._txLock.unlock()
|
|
1034
|
+
}
|
|
1027
1035
|
}
|
|
1028
1036
|
|
|
1029
1037
|
async _handleRequest(msg) {
|
|
1030
|
-
const
|
|
1038
|
+
const locked = !!msg.upgrade && this.remoteAllowPush
|
|
1031
1039
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
: new ProofRequest(msg, null, null, null)
|
|
1040
|
+
if (locked) {
|
|
1041
|
+
// ensure contig push proofs
|
|
1042
|
+
await this.replicator._txLock.lock()
|
|
1043
|
+
const end = msg.upgrade.start + msg.upgrade.length
|
|
1037
1044
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1045
|
+
if (this.pushedLength < end) {
|
|
1046
|
+
msg.upgrade.start = this.pushedLength
|
|
1047
|
+
msg.upgrade.length = end - this.pushedLength
|
|
1048
|
+
}
|
|
1041
1049
|
}
|
|
1042
1050
|
|
|
1043
|
-
|
|
1051
|
+
try {
|
|
1052
|
+
const batch = this.core.storage.read()
|
|
1044
1053
|
|
|
1045
|
-
|
|
1054
|
+
// TODO: could still be answerable if (index, fork) is an ancestor of the current fork
|
|
1055
|
+
const req =
|
|
1056
|
+
msg.fork === this.core.state.fork
|
|
1057
|
+
? await this._getProof(batch, msg, false)
|
|
1058
|
+
: new ProofRequest(msg, null, null, null)
|
|
1059
|
+
|
|
1060
|
+
if (req === null) {
|
|
1061
|
+
this.wireNoData.send({ request: msg.id, reason: INVALID_REQUEST })
|
|
1062
|
+
return
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
batch.tryFlush()
|
|
1066
|
+
|
|
1067
|
+
await this._fulfillRequest(req, false)
|
|
1068
|
+
} finally {
|
|
1069
|
+
if (locked) this.replicator._txLock.unlock()
|
|
1070
|
+
}
|
|
1046
1071
|
}
|
|
1047
1072
|
|
|
1048
1073
|
async _fulfillRequest(req, pushing) {
|
|
1074
|
+
if (this.core._repairMode) {
|
|
1075
|
+
// if cancelled do not reply
|
|
1076
|
+
if (this.remoteRequests.get(req.msg.id) !== req.msg) {
|
|
1077
|
+
return
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// sync from now on, so safe to delete from the map
|
|
1081
|
+
this.remoteRequests.delete(req.msg.id)
|
|
1082
|
+
|
|
1083
|
+
// If cutting off the fulfillment, ensure promise is caught.
|
|
1084
|
+
// While repairing merkle tree nodes can be `nil` so can throw.
|
|
1085
|
+
req.fulfill().catch(safetyCatch)
|
|
1086
|
+
return
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1049
1089
|
const proof = await req.fulfill()
|
|
1050
1090
|
|
|
1051
1091
|
if (!pushing) {
|
|
@@ -1090,7 +1130,7 @@ class Peer {
|
|
|
1090
1130
|
}
|
|
1091
1131
|
|
|
1092
1132
|
if (proof.upgrade) {
|
|
1093
|
-
const remoteLength =
|
|
1133
|
+
const remoteLength = MerkleTree.upgradeLength(proof.upgrade)
|
|
1094
1134
|
if (remoteLength > this.pushedLength) this.pushedLength = remoteLength
|
|
1095
1135
|
}
|
|
1096
1136
|
|
|
@@ -1151,13 +1191,13 @@ class Peer {
|
|
|
1151
1191
|
}
|
|
1152
1192
|
|
|
1153
1193
|
async ondata(data) {
|
|
1154
|
-
if (data.request !== 0) return this._handleData(data)
|
|
1194
|
+
if (data.request !== 0 && !data.upgrade) return this._handleData(data)
|
|
1155
1195
|
|
|
1156
|
-
await this.replicator.
|
|
1196
|
+
await this.replicator._rxLock.lock()
|
|
1157
1197
|
try {
|
|
1158
1198
|
await this._handleData(data)
|
|
1159
1199
|
} finally {
|
|
1160
|
-
this.replicator.
|
|
1200
|
+
this.replicator._rxLock.unlock()
|
|
1161
1201
|
}
|
|
1162
1202
|
}
|
|
1163
1203
|
|
|
@@ -1177,6 +1217,12 @@ class Peer {
|
|
|
1177
1217
|
return
|
|
1178
1218
|
}
|
|
1179
1219
|
|
|
1220
|
+
// Skip accepting data if in repair mode to avoid corruption
|
|
1221
|
+
if (this.core._repairMode) {
|
|
1222
|
+
this.replicator._onnodata(this, req, NOT_AVAILABLE)
|
|
1223
|
+
return
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1180
1226
|
if (req === null && reorg === false && data.block) {
|
|
1181
1227
|
// mark this as inflight to avoid parallel requests
|
|
1182
1228
|
this.replicator._markInflight(data.block.index)
|
|
@@ -1582,6 +1628,8 @@ class Peer {
|
|
|
1582
1628
|
}
|
|
1583
1629
|
|
|
1584
1630
|
_sendBlockRequest(req, b) {
|
|
1631
|
+
if (this.core._repairMode) return
|
|
1632
|
+
|
|
1585
1633
|
req.block = { index: b.index, nodes: 0 }
|
|
1586
1634
|
this.replicator._markInflight(b.index)
|
|
1587
1635
|
|
|
@@ -1952,7 +2000,9 @@ module.exports = class Replicator {
|
|
|
1952
2000
|
this._reorgs = []
|
|
1953
2001
|
this._ranges = []
|
|
1954
2002
|
|
|
1955
|
-
this.
|
|
2003
|
+
this._rxLock = new Mutex()
|
|
2004
|
+
this._txLock = new Mutex()
|
|
2005
|
+
|
|
1956
2006
|
this._hadPeers = false
|
|
1957
2007
|
this._active = 0
|
|
1958
2008
|
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
|
|