hypercore 11.25.0 → 11.27.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 +27 -12
- package/lib/core.js +6 -3
- package/lib/feature-flags.js +3 -0
- package/lib/replicator.js +6 -1
- package/lib/session-state.js +47 -1
- package/lib/wants.js +13 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -11,6 +11,9 @@ const safetyCatch = require('safety-catch')
|
|
|
11
11
|
const unslab = require('unslab')
|
|
12
12
|
const flat = require('flat-tree')
|
|
13
13
|
|
|
14
|
+
const { SMALL_WANTS } = require('./lib/feature-flags')
|
|
15
|
+
const { UPDATE_COMPAT } = require('./lib/wants')
|
|
16
|
+
|
|
14
17
|
const inspect = require('./lib/inspect')
|
|
15
18
|
const Core = require('./lib/core')
|
|
16
19
|
const Info = require('./lib/info')
|
|
@@ -107,6 +110,13 @@ class Hypercore extends EventEmitter {
|
|
|
107
110
|
|
|
108
111
|
static DefaultEncryption = DefaultEncryption
|
|
109
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
|
+
|
|
110
120
|
static key(manifest, { compat, version, namespace } = {}) {
|
|
111
121
|
if (b4a.isBuffer(manifest)) {
|
|
112
122
|
manifest = { version, signers: [{ publicKey: manifest, namespace }] }
|
|
@@ -1047,22 +1057,27 @@ class Hypercore extends EventEmitter {
|
|
|
1047
1057
|
async recoverFromRemoteProof(remoteProof) {
|
|
1048
1058
|
this.core.replicator.setPushOnly(true)
|
|
1049
1059
|
this.core._repairMode = true
|
|
1060
|
+
|
|
1050
1061
|
await this.core.state.mutex.lock()
|
|
1051
|
-
const p = await verify(this.core.db, remoteProof)
|
|
1052
|
-
if (!p) return false
|
|
1053
1062
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
await tx.flush()
|
|
1063
|
+
try {
|
|
1064
|
+
const p = await verify(this.core.db, remoteProof)
|
|
1065
|
+
if (!p) return false
|
|
1059
1066
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1067
|
+
const tx = this.core.storage.write()
|
|
1068
|
+
for (const node of p.proof.upgrade.nodes) {
|
|
1069
|
+
tx.putTreeNode(node)
|
|
1070
|
+
}
|
|
1071
|
+
await tx.flush()
|
|
1072
|
+
|
|
1073
|
+
const succeed = p.proof.upgrade.nodes.length !== 0
|
|
1074
|
+
if (succeed) {
|
|
1075
|
+
this.core.replicator.setPushOnly(false)
|
|
1076
|
+
}
|
|
1077
|
+
return succeed
|
|
1078
|
+
} finally {
|
|
1079
|
+
this.core.state.mutex.unlock()
|
|
1064
1080
|
}
|
|
1065
|
-
return succeed
|
|
1066
1081
|
}
|
|
1067
1082
|
|
|
1068
1083
|
recoverTreeNodeFromPeers() {
|
package/lib/core.js
CHANGED
|
@@ -204,7 +204,8 @@ module.exports = class Core {
|
|
|
204
204
|
hints: {
|
|
205
205
|
reorgs: [],
|
|
206
206
|
contiguousLength: 0,
|
|
207
|
-
remoteContiguousLength: 0
|
|
207
|
+
remoteContiguousLength: 0,
|
|
208
|
+
recovering: 0
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -553,7 +554,8 @@ module.exports = class Core {
|
|
|
553
554
|
|
|
554
555
|
tx.setHints({
|
|
555
556
|
contiguousLength: this.header.hints.contiguousLength,
|
|
556
|
-
remoteContiguousLength: this.header.hints.remoteContiguousLength
|
|
557
|
+
remoteContiguousLength: this.header.hints.remoteContiguousLength,
|
|
558
|
+
recovering: this.header.hints.recovering
|
|
557
559
|
})
|
|
558
560
|
|
|
559
561
|
await tx.flush()
|
|
@@ -963,7 +965,8 @@ function parseHeader(info) {
|
|
|
963
965
|
hints: {
|
|
964
966
|
reorgs: [],
|
|
965
967
|
contiguousLength: info.hints ? info.hints.contiguousLength : 0,
|
|
966
|
-
remoteContiguousLength: info.hints ? info.hints.remoteContiguousLength : 0
|
|
968
|
+
remoteContiguousLength: info.hints ? info.hints.remoteContiguousLength : 0,
|
|
969
|
+
recovering: info.recovering || 0
|
|
967
970
|
}
|
|
968
971
|
}
|
|
969
972
|
}
|
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,
|
|
@@ -986,6 +987,7 @@ class Peer {
|
|
|
986
987
|
async push(index) {
|
|
987
988
|
if (!this.remoteAllowPush) return
|
|
988
989
|
if (!this.remoteDownloading) return
|
|
990
|
+
if (this.core.state.length <= index) return
|
|
989
991
|
if (this.core.state.fork !== this.remoteFork) return
|
|
990
992
|
if (this.remoteBitfield.get(index)) return
|
|
991
993
|
|
|
@@ -1730,6 +1732,7 @@ class Peer {
|
|
|
1730
1732
|
}
|
|
1731
1733
|
|
|
1732
1734
|
_addRangeBatch(r, offset, end) {
|
|
1735
|
+
const { WANT_BATCH } = w
|
|
1733
1736
|
while (offset < end) {
|
|
1734
1737
|
const next = this.core.bitfield.findFirst(false, offset)
|
|
1735
1738
|
if (next === -1 || next >= end) return false
|
|
@@ -1745,6 +1748,7 @@ class Peer {
|
|
|
1745
1748
|
}
|
|
1746
1749
|
|
|
1747
1750
|
_populateRangeBatches(r) {
|
|
1751
|
+
const { WANT_BATCH } = w
|
|
1748
1752
|
for (let i = r.batches.length - 1; i >= 0; i--) {
|
|
1749
1753
|
const b = r.batches[i]
|
|
1750
1754
|
const minStart = b.batch * WANT_BATCH
|
|
@@ -1800,6 +1804,7 @@ class Peer {
|
|
|
1800
1804
|
}
|
|
1801
1805
|
|
|
1802
1806
|
_requestRange(r) {
|
|
1807
|
+
const { WANT_BATCH } = w
|
|
1803
1808
|
if (this.syncsProcessing > 0) return false
|
|
1804
1809
|
|
|
1805
1810
|
const { length, fork } = this.core.state
|
package/lib/session-state.js
CHANGED
|
@@ -4,7 +4,13 @@ const assert = require('nanoassert')
|
|
|
4
4
|
const flat = require('flat-tree')
|
|
5
5
|
const quickbit = require('quickbit-universal')
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
INVALID_OPERATION,
|
|
9
|
+
INVALID_SIGNATURE,
|
|
10
|
+
ASSERTION,
|
|
11
|
+
SESSION_CLOSED,
|
|
12
|
+
SESSION_NOT_WRITABLE
|
|
13
|
+
} = require('hypercore-errors')
|
|
8
14
|
|
|
9
15
|
const Mutex = require('./mutex')
|
|
10
16
|
const Bitfield = require('./bitfield')
|
|
@@ -562,9 +568,47 @@ module.exports = class SessionState {
|
|
|
562
568
|
}
|
|
563
569
|
}
|
|
564
570
|
|
|
571
|
+
// simple tool to wait for other peers to catch us up
|
|
572
|
+
async _waitForRecovery() {
|
|
573
|
+
const length = this.length
|
|
574
|
+
const replicator = this.core.replicator
|
|
575
|
+
|
|
576
|
+
while (!this.closing) {
|
|
577
|
+
if (replicator.peers.length < 1) {
|
|
578
|
+
await new Promise((resolve) => setTimeout(resolve, 5000))
|
|
579
|
+
continue
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
await new Promise((resolve) => setTimeout(resolve, 5000))
|
|
583
|
+
|
|
584
|
+
const peers = replicator.peers
|
|
585
|
+
|
|
586
|
+
if (peers.length < 1 || this.closing) continue
|
|
587
|
+
if (peers[0].remoteLength > this.length) continue
|
|
588
|
+
|
|
589
|
+
if (this.length !== length) {
|
|
590
|
+
throw SESSION_NOT_WRITABLE('core recovered during append')
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
this.core.header.hints.recovering = 0
|
|
594
|
+
|
|
595
|
+
await this.mutex.lock()
|
|
596
|
+
try {
|
|
597
|
+
await this.core.flushHints()
|
|
598
|
+
return
|
|
599
|
+
} finally {
|
|
600
|
+
this.mutex.unlock()
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
throw SESSION_CLOSED('Cannot recover a closed session', this.discoveryKey)
|
|
605
|
+
}
|
|
606
|
+
|
|
565
607
|
async append(values, { signature, keyPair, preappend, postappend, maxLength = -1 } = {}) {
|
|
566
608
|
if (!keyPair && this.isDefault()) keyPair = this.core.header.keyPair
|
|
567
609
|
|
|
610
|
+
if (this.isDefault() && this.core.header.hints.recovering) await this._waitForRecovery()
|
|
611
|
+
|
|
568
612
|
await this.mutex.lock()
|
|
569
613
|
|
|
570
614
|
try {
|
|
@@ -921,6 +965,8 @@ module.exports = class SessionState {
|
|
|
921
965
|
throw ASSERTION('Cannot commit while repair mode is on')
|
|
922
966
|
}
|
|
923
967
|
|
|
968
|
+
if (this.core.header.hints.recovering) await this._waitForRecovery()
|
|
969
|
+
|
|
924
970
|
let srcLocked = false
|
|
925
971
|
await this.mutex.lock()
|
|
926
972
|
|
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) {
|