hypercore 10.31.5 → 10.31.7

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) {
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')
@@ -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
 
@@ -60,14 +65,22 @@ class RemoteBitfieldSegment {
60
65
  this.offset = index * BYTES_PER_SEGMENT
61
66
  this.tree = quickbit.Index.from([], BYTES_PER_SEGMENT)
62
67
  this.pages = new Array(PAGES_PER_SEGMENT)
68
+ this.pagesLength = 0
63
69
  }
64
70
 
65
71
  get chunks () {
66
72
  return this.tree.chunks
67
73
  }
68
74
 
75
+ refresh () {
76
+ this.tree = quickbit.Index.from(this.tree.chunks, BYTES_PER_SEGMENT)
77
+ }
78
+
69
79
  add (page) {
70
- this.pages[page.index - this.index * PAGES_PER_SEGMENT] = page
80
+ const pageIndex = page.index - this.index * PAGES_PER_SEGMENT
81
+ if (pageIndex >= this.pagesLength) this.pagesLength = pageIndex + 1
82
+
83
+ this.pages[pageIndex] = page
71
84
 
72
85
  const chunk = { field: page.bitfield, offset: page.offset }
73
86
 
@@ -89,7 +102,7 @@ class RemoteBitfieldSegment {
89
102
 
90
103
  if (i >= PAGES_PER_SEGMENT) return -1
91
104
 
92
- while (i < this.pages.length) {
105
+ while (i < this.pagesLength) {
93
106
  const p = this.pages[i]
94
107
 
95
108
  let index = -1
@@ -138,6 +151,14 @@ module.exports = class RemoteBitfield {
138
151
  this._segments = new BigSparseArray()
139
152
  }
140
153
 
154
+ getBitfield (index) {
155
+ const j = index & (BITS_PER_PAGE - 1)
156
+ const i = (index - j) / BITS_PER_PAGE
157
+
158
+ const p = this._pages.get(i)
159
+ return p || null
160
+ }
161
+
141
162
  get (index) {
142
163
  const j = index & (BITS_PER_PAGE - 1)
143
164
  const i = (index - j) / BITS_PER_PAGE
@@ -278,4 +299,37 @@ module.exports = class RemoteBitfield {
278
299
 
279
300
  return true
280
301
  }
302
+
303
+ clear (start, bitfield) {
304
+ if (start % 32 !== 0) return false
305
+
306
+ let length = bitfield.byteLength * 8
307
+
308
+ let j = start & (BITS_PER_PAGE - 1)
309
+ let i = (start - j) / BITS_PER_PAGE
310
+
311
+ while (length > 0) {
312
+ let p = this._pages.get(i)
313
+
314
+ if (!p) {
315
+ const k = Math.floor(i / PAGES_PER_SEGMENT)
316
+ const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))
317
+
318
+ p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))
319
+ }
320
+
321
+ const end = Math.min(j + length, BITS_PER_PAGE)
322
+ const range = end - j
323
+
324
+ p.clear(j, bitfield.subarray(0, range / 32))
325
+
326
+ bitfield = bitfield.subarray(range / 32)
327
+
328
+ j = 0
329
+ i++
330
+ length -= range
331
+ }
332
+
333
+ return true
334
+ }
281
335
  }
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,25 +735,85 @@ 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, this.remoteBitfield.get(start) && !this.core.bitfield.get(start))
747
+ return
748
+ }
749
+
750
+ const contig = Math.min(this.core.tree.length, this.core.header.hints.contiguousLength)
751
+
752
+ if (start + length < contig) {
753
+ const delta = contig - start
754
+ this.missingBlocks.setRange(start, delta, false)
755
+ return
756
+ }
757
+
758
+ const rem = start & 32767
759
+ if (rem > 0) {
760
+ start -= rem
761
+ length += rem
762
+ }
763
+
764
+ const end = start + Math.min(length, this.core.tree.length)
765
+ while (start < end) {
766
+ const local = this.core.bitfield.getBitfield(start)
767
+
768
+ if (local && local.bitfield) {
769
+ this.missingBlocks.clear(start, local.bitfield)
770
+ }
771
+
772
+ start += 32768
738
773
  }
739
774
  }
740
775
 
776
+ _unclearLocalRange (start, length) {
777
+ if (length === 1) {
778
+ this.missingBlocks.set(start, this.remoteBitfield.get(start) && !this.core.bitfield.get(start))
779
+ return
780
+ }
781
+
782
+ const rem = start & 2097151
783
+ if (rem > 0) {
784
+ start -= rem
785
+ length += rem
786
+ }
787
+
788
+ const fixedStart = start
789
+
790
+ const end = start + Math.min(length, this.remoteLength)
791
+ while (start < end) {
792
+ const remote = this.remoteBitfield.getBitfield(start)
793
+ if (remote && remote.bitfield) {
794
+ this.missingBlocks.insert(start, remote.bitfield)
795
+ }
796
+
797
+ start += 2097152
798
+ }
799
+
800
+ this._clearLocalRange(fixedStart, length)
801
+ }
802
+
741
803
  onrange ({ drop, start, length }) {
742
804
  const has = drop === false
743
805
 
744
806
  if (length === 1) {
745
- this.remoteBitfield.setRange(start, length, has)
746
- this.skipList.set(start, false)
807
+ this.remoteBitfield.set(start, has)
808
+ this.missingBlocks.set(start, has && !this.core.bitfield.get(start))
747
809
  } else {
748
810
  const rangeStart = this.remoteBitfield.findFirst(!has, start)
749
811
  const rangeLength = length - (rangeStart - start)
750
812
 
751
813
  if (rangeLength > 0) {
752
814
  this.remoteBitfield.setRange(rangeStart, rangeLength, has)
753
- this.skipList.setRange(rangeStart, rangeLength, false)
815
+ this.missingBlocks.setRange(rangeStart, rangeLength, has)
816
+ if (has) this._clearLocalRange(rangeStart, rangeLength)
754
817
  }
755
818
  }
756
819
 
@@ -942,74 +1005,74 @@ class Peer {
942
1005
  return true
943
1006
  }
944
1007
 
945
- _requestRange (r) {
946
- const { length, fork } = this.core.tree
1008
+ _requestRangeBlock (index, length) {
1009
+ if (this.core.bitfield.get(index) === true || !this._hasTreeParent(index)) return false
947
1010
 
948
- const end = Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength)
949
- if (end < r.start || fork !== this.remoteFork) return false
1011
+ const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
1012
+ if (b.inflight.length > 0) return false
950
1013
 
951
- const len = end - r.start
952
- const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
1014
+ const req = this._makeRequest(index >= length, b.priority)
953
1015
 
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
1016
+ // If the request cannot be satisfied, dealloc the block request if no one is subscribed to it
1017
+ if (req === null) {
1018
+ b.gc()
1019
+ return false
1020
+ }
957
1021
 
958
- let wrapped = 0
1022
+ req.block = { index, nodes: 0 }
959
1023
 
960
- for (let i = 0; i < len && wrapped < 2; i++) {
961
- let index = off + i
962
- if (index >= end) index -= len
1024
+ b.inflight.push(req)
1025
+ this._send(req)
963
1026
 
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
- }
1027
+ // Don't think this will ever happen, as the pending queue is drained before the range queue
1028
+ // but doesn't hurt to check this explicitly here also.
1029
+ if (b.queued) b.queued = false
1030
+ return true
1031
+ }
976
1032
 
977
- if (this.remoteBitfield.get(index) === false || this.core.bitfield.get(index) === true) {
978
- this.skipList.set(index, true)
979
- continue
980
- }
1033
+ _requestRange (r) {
1034
+ const { length, fork } = this.core.tree
981
1035
 
982
- if (!this._hasTreeParent(index)) {
983
- continue
1036
+ if (r.blocks) {
1037
+ let min = -1
1038
+ let max = -1
1039
+
1040
+ for (let i = r.start; i < r.end; i++) {
1041
+ const index = r.blocks[i]
1042
+ if (min === -1 || index < min) min = index
1043
+ if (max === -1 || index > max) max = index
1044
+ if (this.missingBlocks.get(index) === true && this._requestRangeBlock(index, length)) return true
984
1045
  }
985
1046
 
986
- const b = this.replicator._blocks.add(index, PRIORITY.NORMAL)
1047
+ if (min > -1) this._maybeWant(min, max - min)
1048
+ return false
1049
+ }
987
1050
 
988
- if (b.inflight.length > 0) {
989
- this.skipList.set(index, true)
990
- continue
991
- }
1051
+ const end = Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength)
1052
+ if (end < r.start || fork !== this.remoteFork) return false
992
1053
 
993
- const req = this._makeRequest(index >= length, b.priority)
1054
+ const len = end - r.start
1055
+ const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
994
1056
 
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
- }
1057
+ let i = off
1000
1058
 
1001
- this.skipList.set(index, true)
1059
+ while (true) {
1060
+ i = this.missingBlocks.findFirst(true, i)
1061
+ if (i === -1 || i >= end) break
1062
+
1063
+ if (this._requestRangeBlock(i, length)) return true
1064
+ i++
1065
+ }
1002
1066
 
1003
- req.block = { index, nodes: 0 }
1067
+ i = r.start
1004
1068
 
1005
- b.inflight.push(req)
1006
- this._send(req)
1069
+ while (true) {
1070
+ i = this.missingBlocks.findFirst(true, i)
1007
1071
 
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
1072
+ if (i === -1 || i >= off) break
1011
1073
 
1012
- return true
1074
+ if (this._requestRangeBlock(i, length)) return true
1075
+ i++
1013
1076
  }
1014
1077
 
1015
1078
  this._maybeWant(r.start, len)
@@ -1197,7 +1260,7 @@ module.exports = class Replicator {
1197
1260
  }
1198
1261
 
1199
1262
  // Called externally when a truncation upgrade has been processed
1200
- ontruncate (newLength) {
1263
+ ontruncate (newLength, truncated) {
1201
1264
  const notify = []
1202
1265
 
1203
1266
  for (const blk of this._blocks) {
@@ -1211,6 +1274,8 @@ module.exports = class Replicator {
1211
1274
  blk.detach(r, SNAPSHOT_NOT_AVAILABLE())
1212
1275
  }
1213
1276
  }
1277
+
1278
+ for (const peer of this.peers) peer._unclearLocalRange(newLength, truncated)
1214
1279
  }
1215
1280
 
1216
1281
  // Called externally when a upgrade has been processed
@@ -1526,9 +1591,9 @@ module.exports = class Replicator {
1526
1591
 
1527
1592
  if (b === null || removeInflight(b.inflight, req) === false) return
1528
1593
 
1529
- if (isBlock && this.core.bitfield.get(index) === false) {
1530
- for (const peer of this.peers) peer.skipList.set(index, false)
1531
- }
1594
+ // if (isBlock && this.core.bitfield.get(index) === false) {
1595
+ // for (const peer of this.peers) peer.skipList.set(index, false)
1596
+ // }
1532
1597
 
1533
1598
  if (b.refs.length > 0 && isBlock === true) {
1534
1599
  this._queueBlock(b)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.31.5",
3
+ "version": "10.31.7",
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",