hypercore 10.0.0-alpha.52 → 10.0.0-alpha.55
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/README.md +1 -1
- package/index.js +59 -47
- package/lib/bitfield.js +51 -39
- package/lib/core.js +54 -27
- package/lib/remote-bitfield.js +28 -7
- package/lib/replicator.js +57 -23
- package/package.json +6 -8
package/README.md
CHANGED
|
@@ -152,7 +152,7 @@ core.download({ start: 0, end: -1 })
|
|
|
152
152
|
To downloaded a discrete range of blocks pass a list of indices.
|
|
153
153
|
|
|
154
154
|
```js
|
|
155
|
-
core.download({ blocks: [4, 9, 7] })
|
|
155
|
+
core.download({ blocks: [4, 9, 7] })
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
To cancel downloading a range simply destroy the range instance.
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { EventEmitter } = require('events')
|
|
2
|
-
const
|
|
2
|
+
const RAF = require('random-access-file')
|
|
3
3
|
const isOptions = require('is-options')
|
|
4
4
|
const hypercoreCrypto = require('hypercore-crypto')
|
|
5
5
|
const c = require('compact-encoding')
|
|
@@ -9,8 +9,6 @@ const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
|
9
9
|
const Protomux = require('protomux')
|
|
10
10
|
const codecs = require('codecs')
|
|
11
11
|
|
|
12
|
-
const fsctl = requireMaybe('fsctl') || { lock: noop, sparse: noop }
|
|
13
|
-
|
|
14
12
|
const Replicator = require('./lib/replicator')
|
|
15
13
|
const Core = require('./lib/core')
|
|
16
14
|
const BlockEncryption = require('./lib/block-encryption')
|
|
@@ -162,14 +160,25 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
static defaultStorage (storage, opts = {}) {
|
|
165
|
-
if (typeof storage !== 'string')
|
|
163
|
+
if (typeof storage !== 'string') {
|
|
164
|
+
if (!isRandomAccessClass(storage)) return storage
|
|
165
|
+
const Cls = storage // just to satisfy standard...
|
|
166
|
+
return name => new Cls(name)
|
|
167
|
+
}
|
|
168
|
+
|
|
166
169
|
const directory = storage
|
|
167
170
|
const toLock = opts.lock || 'oplog'
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
|
|
172
|
+
return createFile
|
|
173
|
+
|
|
174
|
+
function createFile (name) {
|
|
175
|
+
const lock = isFile(name, toLock)
|
|
176
|
+
const sparse = isFile(name, 'data') || isFile(name, 'bitfield') || isFile(name, 'tree')
|
|
177
|
+
return new RAF(name, { directory, lock, sparse })
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function isFile (name, n) {
|
|
181
|
+
return name === n || name.endsWith('/' + n)
|
|
173
182
|
}
|
|
174
183
|
}
|
|
175
184
|
|
|
@@ -303,8 +312,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
303
312
|
crypto: this.crypto,
|
|
304
313
|
legacy: opts.legacy,
|
|
305
314
|
auth: opts.auth,
|
|
306
|
-
onupdate: this._oncoreupdate.bind(this)
|
|
307
|
-
oncontigupdate: this._oncorecontigupdate.bind(this)
|
|
315
|
+
onupdate: this._oncoreupdate.bind(this)
|
|
308
316
|
})
|
|
309
317
|
|
|
310
318
|
if (opts.userData) {
|
|
@@ -487,30 +495,50 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
487
495
|
|
|
488
496
|
_oncoreupdate (status, bitfield, value, from) {
|
|
489
497
|
if (status !== 0) {
|
|
490
|
-
const
|
|
491
|
-
const
|
|
498
|
+
const truncatedNonSparse = (status & 0b1000) !== 0
|
|
499
|
+
const appendedNonSparse = (status & 0b0100) !== 0
|
|
500
|
+
const truncated = (status & 0b0010) !== 0
|
|
501
|
+
const appended = (status & 0b0001) !== 0
|
|
492
502
|
|
|
493
503
|
if (truncated) {
|
|
494
504
|
this.replicator.ontruncate(bitfield.start)
|
|
495
505
|
}
|
|
496
506
|
|
|
507
|
+
if ((status & 0b0011) !== 0) {
|
|
508
|
+
this.replicator.onupgrade()
|
|
509
|
+
}
|
|
510
|
+
|
|
497
511
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
498
512
|
const s = this.sessions[i]
|
|
499
513
|
|
|
500
514
|
if (truncated) {
|
|
501
515
|
if (s.cache) s.cache.clear()
|
|
502
|
-
|
|
516
|
+
|
|
503
517
|
// If snapshotted, make sure to update our compat so we can fail gets
|
|
504
518
|
if (s._snapshot && bitfield.start < s._snapshot.compatLength) s._snapshot.compatLength = bitfield.start
|
|
505
519
|
}
|
|
506
520
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
521
|
+
if (s.sparse ? truncated : truncatedNonSparse) {
|
|
522
|
+
s.emit('truncate', bitfield.start, this.core.tree.fork)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// For sparse sessions, immediately emit appends. If non-sparse, emit if contig length has updated
|
|
526
|
+
if (s.sparse ? appended : appendedNonSparse) {
|
|
527
|
+
s.emit('append')
|
|
528
|
+
}
|
|
511
529
|
}
|
|
512
530
|
|
|
513
|
-
this.
|
|
531
|
+
const contig = this.core.header.contiguousLength
|
|
532
|
+
|
|
533
|
+
// When the contig length catches up, broadcast the non-sparse length to peers
|
|
534
|
+
if (appendedNonSparse && contig === this.core.tree.length) {
|
|
535
|
+
for (const peer of this.peers) {
|
|
536
|
+
if (peer.broadcastedNonSparse) continue
|
|
537
|
+
|
|
538
|
+
peer.broadcastRange(0, contig)
|
|
539
|
+
peer.broadcastedNonSparse = true
|
|
540
|
+
}
|
|
541
|
+
}
|
|
514
542
|
}
|
|
515
543
|
|
|
516
544
|
if (bitfield) {
|
|
@@ -526,16 +554,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
526
554
|
}
|
|
527
555
|
}
|
|
528
556
|
|
|
529
|
-
_oncorecontigupdate () {
|
|
530
|
-
// For non-sparse sessions, emit appends only when the contiguous length is
|
|
531
|
-
// updated.
|
|
532
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
533
|
-
const s = this.sessions[i]
|
|
534
|
-
|
|
535
|
-
if (!s.sparse) s.emit('append')
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
557
|
_onpeerupdate (added, peer) {
|
|
540
558
|
const name = added ? 'peer-add' : 'peer-remove'
|
|
541
559
|
|
|
@@ -656,29 +674,25 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
656
674
|
}
|
|
657
675
|
|
|
658
676
|
async _get (index, opts) {
|
|
659
|
-
let
|
|
677
|
+
let block
|
|
660
678
|
|
|
661
679
|
if (this.core.bitfield.get(index)) {
|
|
662
|
-
|
|
680
|
+
block = this.core.blocks.get(index)
|
|
663
681
|
|
|
664
|
-
if (this.cache) this.cache.set(index,
|
|
682
|
+
if (this.cache) this.cache.set(index, block)
|
|
665
683
|
} else {
|
|
666
684
|
if (opts && opts.wait === false) return null
|
|
667
685
|
if (opts && opts.onwait) opts.onwait(index, this)
|
|
668
|
-
|
|
686
|
+
if (this.onwait) this.onwait(index, this)
|
|
669
687
|
|
|
670
688
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
671
689
|
|
|
672
|
-
req = this.
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
.addBlock(activeRequests, index)
|
|
676
|
-
.promise,
|
|
677
|
-
this.core.tree.fork
|
|
678
|
-
)
|
|
690
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
691
|
+
|
|
692
|
+
block = this._cacheOnResolve(index, req.promise, this.core.tree.fork)
|
|
679
693
|
}
|
|
680
694
|
|
|
681
|
-
return
|
|
695
|
+
return block
|
|
682
696
|
}
|
|
683
697
|
|
|
684
698
|
async _cacheOnResolve (index, req, fork) {
|
|
@@ -719,7 +733,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
719
733
|
|
|
720
734
|
async _download (range) {
|
|
721
735
|
if (this.opened === false) await this.opening
|
|
736
|
+
|
|
722
737
|
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
738
|
+
|
|
723
739
|
return this.replicator.addRange(activeRequests, range)
|
|
724
740
|
}
|
|
725
741
|
|
|
@@ -852,12 +868,8 @@ function isStream (s) {
|
|
|
852
868
|
return typeof s === 'object' && s && typeof s.pipe === 'function'
|
|
853
869
|
}
|
|
854
870
|
|
|
855
|
-
function
|
|
856
|
-
|
|
857
|
-
return require(name)
|
|
858
|
-
} catch (_) {
|
|
859
|
-
return null
|
|
860
|
-
}
|
|
871
|
+
function isRandomAccessClass (fn) {
|
|
872
|
+
return !!(typeof fn === 'function' && fn.prototype && typeof fn.prototype.open === 'function')
|
|
861
873
|
}
|
|
862
874
|
|
|
863
875
|
function toHex (buf) {
|
package/lib/bitfield.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// TODO: needs massive improvements obvs
|
|
2
|
-
|
|
3
1
|
const BigSparseArray = require('big-sparse-array')
|
|
4
2
|
const b4a = require('b4a')
|
|
3
|
+
const bits = require('bits-to-bytes')
|
|
5
4
|
|
|
6
5
|
class FixedBitfield {
|
|
7
6
|
constructor (index, bitfield) {
|
|
@@ -11,26 +10,17 @@ class FixedBitfield {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
get (index) {
|
|
14
|
-
|
|
15
|
-
const i = (index - j) / 32
|
|
16
|
-
|
|
17
|
-
return i < this.bitfield.length && (this.bitfield[i] & (1 << j)) !== 0
|
|
13
|
+
return bits.get(this.bitfield, index)
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
set (index, val) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const v = this.bitfield[i]
|
|
24
|
-
|
|
25
|
-
if (val === ((v & (1 << j)) !== 0)) return false
|
|
26
|
-
|
|
27
|
-
const u = val
|
|
28
|
-
? v | (1 << j)
|
|
29
|
-
: v ^ (1 << j)
|
|
30
|
-
|
|
31
|
-
if (u === v) return false
|
|
17
|
+
return bits.set(this.bitfield, index, val)
|
|
18
|
+
}
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
setRange (start, length, val) {
|
|
21
|
+
// Using fill instead of setRange is ~2 orders of magnitude faster, but does
|
|
22
|
+
// have the downside of not being able to tell if any bits actually changed.
|
|
23
|
+
bits.fill(this.bitfield, val, start, start + length)
|
|
34
24
|
return true
|
|
35
25
|
}
|
|
36
26
|
}
|
|
@@ -44,7 +34,11 @@ module.exports = class Bitfield {
|
|
|
44
34
|
this.resumed = !!(buf && buf.byteLength >= 4)
|
|
45
35
|
|
|
46
36
|
const all = this.resumed
|
|
47
|
-
? new Uint32Array(
|
|
37
|
+
? new Uint32Array(
|
|
38
|
+
buf.buffer,
|
|
39
|
+
buf.byteOffset,
|
|
40
|
+
Math.floor(buf.byteLength / 4)
|
|
41
|
+
)
|
|
48
42
|
: new Uint32Array(1024)
|
|
49
43
|
|
|
50
44
|
for (let i = 0; i < all.length; i += 1024) {
|
|
@@ -55,40 +49,53 @@ module.exports = class Bitfield {
|
|
|
55
49
|
}
|
|
56
50
|
|
|
57
51
|
get (index) {
|
|
58
|
-
const j = index &
|
|
59
|
-
const i = (index - j) /
|
|
52
|
+
const j = index & (this.pageSize - 1)
|
|
53
|
+
const i = (index - j) / this.pageSize
|
|
54
|
+
|
|
60
55
|
const p = this.pages.get(i)
|
|
61
56
|
|
|
62
57
|
return p ? p.get(j) : false
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
set (index, val) {
|
|
66
|
-
const j = index &
|
|
67
|
-
const i = (index - j) /
|
|
61
|
+
const j = index & (this.pageSize - 1)
|
|
62
|
+
const i = (index - j) / this.pageSize
|
|
68
63
|
|
|
69
64
|
let p = this.pages.get(i)
|
|
70
65
|
|
|
71
|
-
if (!p) {
|
|
72
|
-
if (!val) return
|
|
66
|
+
if (!p && val) {
|
|
73
67
|
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
74
68
|
}
|
|
75
69
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
if (p && p.set(j, val) && !p.dirty) {
|
|
71
|
+
p.dirty = true
|
|
72
|
+
this.unflushed.push(p)
|
|
73
|
+
}
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
setRange (start, length, val) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
}
|
|
77
|
+
let j = start & (this.pageSize - 1)
|
|
78
|
+
let i = (start - j) / this.pageSize
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
while (length > 0) {
|
|
81
|
+
let p = this.pages.get(i)
|
|
82
|
+
|
|
83
|
+
if (!p && val) {
|
|
84
|
+
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const end = Math.min(j + length, this.pageSize)
|
|
88
|
+
const range = end - j
|
|
89
|
+
|
|
90
|
+
if (p && p.setRange(j, range, val) && !p.dirty) {
|
|
91
|
+
p.dirty = true
|
|
92
|
+
this.unflushed.push(p)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
j = 0
|
|
96
|
+
i++
|
|
97
|
+
length -= range
|
|
98
|
+
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
clear () {
|
|
@@ -120,7 +127,12 @@ module.exports = class Bitfield {
|
|
|
120
127
|
let error = null
|
|
121
128
|
|
|
122
129
|
for (const page of this.unflushed) {
|
|
123
|
-
const buf = b4a.from(
|
|
130
|
+
const buf = b4a.from(
|
|
131
|
+
page.bitfield.buffer,
|
|
132
|
+
page.bitfield.byteOffset,
|
|
133
|
+
page.bitfield.byteLength
|
|
134
|
+
)
|
|
135
|
+
|
|
124
136
|
page.dirty = false
|
|
125
137
|
this.storage.write(page.index * 4096, buf, done)
|
|
126
138
|
}
|
|
@@ -151,7 +163,7 @@ module.exports = class Bitfield {
|
|
|
151
163
|
}
|
|
152
164
|
|
|
153
165
|
function ensureSize (uint32, size) {
|
|
154
|
-
if (uint32.
|
|
166
|
+
if (uint32.byteLength === size) return uint32
|
|
155
167
|
const a = new Uint32Array(1024)
|
|
156
168
|
a.set(uint32, 0)
|
|
157
169
|
return a
|
package/lib/core.js
CHANGED
|
@@ -9,9 +9,8 @@ const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_SIGNATURE } = req
|
|
|
9
9
|
const m = require('./messages')
|
|
10
10
|
|
|
11
11
|
module.exports = class Core {
|
|
12
|
-
constructor (header, crypto, oplog, tree, blocks, bitfield, auth, legacy, onupdate
|
|
12
|
+
constructor (header, crypto, oplog, tree, blocks, bitfield, auth, legacy, onupdate) {
|
|
13
13
|
this.onupdate = onupdate
|
|
14
|
-
this.oncontigupdate = oncontigupdate
|
|
15
14
|
this.header = header
|
|
16
15
|
this.crypto = crypto
|
|
17
16
|
this.oplog = oplog
|
|
@@ -27,8 +26,6 @@ module.exports = class Core {
|
|
|
27
26
|
this._verifiesFlushed = null
|
|
28
27
|
this._mutex = new Mutex()
|
|
29
28
|
this._legacy = legacy
|
|
30
|
-
|
|
31
|
-
this._updateContiguousLength(header.contiguousLength)
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
static async open (storage, opts = {}) {
|
|
@@ -134,6 +131,11 @@ module.exports = class Core {
|
|
|
134
131
|
await bitfield.clear()
|
|
135
132
|
}
|
|
136
133
|
|
|
134
|
+
// compat from earlier version that do not store contig length
|
|
135
|
+
if (header.contiguousLength === 0) {
|
|
136
|
+
while (bitfield.get(header.contiguousLength)) header.contiguousLength++
|
|
137
|
+
}
|
|
138
|
+
|
|
137
139
|
const auth = opts.auth || this.createAuth(crypto, header.signer)
|
|
138
140
|
|
|
139
141
|
for (const e of entries) {
|
|
@@ -149,6 +151,7 @@ module.exports = class Core {
|
|
|
149
151
|
|
|
150
152
|
if (e.bitfield) {
|
|
151
153
|
bitfield.setRange(e.bitfield.start, e.bitfield.length, !e.bitfield.drop)
|
|
154
|
+
updateContig(header, e.bitfield, bitfield)
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
if (e.treeUpgrade) {
|
|
@@ -165,7 +168,7 @@ module.exports = class Core {
|
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
|
|
168
|
-
return new this(header, crypto, oplog, tree, blocks, bitfield, auth, !!opts.legacy, opts.onupdate || noop
|
|
171
|
+
return new this(header, crypto, oplog, tree, blocks, bitfield, auth, !!opts.legacy, opts.onupdate || noop)
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
_shouldFlush () {
|
|
@@ -196,17 +199,6 @@ module.exports = class Core {
|
|
|
196
199
|
await this.blocks.put(index, value, byteOffset)
|
|
197
200
|
}
|
|
198
201
|
|
|
199
|
-
_updateContiguousLength (index, length = 0) {
|
|
200
|
-
if (index === this.header.contiguousLength) {
|
|
201
|
-
let i = this.header.contiguousLength + length
|
|
202
|
-
|
|
203
|
-
while (this.bitfield.get(i)) i++
|
|
204
|
-
|
|
205
|
-
this.header.contiguousLength = i
|
|
206
|
-
this.oncontigupdate()
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
202
|
async userData (key, value) {
|
|
211
203
|
// TODO: each oplog append can set user data, so we should have a way
|
|
212
204
|
// to just hitch a ride on one of the other ongoing appends?
|
|
@@ -286,12 +278,12 @@ module.exports = class Core {
|
|
|
286
278
|
this.bitfield.setRange(batch.ancestors, batch.length - batch.ancestors, true)
|
|
287
279
|
batch.commit()
|
|
288
280
|
|
|
289
|
-
this.header.contiguousLength = batch.length
|
|
290
|
-
this.oncontigupdate()
|
|
291
281
|
this.header.tree.length = batch.length
|
|
292
282
|
this.header.tree.rootHash = hash
|
|
293
283
|
this.header.tree.signature = batch.signature
|
|
294
|
-
|
|
284
|
+
|
|
285
|
+
const status = 0b0001 | updateContig(this.header, entry.bitfield, this.bitfield)
|
|
286
|
+
this.onupdate(status, entry.bitfield, null, null)
|
|
295
287
|
|
|
296
288
|
if (this._shouldFlush()) await this._flushOplog()
|
|
297
289
|
|
|
@@ -329,9 +321,11 @@ module.exports = class Core {
|
|
|
329
321
|
|
|
330
322
|
await this.oplog.append([entry], false)
|
|
331
323
|
|
|
324
|
+
let status = 0b0001
|
|
325
|
+
|
|
332
326
|
if (bitfield) {
|
|
333
327
|
this.bitfield.set(bitfield.start, true)
|
|
334
|
-
this.
|
|
328
|
+
status |= updateContig(this.header, bitfield, this.bitfield)
|
|
335
329
|
}
|
|
336
330
|
|
|
337
331
|
batch.commit()
|
|
@@ -340,7 +334,8 @@ module.exports = class Core {
|
|
|
340
334
|
this.header.tree.length = batch.length
|
|
341
335
|
this.header.tree.rootHash = batch.rootHash
|
|
342
336
|
this.header.tree.signature = batch.signature
|
|
343
|
-
|
|
337
|
+
|
|
338
|
+
this.onupdate(status, bitfield, value, from)
|
|
344
339
|
|
|
345
340
|
if (this._shouldFlush()) await this._flushOplog()
|
|
346
341
|
} finally {
|
|
@@ -387,14 +382,16 @@ module.exports = class Core {
|
|
|
387
382
|
continue
|
|
388
383
|
}
|
|
389
384
|
|
|
385
|
+
let status = 0
|
|
386
|
+
|
|
390
387
|
if (bitfield) {
|
|
391
388
|
this.bitfield.set(bitfield.start, true)
|
|
392
|
-
this.
|
|
389
|
+
status = updateContig(this.header, bitfield, this.bitfield)
|
|
393
390
|
}
|
|
394
391
|
|
|
395
392
|
batch.commit()
|
|
396
393
|
|
|
397
|
-
this.onupdate(
|
|
394
|
+
this.onupdate(status, bitfield, value, from)
|
|
398
395
|
}
|
|
399
396
|
|
|
400
397
|
if (this._shouldFlush()) await this._flushOplog()
|
|
@@ -472,15 +469,15 @@ module.exports = class Core {
|
|
|
472
469
|
addReorgHint(this.header.hints.reorgs, this.tree, batch)
|
|
473
470
|
batch.commit()
|
|
474
471
|
|
|
475
|
-
const
|
|
472
|
+
const contigStatus = updateContig(this.header, entry.bitfield, this.bitfield)
|
|
473
|
+
const status = ((batch.length > batch.ancestors) ? 0b0011 : 0b0010) | contigStatus
|
|
476
474
|
|
|
477
|
-
this.header.contiguousLength = Math.min(batch.ancestors, this.header.contiguousLength)
|
|
478
|
-
this.oncontigupdate()
|
|
479
475
|
this.header.tree.fork = batch.fork
|
|
480
476
|
this.header.tree.length = batch.length
|
|
481
477
|
this.header.tree.rootHash = batch.hash()
|
|
482
478
|
this.header.tree.signature = batch.signature
|
|
483
|
-
|
|
479
|
+
|
|
480
|
+
this.onupdate(status, entry.bitfield, null, from)
|
|
484
481
|
|
|
485
482
|
// TODO: there is a bug in the merkle tree atm where it cannot handle unflushed
|
|
486
483
|
// truncates if we append or download anything after the truncation point later on
|
|
@@ -501,6 +498,36 @@ module.exports = class Core {
|
|
|
501
498
|
}
|
|
502
499
|
}
|
|
503
500
|
|
|
501
|
+
function updateContig (header, upd, bitfield) {
|
|
502
|
+
const end = upd.start + upd.length
|
|
503
|
+
|
|
504
|
+
let c = header.contiguousLength
|
|
505
|
+
|
|
506
|
+
if (upd.drop) {
|
|
507
|
+
// If we dropped a block in the current contig range, "downgrade" it
|
|
508
|
+
if (c <= end && c > upd.start) {
|
|
509
|
+
c = upd.start
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
if (c <= end && c >= upd.start) {
|
|
513
|
+
c = end
|
|
514
|
+
while (bitfield.get(c)) c++
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (c === header.contiguousLength) {
|
|
519
|
+
return 0b0000
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (c > header.contiguousLength) {
|
|
523
|
+
header.contiguousLength = c
|
|
524
|
+
return 0b0100
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
header.contiguousLength = c
|
|
528
|
+
return 0b1000
|
|
529
|
+
}
|
|
530
|
+
|
|
504
531
|
function addReorgHint (list, tree, batch) {
|
|
505
532
|
if (tree.length === 0 || tree.fork === batch.fork) return
|
|
506
533
|
|
package/lib/remote-bitfield.js
CHANGED
|
@@ -1,24 +1,45 @@
|
|
|
1
1
|
const BigSparseArray = require('big-sparse-array')
|
|
2
|
+
const bits = require('bits-to-bytes')
|
|
2
3
|
|
|
3
4
|
module.exports = class RemoteBitfield {
|
|
4
5
|
constructor () {
|
|
6
|
+
this.pageSize = 32768
|
|
5
7
|
this.pages = new BigSparseArray()
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
get (index) {
|
|
9
|
-
const
|
|
10
|
-
const i = (index -
|
|
11
|
+
const j = index & (this.pageSize - 1)
|
|
12
|
+
const i = (index - j) / this.pageSize
|
|
13
|
+
|
|
11
14
|
const p = this.pages.get(i)
|
|
12
15
|
|
|
13
|
-
return p ? (p
|
|
16
|
+
return p ? bits.get(p, j) : false
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
set (index, val) {
|
|
17
|
-
const
|
|
18
|
-
const i = (index -
|
|
20
|
+
const j = index & (this.pageSize - 1)
|
|
21
|
+
const i = (index - j) / this.pageSize
|
|
22
|
+
|
|
19
23
|
const p = this.pages.get(i) || this.pages.set(i, new Uint32Array(1024))
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
bits.set(p, j, val)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setRange (start, length, val) {
|
|
29
|
+
let j = start & (this.pageSize - 1)
|
|
30
|
+
let i = (start - j) / this.pageSize
|
|
31
|
+
|
|
32
|
+
while (length > 0) {
|
|
33
|
+
const p = this.pages.get(i) || this.pages.set(i, new Uint32Array(1024))
|
|
34
|
+
|
|
35
|
+
const end = Math.min(j + length, this.pageSize)
|
|
36
|
+
const range = end - j
|
|
37
|
+
|
|
38
|
+
bits.fill(p, val, j, end)
|
|
39
|
+
|
|
40
|
+
j = 0
|
|
41
|
+
i++
|
|
42
|
+
length -= range
|
|
43
|
+
}
|
|
23
44
|
}
|
|
24
45
|
}
|
package/lib/replicator.js
CHANGED
|
@@ -7,6 +7,7 @@ const m = require('./messages')
|
|
|
7
7
|
const caps = require('./caps')
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MAX_INFLIGHT = 32
|
|
10
|
+
const DEFAULT_SEGMENT_SIZE = 128 * 1024 // 128 KiB
|
|
10
11
|
|
|
11
12
|
class Attachable {
|
|
12
13
|
constructor () {
|
|
@@ -286,6 +287,9 @@ class Peer {
|
|
|
286
287
|
this.remoteDownloading = true
|
|
287
288
|
this.remoteSynced = false
|
|
288
289
|
|
|
290
|
+
this.segmentsWanted = new Set()
|
|
291
|
+
this.broadcastedNonSparse = false
|
|
292
|
+
|
|
289
293
|
this.lengthAcked = 0
|
|
290
294
|
|
|
291
295
|
this.extensions = new Map()
|
|
@@ -356,13 +360,13 @@ class Peer {
|
|
|
356
360
|
|
|
357
361
|
this.sendSync()
|
|
358
362
|
|
|
359
|
-
const
|
|
363
|
+
const contig = this.core.header.contiguousLength
|
|
364
|
+
if (contig > 0) {
|
|
365
|
+
this.broadcastRange(0, contig, false)
|
|
360
366
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
bitfield: p[index]
|
|
365
|
-
})
|
|
367
|
+
if (contig === this.core.tree.length) {
|
|
368
|
+
this.broadcastedNonSparse = true
|
|
369
|
+
}
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
this.replicator._ifAvailable--
|
|
@@ -552,8 +556,8 @@ class Peer {
|
|
|
552
556
|
this.replicator._onnodata(this, req)
|
|
553
557
|
}
|
|
554
558
|
|
|
555
|
-
onwant () {
|
|
556
|
-
|
|
559
|
+
onwant ({ start, length }) {
|
|
560
|
+
this.replicator._onwant(this, start, length)
|
|
557
561
|
}
|
|
558
562
|
|
|
559
563
|
onunwant () {
|
|
@@ -577,9 +581,7 @@ class Peer {
|
|
|
577
581
|
onrange ({ drop, start, length }) {
|
|
578
582
|
const has = drop === false
|
|
579
583
|
|
|
580
|
-
|
|
581
|
-
this.remoteBitfield.set(start, has)
|
|
582
|
-
}
|
|
584
|
+
this.remoteBitfield.setRange(start, length, has)
|
|
583
585
|
|
|
584
586
|
if (drop === false) this._update()
|
|
585
587
|
}
|
|
@@ -674,6 +676,7 @@ class Peer {
|
|
|
674
676
|
return true
|
|
675
677
|
}
|
|
676
678
|
|
|
679
|
+
this._maybeWant(s.seeker.start, len)
|
|
677
680
|
return false
|
|
678
681
|
}
|
|
679
682
|
|
|
@@ -685,7 +688,10 @@ class Peer {
|
|
|
685
688
|
_requestBlock (b) {
|
|
686
689
|
const { length, fork } = this.core.tree
|
|
687
690
|
|
|
688
|
-
if (this.remoteBitfield.get(b.index) === false || fork !== this.remoteFork)
|
|
691
|
+
if (this.remoteBitfield.get(b.index) === false || fork !== this.remoteFork) {
|
|
692
|
+
this._maybeWant(b.index)
|
|
693
|
+
return false
|
|
694
|
+
}
|
|
689
695
|
|
|
690
696
|
const req = this._makeRequest(b.index >= length)
|
|
691
697
|
if (req === null) return false
|
|
@@ -743,6 +749,7 @@ class Peer {
|
|
|
743
749
|
return true
|
|
744
750
|
}
|
|
745
751
|
|
|
752
|
+
this._maybeWant(r.start, len)
|
|
746
753
|
return false
|
|
747
754
|
}
|
|
748
755
|
|
|
@@ -780,9 +787,25 @@ class Peer {
|
|
|
780
787
|
return true
|
|
781
788
|
}
|
|
782
789
|
|
|
790
|
+
this._maybeWant(f.batch.want.start, len)
|
|
783
791
|
return false
|
|
784
792
|
}
|
|
785
793
|
|
|
794
|
+
_maybeWant (start, length = 1) {
|
|
795
|
+
let i = Math.floor(start / DEFAULT_SEGMENT_SIZE)
|
|
796
|
+
const n = Math.ceil((start + length) / DEFAULT_SEGMENT_SIZE)
|
|
797
|
+
|
|
798
|
+
for (; i < n; i++) {
|
|
799
|
+
if (this.segmentsWanted.has(i)) continue
|
|
800
|
+
this.segmentsWanted.add(i)
|
|
801
|
+
|
|
802
|
+
this.wireWant.send({
|
|
803
|
+
start: i * DEFAULT_SEGMENT_SIZE,
|
|
804
|
+
length: DEFAULT_SEGMENT_SIZE
|
|
805
|
+
})
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
786
809
|
async _send (req) {
|
|
787
810
|
const fork = this.core.tree.fork
|
|
788
811
|
|
|
@@ -1299,6 +1322,28 @@ module.exports = class Replicator {
|
|
|
1299
1322
|
else this.updatePeer(peer)
|
|
1300
1323
|
}
|
|
1301
1324
|
|
|
1325
|
+
_onwant (peer, start, length) {
|
|
1326
|
+
length = Math.min(length, this.core.tree.length - start)
|
|
1327
|
+
|
|
1328
|
+
peer.protomux.cork()
|
|
1329
|
+
|
|
1330
|
+
let i = Math.floor(start / this.core.bitfield.pageSize)
|
|
1331
|
+
const n = Math.ceil((start + length) / this.core.bitfield.pageSize)
|
|
1332
|
+
|
|
1333
|
+
for (; i < n; i++) {
|
|
1334
|
+
const p = this.core.bitfield.pages.get(i)
|
|
1335
|
+
|
|
1336
|
+
if (p) {
|
|
1337
|
+
peer.wireBitfield.send({
|
|
1338
|
+
start: i * this.core.bitfield.pageSize,
|
|
1339
|
+
bitfield: p.bitfield
|
|
1340
|
+
})
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
peer.protomux.uncork()
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1302
1347
|
async _onreorgdata (peer, req, data) {
|
|
1303
1348
|
const f = this._addReorg(data.fork, peer)
|
|
1304
1349
|
|
|
@@ -1525,17 +1570,6 @@ module.exports = class Replicator {
|
|
|
1525
1570
|
}
|
|
1526
1571
|
}
|
|
1527
1572
|
|
|
1528
|
-
function pages (core) {
|
|
1529
|
-
const res = []
|
|
1530
|
-
|
|
1531
|
-
for (let i = 0; i < core.tree.length; i += core.bitfield.pageSize) {
|
|
1532
|
-
const p = core.bitfield.page(i / core.bitfield.pageSize)
|
|
1533
|
-
res.push(p)
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
return res
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
1573
|
function matchingRequest (req, data) {
|
|
1540
1574
|
if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) return false
|
|
1541
1575
|
if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) return false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.55",
|
|
4
4
|
"description": "Hypercore 10",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"@hyperswarm/secret-stream": "^6.0.0",
|
|
36
36
|
"b4a": "^1.1.0",
|
|
37
37
|
"big-sparse-array": "^1.0.2",
|
|
38
|
+
"bits-to-bytes": "^1.2.0",
|
|
38
39
|
"codecs": "^3.0.0",
|
|
39
40
|
"compact-encoding": "^2.5.0",
|
|
40
41
|
"crc32-universal": "^1.0.1",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"hypercore-crypto": "^3.2.1",
|
|
44
45
|
"is-options": "^1.0.1",
|
|
45
46
|
"protomux": "^3.2.0",
|
|
46
|
-
"random-access-file": "^
|
|
47
|
+
"random-access-file": "^3.0.1",
|
|
47
48
|
"random-array-iterator": "^1.0.0",
|
|
48
49
|
"safety-catch": "^1.0.1",
|
|
49
50
|
"sodium-universal": "^3.0.4",
|
|
@@ -52,13 +53,10 @@
|
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"brittle": "^2.0.0",
|
|
55
|
-
"hyperswarm": "
|
|
56
|
-
"random-access-memory": "^
|
|
57
|
-
"random-access-memory-overlay": "^
|
|
56
|
+
"hyperswarm": "^4.1.1",
|
|
57
|
+
"random-access-memory": "^5.0.0",
|
|
58
|
+
"random-access-memory-overlay": "^2.0.0",
|
|
58
59
|
"standard": "^16.0.3",
|
|
59
60
|
"tmp-promise": "^3.0.2"
|
|
60
|
-
},
|
|
61
|
-
"optionalDependencies": {
|
|
62
|
-
"fsctl": "^1.0.0"
|
|
63
61
|
}
|
|
64
62
|
}
|