hypercore 10.24.11 → 10.25.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 +9 -2
- package/lib/batch.js +21 -4
- package/lib/core.js +55 -28
- package/lib/replicator.js +20 -5
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -511,6 +511,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
511
511
|
// because it doesn't really make a lot of sense.
|
|
512
512
|
if (Protomux.isProtomux(isInitiator)) return this._attachToMuxer(isInitiator, opts)
|
|
513
513
|
|
|
514
|
+
// if same stream is passed twice, ignore the 2nd one before we make sessions etc
|
|
515
|
+
if (isStream(isInitiator) && this._isAttached(isInitiator)) return isInitiator
|
|
516
|
+
|
|
514
517
|
const protocolStream = Hypercore.createProtocolStream(isInitiator, opts)
|
|
515
518
|
const noiseStream = protocolStream.noiseStream
|
|
516
519
|
const protocol = noiseStream.userData
|
|
@@ -521,6 +524,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
521
524
|
return protocolStream
|
|
522
525
|
}
|
|
523
526
|
|
|
527
|
+
_isAttached (stream) {
|
|
528
|
+
return stream.userData && this.replicator && this.replicator.attached(stream.userData)
|
|
529
|
+
}
|
|
530
|
+
|
|
524
531
|
_attachToMuxer (mux, useSession) {
|
|
525
532
|
if (this.opened) {
|
|
526
533
|
this._attachToMuxerOpened(mux, useSession)
|
|
@@ -771,9 +778,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
771
778
|
return true
|
|
772
779
|
}
|
|
773
780
|
|
|
774
|
-
batch ({ autoClose = true, session = true } = {}) {
|
|
781
|
+
batch ({ checkout = -1, autoClose = true, session = true } = {}) {
|
|
775
782
|
if (this._batch !== null) throw BATCH_ALREADY_EXISTS()
|
|
776
|
-
const batch = new Batch(session ? this.session() : this, autoClose)
|
|
783
|
+
const batch = new Batch(session ? this.session() : this, checkout, autoClose)
|
|
777
784
|
for (const session of this.sessions) session._batch = batch
|
|
778
785
|
return batch
|
|
779
786
|
}
|
package/lib/batch.js
CHANGED
|
@@ -3,7 +3,7 @@ const EventEmitter = require('events')
|
|
|
3
3
|
const c = require('compact-encoding')
|
|
4
4
|
|
|
5
5
|
module.exports = class HypercoreBatch extends EventEmitter {
|
|
6
|
-
constructor (session, autoClose) {
|
|
6
|
+
constructor (session, checkoutLength, autoClose) {
|
|
7
7
|
super()
|
|
8
8
|
|
|
9
9
|
this.session = session
|
|
@@ -16,6 +16,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
16
16
|
this.fork = 0
|
|
17
17
|
|
|
18
18
|
this._appends = []
|
|
19
|
+
this._checkoutLength = checkoutLength
|
|
19
20
|
this._byteLength = 0
|
|
20
21
|
this._sessionLength = 0
|
|
21
22
|
this._sessionByteLength = 0
|
|
@@ -64,9 +65,21 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
64
65
|
async ready () {
|
|
65
66
|
await this.session.ready()
|
|
66
67
|
if (this.opened) return
|
|
67
|
-
|
|
68
|
-
this.
|
|
69
|
-
|
|
68
|
+
|
|
69
|
+
if (this._checkoutLength !== -1) {
|
|
70
|
+
const batch = await this.session.core.tree.truncate(this._checkoutLength, this.session.fork)
|
|
71
|
+
batch.upgraded = false
|
|
72
|
+
batch.treeLength = batch.ancestors = this._checkoutLength
|
|
73
|
+
if (this.opened) return
|
|
74
|
+
this._sessionLength = batch.length
|
|
75
|
+
this._sessionByteLength = batch.byteLength
|
|
76
|
+
this._sessionBatch = batch
|
|
77
|
+
} else {
|
|
78
|
+
this._sessionLength = this.session.length
|
|
79
|
+
this._sessionByteLength = this.session.byteLength
|
|
80
|
+
this._sessionBatch = this.session.createTreeBatch()
|
|
81
|
+
}
|
|
82
|
+
|
|
70
83
|
this.fork = this.session.fork
|
|
71
84
|
this.opened = true
|
|
72
85
|
this.emit('ready')
|
|
@@ -77,6 +90,10 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
77
90
|
await this.session.update(opts)
|
|
78
91
|
}
|
|
79
92
|
|
|
93
|
+
treeHash () {
|
|
94
|
+
return this._sessionBatch.hash()
|
|
95
|
+
}
|
|
96
|
+
|
|
80
97
|
setUserData (key, value, opts) {
|
|
81
98
|
return this.session.setUserData(key, value, opts)
|
|
82
99
|
}
|
package/lib/core.js
CHANGED
|
@@ -217,20 +217,21 @@ module.exports = class Core {
|
|
|
217
217
|
return false
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
async copyFrom (src, signature) {
|
|
220
|
+
async copyFrom (src, signature, { length = src.tree.length } = {}) {
|
|
221
221
|
await this._mutex.lock()
|
|
222
222
|
|
|
223
223
|
try {
|
|
224
|
-
let pos =
|
|
224
|
+
let pos = 0
|
|
225
225
|
|
|
226
|
-
while (pos
|
|
227
|
-
const segmentStart = pos
|
|
228
|
-
const segmentEnd = src.bitfield.
|
|
226
|
+
while (pos < length) {
|
|
227
|
+
const segmentStart = maximumSegmentStart(pos, src.bitfield, this.bitfield)
|
|
228
|
+
const segmentEnd = minimumSegmentEnd(segmentStart, src.bitfield, this.bitfield)
|
|
229
229
|
|
|
230
|
-
if (segmentStart < 0) break
|
|
230
|
+
if (segmentStart >= length || segmentStart < 0) break
|
|
231
231
|
|
|
232
232
|
const segment = []
|
|
233
233
|
|
|
234
|
+
pos = segmentStart
|
|
234
235
|
while (pos < segmentEnd) {
|
|
235
236
|
const val = await src.blocks.get(pos++)
|
|
236
237
|
segment.push(val)
|
|
@@ -241,13 +242,10 @@ module.exports = class Core {
|
|
|
241
242
|
|
|
242
243
|
this.bitfield.setRange(segmentStart, segmentEnd, true)
|
|
243
244
|
|
|
244
|
-
pos =
|
|
245
|
+
pos = segmentEnd + 1
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
|
|
248
|
-
// TODO: make flat iterator that computes needed nodes
|
|
249
|
-
|
|
250
|
-
for (let i = 0; i <= src.tree.length * 2; i++) {
|
|
248
|
+
for (let i = 0; i < length * 2; i++) {
|
|
251
249
|
const node = await src.tree.get(i, false)
|
|
252
250
|
if (node === null) continue
|
|
253
251
|
|
|
@@ -256,25 +254,35 @@ module.exports = class Core {
|
|
|
256
254
|
|
|
257
255
|
await this.tree.flush()
|
|
258
256
|
|
|
259
|
-
this.tree.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
257
|
+
if (length > this.tree.length) {
|
|
258
|
+
this.tree.fork = src.tree.fork
|
|
259
|
+
this.tree.roots = [...src.tree.roots]
|
|
260
|
+
this.tree.length = src.tree.length
|
|
261
|
+
this.tree.byteLength = src.tree.byteLength
|
|
262
|
+
|
|
263
|
+
if (length < this.tree.length) {
|
|
264
|
+
const batch = await src.tree.truncate(length)
|
|
265
|
+
this.tree.roots = [...batch.roots]
|
|
266
|
+
this.tree.length = batch.length
|
|
267
|
+
this.tree.byteLength = batch.byteLength
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const batch = this.tree.batch()
|
|
272
|
+
batch.signature = signature
|
|
273
|
+
this._verifyBatchUpgrade(batch, this.header.manifest)
|
|
274
|
+
this.tree.signature = signature
|
|
275
|
+
} catch (err) {
|
|
276
|
+
this.tree.signature = null
|
|
277
|
+
// TODO: how to handle signature failure?
|
|
278
|
+
throw err
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
this.header.tree.length = this.tree.length
|
|
282
|
+
this.header.tree.rootHash = this.tree.hash()
|
|
283
|
+
this.header.tree.signature = this.tree.signature
|
|
273
284
|
}
|
|
274
285
|
|
|
275
|
-
this.header.tree.length = this.tree.length
|
|
276
|
-
this.header.tree.rootHash = this.tree.hash()
|
|
277
|
-
this.header.tree.signature = this.tree.signature
|
|
278
286
|
this.header.userData = src.header.userData // should copy?
|
|
279
287
|
|
|
280
288
|
await this._flushOplog()
|
|
@@ -457,6 +465,7 @@ module.exports = class Core {
|
|
|
457
465
|
|
|
458
466
|
batch.upgraded = batch.length > this.tree.length
|
|
459
467
|
batch.treeLength = this.tree.length
|
|
468
|
+
batch.ancestors = this.tree.length
|
|
460
469
|
if (batch.upgraded) batch.signature = signature || this.verifier.sign(batch, keyPair)
|
|
461
470
|
|
|
462
471
|
let byteOffset = batch.byteLength
|
|
@@ -897,3 +906,21 @@ async function flushHeader (oplog, bigHeader, header) {
|
|
|
897
906
|
}
|
|
898
907
|
|
|
899
908
|
function noop () {}
|
|
909
|
+
|
|
910
|
+
function maximumSegmentStart (start, src, dst) {
|
|
911
|
+
const a = src.firstSet(start)
|
|
912
|
+
const b = dst.firstUnset(start)
|
|
913
|
+
|
|
914
|
+
if (a === -1) return -1
|
|
915
|
+
if (b === -1) return a
|
|
916
|
+
return a < b ? b : a
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function minimumSegmentEnd (start, src, dst) {
|
|
920
|
+
const a = src.firstUnset(start)
|
|
921
|
+
const b = dst.firstSet(start)
|
|
922
|
+
|
|
923
|
+
if (a === -1) return -1
|
|
924
|
+
if (b === -1) return a
|
|
925
|
+
return a < b ? a : b
|
|
926
|
+
}
|
package/lib/replicator.js
CHANGED
|
@@ -440,6 +440,16 @@ class Peer {
|
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
+
closeIfIdle () {
|
|
444
|
+
if (this.remoteDownloading === false && this.replicator.isDownloading() === false) {
|
|
445
|
+
// idling, shut it down...
|
|
446
|
+
this.channel.close()
|
|
447
|
+
return true
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return false
|
|
451
|
+
}
|
|
452
|
+
|
|
443
453
|
async onsync ({ fork, length, remoteLength, canUpgrade, uploading, downloading, hasManifest }) {
|
|
444
454
|
const lengthChanged = length !== this.remoteLength
|
|
445
455
|
const sameFork = fork === this.core.tree.fork
|
|
@@ -452,11 +462,7 @@ class Peer {
|
|
|
452
462
|
this.remoteDownloading = downloading
|
|
453
463
|
this.remoteHasManifest = hasManifest
|
|
454
464
|
|
|
455
|
-
if (this.
|
|
456
|
-
// idling, shut it down...
|
|
457
|
-
this.channel.close()
|
|
458
|
-
return
|
|
459
|
-
}
|
|
465
|
+
if (this.closeIfIdle()) return
|
|
460
466
|
|
|
461
467
|
this.lengthAcked = sameFork ? remoteLength : 0
|
|
462
468
|
this.syncsProcessing++
|
|
@@ -1082,6 +1088,7 @@ module.exports = class Replicator {
|
|
|
1082
1088
|
this.allowFork = allowFork
|
|
1083
1089
|
this.onpeerupdate = onpeerupdate
|
|
1084
1090
|
this.onupload = onupload
|
|
1091
|
+
this.ondownloading = null // optional external hook for monitoring downloading status
|
|
1085
1092
|
this.peers = []
|
|
1086
1093
|
this.findingPeers = 0 // updateable from the outside
|
|
1087
1094
|
this.destroyed = false
|
|
@@ -1136,7 +1143,11 @@ module.exports = class Replicator {
|
|
|
1136
1143
|
if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) continue
|
|
1137
1144
|
this._makePeer(protomux, session && session.session({ active: false }))
|
|
1138
1145
|
}
|
|
1146
|
+
} else {
|
|
1147
|
+
for (const peer of this.peers) peer.closeIfIdle()
|
|
1139
1148
|
}
|
|
1149
|
+
|
|
1150
|
+
if (this.ondownloading !== null && downloading) this.ondownloading()
|
|
1140
1151
|
}
|
|
1141
1152
|
|
|
1142
1153
|
cork () {
|
|
@@ -1836,6 +1847,10 @@ module.exports = class Replicator {
|
|
|
1836
1847
|
session.close().catch(noop)
|
|
1837
1848
|
}
|
|
1838
1849
|
|
|
1850
|
+
attached (protomux) {
|
|
1851
|
+
return this._attached.has(protomux)
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1839
1854
|
attachTo (protomux, session) {
|
|
1840
1855
|
const makePeer = this._makePeer.bind(this, protomux, session)
|
|
1841
1856
|
|