hypercore 10.24.5 → 10.24.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
@@ -85,6 +85,7 @@ module.exports = class Hypercore extends EventEmitter {
85
85
  this._snapshot = null
86
86
  this._batch = opts._batch || null
87
87
  this._findingPeers = 0
88
+ this._active = opts.active !== false
88
89
 
89
90
  this.opening = this._openSession(key, storage, opts)
90
91
  this.opening.catch(noop)
@@ -323,6 +324,8 @@ module.exports = class Hypercore extends EventEmitter {
323
324
  // It's required so that corestore can load a name from userData before 'ready' is emitted.
324
325
  if (opts._preready) await opts._preready(this)
325
326
 
327
+ this.replicator.updateActivity(this._active ? 1 : 0, this)
328
+
326
329
  this.opened = true
327
330
  this.emit('ready')
328
331
  }
@@ -441,6 +444,7 @@ module.exports = class Hypercore extends EventEmitter {
441
444
  if (this.replicator !== null) {
442
445
  this.replicator.findingPeers -= this._findingPeers
443
446
  this.replicator.clearRequests(this.activeRequests, err)
447
+ this.replicator.updateActivity(this._active ? -1 : 0, this)
444
448
  }
445
449
 
446
450
  this._findingPeers = 0
@@ -530,7 +534,7 @@ module.exports = class Hypercore extends EventEmitter {
530
534
  _attachToMuxerOpened (mux, useSession) {
531
535
  // If the user wants to, we can make this replication run in a session
532
536
  // that way the core wont close "under them" during replication
533
- const session = useSession ? this.session() : null
537
+ const session = useSession ? this.session({ active: false }) : null
534
538
  this.replicator.attachTo(mux, session)
535
539
  }
536
540
 
@@ -958,12 +962,8 @@ module.exports = class Hypercore extends EventEmitter {
958
962
  }
959
963
 
960
964
  async append (blocks, opts = {}) {
961
- if (this._batch && this !== this._batch.session) throw BATCH_UNFLUSHED()
962
-
963
- // if a batched append, run unlocked as it already locked...
964
- const lock = !this._batch
965
-
966
965
  if (this.opened === false) await this.opening
966
+ if (this._batch) throw BATCH_UNFLUSHED()
967
967
 
968
968
  const { keyPair = this.keyPair, signature = null } = opts
969
969
  const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
@@ -982,7 +982,7 @@ module.exports = class Hypercore extends EventEmitter {
982
982
  }
983
983
  }
984
984
 
985
- return this.core.append(buffers, { lock, keyPair, signature, preappend })
985
+ return this.core.append(buffers, { keyPair, signature, preappend })
986
986
  }
987
987
 
988
988
  async treeHash (length) {
package/lib/batch.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const { BLOCK_NOT_AVAILABLE, SESSION_CLOSED } = require('hypercore-errors')
2
2
  const EventEmitter = require('events')
3
3
  const c = require('compact-encoding')
4
- const b4a = require('b4a')
5
4
 
6
5
  module.exports = class HypercoreBatch extends EventEmitter {
7
6
  constructor (session, autoClose) {
@@ -76,7 +75,6 @@ module.exports = class HypercoreBatch extends EventEmitter {
76
75
  async update (opts) {
77
76
  if (this.opened === false) await this.ready()
78
77
  await this.session.update(opts)
79
- await this._catchup()
80
78
  }
81
79
 
82
80
  setUserData (key, value, opts) {
@@ -253,64 +251,26 @@ module.exports = class HypercoreBatch extends EventEmitter {
253
251
  return flushed
254
252
  }
255
253
 
256
- async _catchup () {
257
- if (this.core.tree.length === this._sessionLength) return true
258
-
259
- const batchLength = this._sessionLength + this._appends.length
260
-
261
- if (this.core.tree.length > batchLength) return false
262
-
263
- const b = this.createTreeBatch()
264
-
265
- for (const root of this.core.tree.roots) {
266
- const batchRoot = await b.get(root.index)
267
-
268
- if (batchRoot === null) continue // in the shared tree
269
- if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) {
270
- // TODO: type this error
271
- throw new Error('Batch is uncommitable due to a conflict at ' + root.index)
272
- }
273
- }
274
-
275
- const offset = this.core.tree.length - this._sessionLength
276
- for (let i = 0; i < offset; i++) this._byteLength -= this._appends[i].byteLength
277
-
278
- await this.core.insertValuesUnsafe(this._appends.slice(0, offset), this._sessionLength, this._sessionByteLength, b.nodes)
279
-
280
- this._sessionLength = this.session.length
281
- this._sessionByteLength = this.session.byteLength
282
- this._sessionBatch = this.session.createTreeBatch()
283
- this._appends = this._appends.slice(offset)
284
-
285
- return true
286
- }
287
-
288
254
  async _flush (length, keyPair, signature) { // TODO: make this safe to interact with a parallel truncate...
289
- if (this._appends.length === 0) return true
290
- if (!(await this._catchup())) return false
255
+ const flushingLength = Math.min(length - this._sessionLength, this._appends.length)
256
+ if (flushingLength <= 0) return true
291
257
 
292
- await this.core._mutex.lock()
258
+ const batch = this.createTreeBatch(this._sessionLength + flushingLength)
259
+ if (batch === null) return false
293
260
 
294
- try {
295
- const flushingLength = Math.min(length - this._sessionLength, this._appends.length)
296
- if (flushingLength <= 0) return true
261
+ const info = await this.core.insertBatch(batch, this._appends, { keyPair, signature })
262
+ if (info === null) return false
297
263
 
298
- const blocks = flushingLength < this._appends.length ? this._appends.slice(0, flushingLength) : this._appends
264
+ const delta = info.byteLength - this._sessionByteLength
299
265
 
300
- const info = await this.session.append(blocks, { keyPair, signature })
301
- const delta = info.byteLength - this._sessionByteLength
302
-
303
- this._sessionLength = info.length
304
- this._sessionByteLength = info.byteLength
305
- this._sessionBatch = this.session.createTreeBatch()
266
+ this._sessionLength = info.length
267
+ this._sessionByteLength = info.byteLength
268
+ this._sessionBatch = this.session.createTreeBatch()
306
269
 
307
- this._appends = this._appends.slice(flushingLength)
308
- this._byteLength -= delta
270
+ this._appends = this._appends.slice(flushingLength)
271
+ this._byteLength -= delta
309
272
 
310
- this.emit('flush')
311
- } finally {
312
- this.core._mutex.unlock()
313
- }
273
+ this.emit('flush')
314
274
 
315
275
  return true
316
276
  }
package/lib/core.js CHANGED
@@ -432,40 +432,71 @@ module.exports = class Core {
432
432
  })
433
433
  }
434
434
 
435
- // assumes mutex is already acquried
436
- async insertValuesUnsafe (values, offset, byteOffset, treeNodes) {
437
- if (!values.length) return
435
+ async insertBatch (batch, values, { signature, keyPair = this.header.keyPair } = {}) {
436
+ await this._mutex.lock()
438
437
 
439
- const bitfield = {
440
- drop: false,
441
- start: offset,
442
- end: offset + values.length
443
- }
438
+ try {
439
+ // upsert compat manifest
440
+ if (this.verifier === null && keyPair) this.setManifest(null, keyPair)
444
441
 
445
- await this.blocks.putBatch(offset, values, byteOffset)
446
- await this.oplog.append({
447
- userData: null,
448
- treeNodes,
449
- treeUpgrade: null,
450
- bitfield
451
- }, false)
442
+ if (this.tree.fork !== batch.fork) return null
452
443
 
453
- this.bitfield.setRange(bitfield.start, values.length, true)
454
- for (const node of treeNodes) this.tree.unflushed.set(node.index, node)
444
+ if (this.tree.length > batch.treeLength) {
445
+ if (this.tree.length > batch.length) return null // TODO: partial commit in the future if possible
455
446
 
456
- const status = updateContig(this.header, bitfield, this.bitfield)
457
- this.onupdate(status, bitfield, null, null)
447
+ for (const root of this.tree.roots) {
448
+ const batchRoot = await batch.get(root.index)
449
+ if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) {
450
+ return null
451
+ }
452
+ }
453
+ }
458
454
 
459
- if (this._shouldFlush()) await this._flushOplog()
460
- }
455
+ const offset = batch.treeLength
456
+ const adding = batch.length - batch.treeLength
461
457
 
462
- async append (values, { lock, signature, keyPair = this.header.keyPair, preappend } = {}) {
463
- if (lock) await this._mutex.lock()
458
+ batch.upgraded = batch.length > this.tree.length
459
+ batch.treeLength = this.tree.length
460
+ if (batch.upgraded) batch.signature = signature || this.verifier.sign(batch, keyPair)
464
461
 
465
- // upsert compat manifest
466
- if (this.verifier === null && keyPair) this.setManifest(null, keyPair)
462
+ let byteOffset = batch.byteLength
463
+ for (let i = 0; i < adding; i++) byteOffset -= values[i].byteLength
464
+
465
+ const entry = {
466
+ userData: null,
467
+ treeNodes: batch.nodes,
468
+ treeUpgrade: batch.upgraded ? batch : null,
469
+ bitfield: {
470
+ drop: false,
471
+ start: offset,
472
+ length: adding
473
+ }
474
+ }
475
+
476
+ await this.blocks.putBatch(offset, adding > values.length ? values.slice(0, adding) : values, byteOffset)
477
+ await this.oplog.append([entry], false)
478
+
479
+ this.bitfield.setRange(entry.bitfield.start, entry.bitfield.length, true)
480
+ batch.commit()
481
+
482
+ const status = (batch.upgraded ? 0b0001 : 0) | updateContig(this.header, entry.bitfield, this.bitfield)
483
+ this.onupdate(status, entry.bitfield, null, null)
484
+
485
+ if (this._shouldFlush()) await this._flushOplog()
486
+ } finally {
487
+ this._mutex.unlock()
488
+ }
489
+
490
+ return { length: batch.length, byteLength: batch.byteLength }
491
+ }
492
+
493
+ async append (values, { signature, keyPair = this.header.keyPair, preappend } = {}) {
494
+ await this._mutex.lock()
467
495
 
468
496
  try {
497
+ // upsert compat manifest
498
+ if (this.verifier === null && keyPair) this.setManifest(null, keyPair)
499
+
469
500
  if (preappend) await preappend(values)
470
501
 
471
502
  if (!values.length) {
@@ -506,7 +537,7 @@ module.exports = class Core {
506
537
 
507
538
  return { length: batch.length, byteLength }
508
539
  } finally {
509
- if (lock) this._mutex.unlock()
540
+ this._mutex.unlock()
510
541
  }
511
542
  }
512
543
 
package/lib/replicator.js CHANGED
@@ -1046,7 +1046,7 @@ module.exports = class Replicator {
1046
1046
  this.findingPeers = 0 // updateable from the outside
1047
1047
  this.destroyed = false
1048
1048
  this.downloading = true
1049
- this.sessions = 0
1049
+ this.activeSessions = 0
1050
1050
 
1051
1051
  this._attached = new Set()
1052
1052
  this._inflight = new InflightTracker()
@@ -1074,15 +1074,29 @@ module.exports = class Replicator {
1074
1074
  }
1075
1075
  }
1076
1076
 
1077
+ updateActivity (inc, session) {
1078
+ this.activeSessions += inc
1079
+ this.setDownloading(this.activeSessions !== 0, session)
1080
+ }
1081
+
1077
1082
  isDownloading () {
1078
1083
  return this.downloading || !this._inflight.idle
1079
1084
  }
1080
1085
 
1081
- setDownloading (downloading) {
1086
+ setDownloading (downloading, session) {
1082
1087
  if (this.downloading === downloading) return
1083
1088
  this.downloading = downloading
1084
1089
  if (!downloading && this.isDownloading()) return
1090
+
1085
1091
  for (const peer of this.peers) peer.signalUpgrade()
1092
+
1093
+ if (downloading) { // restart channel if needed...
1094
+ for (const protomux of this._attached) {
1095
+ if (!protomux.stream.handshakeHash) continue
1096
+ if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) continue
1097
+ this._makePeer(protomux, session && session.session())
1098
+ }
1099
+ }
1086
1100
  }
1087
1101
 
1088
1102
  cork () {
@@ -1779,12 +1793,10 @@ module.exports = class Replicator {
1779
1793
  }
1780
1794
 
1781
1795
  _closeSession (session) {
1782
- if (!session.closing) this.sessions--
1783
1796
  session.close().catch(noop)
1784
1797
  }
1785
1798
 
1786
1799
  attachTo (protomux, session) {
1787
- if (session) this.sessions++
1788
1800
  const makePeer = this._makePeer.bind(this, protomux, session)
1789
1801
 
1790
1802
  this._attached.add(protomux)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.24.5",
3
+ "version": "10.24.7",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {