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 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
- this._sessionLength = this.session.length
68
- this._sessionByteLength = this.session.byteLength
69
- this._sessionBatch = this.session.createTreeBatch()
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 = src.bitfield.firstSet(0)
224
+ let pos = 0
225
225
 
226
- while (pos >= 0) {
227
- const segmentStart = pos
228
- const segmentEnd = src.bitfield.firstUnset(pos)
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 = src.bitfield.firstSet(segmentEnd + 1)
245
+ pos = segmentEnd + 1
245
246
  }
246
247
 
247
- // TODO: is it an issue to move the nodes directly?
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.fork = src.tree.fork
260
- this.tree.roots = [...src.tree.roots]
261
- this.tree.length = src.tree.length
262
- this.tree.byteLength = src.tree.byteLength
263
-
264
- try {
265
- const batch = this.tree.batch()
266
- batch.signature = signature
267
- this._verifyBatchUpgrade(batch, this.header.manifest)
268
- this.tree.signature = signature
269
- } catch (err) {
270
- this.tree.signature = null
271
- // TODO: how to handle signature failure?
272
- throw err
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.remoteDownloading === false && this.replicator.isDownloading() === false) {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.24.11",
3
+ "version": "10.25.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {