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 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
- const tx = this.core.storage.write()
1055
- for (const node of p.proof.upgrade.nodes) {
1056
- tx.putTreeNode(node)
1057
- }
1058
- await tx.flush()
1063
+ try {
1064
+ const p = await verify(this.core.db, remoteProof)
1065
+ if (!p) return false
1059
1066
 
1060
- this.core.state.mutex.unlock()
1061
- const succeed = p.proof.upgrade.nodes.length !== 0
1062
- if (succeed) {
1063
- this.core.replicator.setPushOnly(false)
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
  }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ SMALL_WANTS: 1
3
+ }
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 { LocalWants, RemoteWants, WANT_BATCH } = require('./wants')
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
@@ -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 { INVALID_OPERATION, INVALID_SIGNATURE, ASSERTION } = require('hypercore-errors')
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
- const COMPAT = true // should be flipped once this is widely deployed
2
- const BATCH_UINTS = COMPAT ? 65536 : 128
3
- const BATCH_BYTES = BATCH_UINTS * 4
4
- const BATCH = BATCH_BYTES * 8 // in bits
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "11.25.0",
3
+ "version": "11.27.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {