hypercore 10.31.4 → 10.31.6

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
@@ -658,7 +658,7 @@ module.exports = class Hypercore extends EventEmitter {
658
658
  const appended = (status & 0b0001) !== 0
659
659
 
660
660
  if (truncated) {
661
- this.replicator.ontruncate(bitfield.start)
661
+ this.replicator.ontruncate(bitfield.start, bitfield.length)
662
662
  }
663
663
 
664
664
  if ((status & 0b10011) !== 0) {
@@ -985,8 +985,7 @@ module.exports = class Hypercore extends EventEmitter {
985
985
  } = typeof opts === 'number' ? { fork: opts } : opts
986
986
 
987
987
  const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
988
-
989
- if (writable === false) throw SESSION_NOT_WRITABLE()
988
+ if (writable === false && (newLength > 0 || fork !== this.core.tree.fork)) throw SESSION_NOT_WRITABLE()
990
989
 
991
990
  await this.core.truncate(newLength, fork, { keyPair, signature })
992
991
 
package/lib/bitfield.js CHANGED
@@ -40,10 +40,10 @@ class BitfieldPage {
40
40
  setRange (start, length, val) {
41
41
  quickbit.fill(this.bitfield, val, start, start + length)
42
42
 
43
- let i = Math.floor(start / 32)
44
- const n = i + Math.ceil(length / 32)
43
+ let i = Math.floor(start / 128)
44
+ const n = i + Math.ceil(length / 128)
45
45
 
46
- while (i < n) this.tree.update(this.offset * 8 + i++ * 32)
46
+ while (i < n) this.tree.update(this.offset * 8 + i++ * 128)
47
47
  }
48
48
 
49
49
  findFirst (val, position) {
@@ -213,6 +213,14 @@ module.exports = class Bitfield {
213
213
  }
214
214
  }
215
215
 
216
+ getBitfield (index, length) {
217
+ const j = index & (BITS_PER_PAGE - 1)
218
+ const i = (index - j) / BITS_PER_PAGE
219
+
220
+ const p = this._pages.get(i)
221
+ return p || null
222
+ }
223
+
216
224
  get (index) {
217
225
  const j = index & (BITS_PER_PAGE - 1)
218
226
  const i = (index - j) / BITS_PER_PAGE
package/lib/compat.js CHANGED
@@ -3,7 +3,8 @@
3
3
  let quickbit = require('quickbit-universal')
4
4
  if (
5
5
  typeof quickbit.findFirst !== 'function' ||
6
- typeof quickbit.findLast !== 'function'
6
+ typeof quickbit.findLast !== 'function' ||
7
+ typeof quickbit.clear !== 'function'
7
8
  ) {
8
9
  // This should always load the fallback from the locally installed version
9
10
  quickbit = require('quickbit-universal/fallback')
package/lib/core.js CHANGED
@@ -375,7 +375,7 @@ module.exports = class Core {
375
375
 
376
376
  try {
377
377
  const batch = await this.tree.truncate(length, fork)
378
- batch.signature = signature || this.verifier.sign(batch, keyPair)
378
+ if (length > 0) batch.signature = signature || this.verifier.sign(batch, keyPair)
379
379
  await this._truncate(batch, null)
380
380
  } finally {
381
381
  this.truncating--
@@ -35,10 +35,10 @@ class RemoteBitfieldPage {
35
35
  setRange (start, length, val) {
36
36
  quickbit.fill(this.bitfield, val, start, start + length)
37
37
 
38
- let i = Math.floor(start / 32)
39
- const n = i + Math.ceil(length / 32)
38
+ let i = Math.floor(start / 128)
39
+ const n = i + Math.ceil(length / 128)
40
40
 
41
- while (i < n) this.tree.update(this.offset * 8 + i++ * 32)
41
+ while (i < n) this.tree.update(this.offset * 8 + i++ * 128)
42
42
  }
43
43
 
44
44
  findFirst (val, position) {
@@ -51,6 +51,11 @@ class RemoteBitfieldPage {
51
51
 
52
52
  insert (start, bitfield) {
53
53
  this.bitfield.set(bitfield, start / 32)
54
+ this.segment.refresh()
55
+ }
56
+
57
+ clear (start, bitfield) {
58
+ quickbit.clear(this.bitfield, { field: bitfield, offset: start })
54
59
  }
55
60
  }
56
61
 
@@ -66,6 +71,10 @@ class RemoteBitfieldSegment {
66
71
  return this.tree.chunks
67
72
  }
68
73
 
74
+ refresh () {
75
+ this.tree = quickbit.Index.from(this.tree.chunks, BYTES_PER_SEGMENT)
76
+ }
77
+
69
78
  add (page) {
70
79
  this.pages[page.index - this.index * PAGES_PER_SEGMENT] = page
71
80
 
@@ -138,6 +147,14 @@ module.exports = class RemoteBitfield {
138
147
  this._segments = new BigSparseArray()
139
148
  }
140
149
 
150
+ getBitfield (index) {
151
+ const j = index & (BITS_PER_PAGE - 1)
152
+ const i = (index - j) / BITS_PER_PAGE
153
+
154
+ const p = this._pages.get(i)
155
+ return p || null
156
+ }
157
+
141
158
  get (index) {
142
159
  const j = index & (BITS_PER_PAGE - 1)
143
160
  const i = (index - j) / BITS_PER_PAGE
@@ -278,4 +295,37 @@ module.exports = class RemoteBitfield {
278
295
 
279
296
  return true
280
297
  }
298
+
299
+ clear (start, bitfield) {
300
+ if (start % 32 !== 0) return false
301
+
302
+ let length = bitfield.byteLength * 8
303
+
304
+ let j = start & (BITS_PER_PAGE - 1)
305
+ let i = (start - j) / BITS_PER_PAGE
306
+
307
+ while (length > 0) {
308
+ let p = this._pages.get(i)
309
+
310
+ if (!p) {
311
+ const k = Math.floor(i / PAGES_PER_SEGMENT)
312
+ const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))
313
+
314
+ p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))
315
+ }
316
+
317
+ const end = Math.min(j + length, BITS_PER_PAGE)
318
+ const range = end - j
319
+
320
+ p.clear(j, bitfield.subarray(0, range / 32))
321
+
322
+ bitfield = bitfield.subarray(range / 32)
323
+
324
+ j = 0
325
+ i++
326
+ length -= range
327
+ }
328
+
329
+ return true
330
+ }
281
331
  }
package/lib/replicator.js CHANGED
@@ -310,7 +310,7 @@ class Peer {
310
310
 
311
311
  this.remoteOpened = false
312
312
  this.remoteBitfield = new RemoteBitfield()
313
- this.skipList = new RemoteBitfield()
313
+ this.missingBlocks = new RemoteBitfield()
314
314
 
315
315
  this.remoteFork = 0
316
316
  this.remoteLength = 0
@@ -350,7 +350,9 @@ class Peer {
350
350
  }
351
351
 
352
352
  broadcastRange (start, length, drop) {
353
- if (drop) this.skipList.setRange(start, length, false)
353
+ if (drop) this._unclearLocalRange(start, length)
354
+ else this._clearLocalRange(start, length)
355
+
354
356
  this.wireRange.send({
355
357
  drop,
356
358
  start,
@@ -699,6 +701,7 @@ class Peer {
699
701
  // might be a fork, verify
700
702
  this._checkIfConflict()
701
703
  }
704
+
702
705
  this.replicator._onnodata(this, req)
703
706
  this.replicator.oninvalid(err, req, data, this)
704
707
  return
@@ -732,10 +735,60 @@ class Peer {
732
735
  }
733
736
 
734
737
  onbitfield ({ start, bitfield }) {
735
- if (this.remoteBitfield.insert(start, bitfield)) {
736
- this.skipList.setRange(start, bitfield.byteLength * 8, false)
737
- this._update()
738
+ this.remoteBitfield.insert(start, bitfield)
739
+ this.missingBlocks.insert(start, bitfield)
740
+ this._clearLocalRange(start, bitfield.byteLength * 8)
741
+ this._update()
742
+ }
743
+
744
+ _clearLocalRange (start, length) {
745
+ if (length === 1) {
746
+ this.missingBlocks.set(start, false)
747
+ return
748
+ }
749
+
750
+ const contig = Math.min(this.core.tree.length, this.core.header.hints.contiguousLength)
751
+
752
+ if (start < contig) {
753
+ const delta = contig - start
754
+ this.missingBlocks.setRange(start, delta)
755
+ start = contig
756
+ length -= delta
757
+ }
758
+
759
+ if ((start & 31) > 0) start -= (start & 31)
760
+
761
+ const end = start + Math.min(length, this.core.tree.length)
762
+ while (start < end) {
763
+ const local = this.core.bitfield.getBitfield(start)
764
+
765
+ if (local && local.bitfield) {
766
+ this.missingBlocks.clear(start, local.bitfield)
767
+ }
768
+
769
+ start += 32768
770
+ }
771
+ }
772
+
773
+ _unclearLocalRange (start, length) {
774
+ if (length === 1) {
775
+ this.missingBlocks.set(start, this.remoteBitfield.get(start))
776
+ return
777
+ }
778
+
779
+ if ((start & 31) > 0) start -= (start & 31)
780
+
781
+ const end = start + Math.min(length, this.remoteLength)
782
+ while (start < end) {
783
+ const remote = this.remoteBitfield.getBitfield(start)
784
+ if (remote && remote.bitfield) {
785
+ this.missingBlocks.insert(start, remote.bitfield)
786
+ }
787
+
788
+ start += 2097152
738
789
  }
790
+
791
+ this._clearLocalRange(start, length)
739
792
  }
740
793
 
741
794
  onrange ({ drop, start, length }) {
@@ -743,14 +796,15 @@ class Peer {
743
796
 
744
797
  if (length === 1) {
745
798
  this.remoteBitfield.setRange(start, length, has)
746
- this.skipList.set(start, false)
799
+ this.missingBlocks.set(start, has && !this.core.bitfield.get(start))
747
800
  } else {
748
801
  const rangeStart = this.remoteBitfield.findFirst(!has, start)
749
802
  const rangeLength = length - (rangeStart - start)
750
803
 
751
804
  if (rangeLength > 0) {
752
805
  this.remoteBitfield.setRange(rangeStart, rangeLength, has)
753
- this.skipList.setRange(rangeStart, rangeLength, false)
806
+ this.missingBlocks.setRange(rangeStart, rangeLength, has)
807
+ if (has) this._clearLocalRange(rangeStart, rangeLength)
754
808
  }
755
809
  }
756
810
 
@@ -942,74 +996,73 @@ class Peer {
942
996
  return true
943
997
  }
944
998
 
945
- _requestRange (r) {
946
- const { length, fork } = this.core.tree
999
+ _requestRangeBlock (index, length) {
1000
+ if (this.core.bitfield.get(index) === true || !this._hasTreeParent(index)) return false
947
1001
 
948
- const end = Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength)
949
- if (end < r.start || fork !== this.remoteFork) return false
1002
+ const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
1003
+ if (b.inflight.length > 0) return false
950
1004
 
951
- const len = end - r.start
952
- const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
1005
+ const req = this._makeRequest(index >= length, b.priority)
953
1006
 
954
- // TODO: we should weight this to request blocks < .length first
955
- // as they are "cheaper" and will trigger an auto upgrade if possible
956
- // If no blocks < .length is avaible then try the "needs upgrade" range
1007
+ // If the request cannot be satisfied, dealloc the block request if no one is subscribed to it
1008
+ if (req === null) {
1009
+ b.gc()
1010
+ return false
1011
+ }
957
1012
 
958
- let wrapped = 0
1013
+ req.block = { index, nodes: 0 }
959
1014
 
960
- for (let i = 0; i < len && wrapped < 2; i++) {
961
- let index = off + i
962
- if (index >= end) index -= len
1015
+ b.inflight.push(req)
1016
+ this._send(req)
963
1017
 
964
- if (r.blocks !== null) {
965
- index = r.blocks[index]
966
- } else { // TODO: make this loop better (something like a for loop that skips with the skip list)
967
- index = this.skipList.findFirst(false, index)
968
- if (index === -1 || index >= end) {
969
- wrapped++
970
- index = this.skipList.findFirst(false, r.start)
971
- if (index === -1 || index >= end) {
972
- return false
973
- }
974
- }
975
- }
1018
+ // Don't think this will ever happen, as the pending queue is drained before the range queue
1019
+ // but doesn't hurt to check this explicitly here also.
1020
+ if (b.queued) b.queued = false
1021
+ return true
1022
+ }
976
1023
 
977
- if (this.remoteBitfield.get(index) === false || this.core.bitfield.get(index) === true) {
978
- this.skipList.set(index, true)
979
- continue
980
- }
1024
+ _requestRange (r) {
1025
+ const { length, fork } = this.core.tree
981
1026
 
982
- if (!this._hasTreeParent(index)) {
983
- continue
1027
+ if (r.blocks) {
1028
+ let min = -1
1029
+ let max = -1
1030
+ for (let i = r.start; i < r.end; i++) {
1031
+ const index = r.blocks[i]
1032
+ if (min === -1 || index < min) min = index
1033
+ if (max === -1 || index > max) max = index
1034
+ if (this.missingBlocks.get(index) === true && this._requestRangeBlock(index, length)) return true
984
1035
  }
1036
+ if (min > -1) this._maybeWant(min, max - min)
1037
+ return false
1038
+ }
985
1039
 
986
- const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
1040
+ const end = Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength)
1041
+ if (end < r.start || fork !== this.remoteFork) return false
987
1042
 
988
- if (b.inflight.length > 0) {
989
- this.skipList.set(index, true)
990
- continue
991
- }
1043
+ const len = end - r.start
1044
+ const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
992
1045
 
993
- const req = this._makeRequest(index >= length, b.priority)
1046
+ let i = off
994
1047
 
995
- // If the request cannot be satisfied, dealloc the block request if no one is subscribed to it
996
- if (req === null) {
997
- b.gc()
998
- return false
999
- }
1048
+ while (true) {
1049
+ i = this.missingBlocks.findFirst(true, i)
1000
1050
 
1001
- this.skipList.set(index, true)
1051
+ if (i === -1 || i >= end) break
1002
1052
 
1003
- req.block = { index, nodes: 0 }
1053
+ if (this._requestRangeBlock(i, length)) return true
1054
+ i++
1055
+ }
1004
1056
 
1005
- b.inflight.push(req)
1006
- this._send(req)
1057
+ i = r.start
1007
1058
 
1008
- // Don't think this will ever happen, as the pending queue is drained before the range queue
1009
- // but doesn't hurt to check this explicitly here also.
1010
- if (b.queued) b.queued = false
1059
+ while (true) {
1060
+ i = this.missingBlocks.findFirst(true, i)
1011
1061
 
1012
- return true
1062
+ if (i === -1 || i >= off) break
1063
+
1064
+ if (this.core.bitfield.get(i) === false && this._hasTreeParent(i) && this._requestRangeBlock(i, length)) return true
1065
+ i++
1013
1066
  }
1014
1067
 
1015
1068
  this._maybeWant(r.start, len)
@@ -1197,7 +1250,7 @@ module.exports = class Replicator {
1197
1250
  }
1198
1251
 
1199
1252
  // Called externally when a truncation upgrade has been processed
1200
- ontruncate (newLength) {
1253
+ ontruncate (newLength, truncated) {
1201
1254
  const notify = []
1202
1255
 
1203
1256
  for (const blk of this._blocks) {
@@ -1211,6 +1264,8 @@ module.exports = class Replicator {
1211
1264
  blk.detach(r, SNAPSHOT_NOT_AVAILABLE())
1212
1265
  }
1213
1266
  }
1267
+
1268
+ for (const peer of this.peers) peer._unclearLocalRange(newLength, truncated)
1214
1269
  }
1215
1270
 
1216
1271
  // Called externally when a upgrade has been processed
@@ -1526,9 +1581,9 @@ module.exports = class Replicator {
1526
1581
 
1527
1582
  if (b === null || removeInflight(b.inflight, req) === false) return
1528
1583
 
1529
- if (isBlock && this.core.bitfield.get(index) === false) {
1530
- for (const peer of this.peers) peer.skipList.set(index, false)
1531
- }
1584
+ // if (isBlock && this.core.bitfield.get(index) === false) {
1585
+ // for (const peer of this.peers) peer.skipList.set(index, false)
1586
+ // }
1532
1587
 
1533
1588
  if (b.refs.length > 0 && isBlock === true) {
1534
1589
  this._queueBlock(b)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.31.4",
3
+ "version": "10.31.6",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -48,7 +48,7 @@
48
48
  "hypercore-id-encoding": "^1.2.0",
49
49
  "is-options": "^1.0.1",
50
50
  "protomux": "^3.5.0",
51
- "quickbit-universal": "^2.1.1",
51
+ "quickbit-universal": "^2.2.0",
52
52
  "random-access-file": "^4.0.0",
53
53
  "random-array-iterator": "^1.0.0",
54
54
  "safety-catch": "^1.0.1",