hypercore 11.0.9 → 11.0.11

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
@@ -318,7 +318,10 @@ class Hypercore extends EventEmitter {
318
318
  if (e) this.core.encryption = new BlockEncryption(e.key, this.key, { compat: this.core.compat, ...e })
319
319
  }
320
320
 
321
+ const parent = opts.parent || null
322
+
321
323
  if (this.core.encryption) this.encryption = this.core.encryption
324
+ else if (parent && parent.encryption) this.encryption = this.core.encryption = parent.encryption
322
325
 
323
326
  this.writable = this._isWritable()
324
327
 
@@ -329,9 +332,9 @@ class Hypercore extends EventEmitter {
329
332
  this.encodeBatch = opts.encodeBatch
330
333
  }
331
334
 
332
- if (opts.parent) {
333
- if (opts.parent._stateIndex === -1) await opts.parent.ready()
334
- this._setupSession(opts.parent)
335
+ if (parent) {
336
+ if (parent._stateIndex === -1) await parent.ready()
337
+ this._setupSession(parent)
335
338
  }
336
339
 
337
340
  if (opts.exclusive) {
@@ -339,23 +342,25 @@ class Hypercore extends EventEmitter {
339
342
  await this.core.lockExclusive()
340
343
  }
341
344
 
342
- const parent = opts.parent || this.core
345
+ const parentState = parent ? parent.state : this.core.state
343
346
  const checkout = opts.checkout === undefined ? -1 : opts.checkout
344
347
  const state = this.state
345
348
 
346
349
  if (opts.atom) {
347
- this.state = await parent.state.createSession(null, checkout, false, opts.atom)
350
+ this.state = await parentState.createSession(null, false, opts.atom)
348
351
  if (state) state.unref()
349
352
  } else if (opts.name) {
350
353
  // todo: need to make named sessions safe before ready
351
354
  // atm we always copy the state in passCapabilities
352
- this.state = await parent.state.createSession(opts.name, checkout, !!opts.overwrite, null)
355
+ this.state = await parentState.createSession(opts.name, !!opts.overwrite, null)
353
356
  if (state) state.unref() // ref'ed above in setup session
357
+ }
354
358
 
355
- if (checkout !== -1 && checkout < this.state.length) {
356
- await this.state.truncate(checkout, this.fork)
357
- }
358
- } else if (this.state === null) {
359
+ if (this.state && checkout !== -1 && checkout < this.state.length) {
360
+ await this.state.truncate(checkout, this.fork)
361
+ }
362
+
363
+ if (this.state === null) {
359
364
  this.state = this.core.state.ref()
360
365
  }
361
366
 
@@ -520,9 +525,7 @@ class Hypercore extends EventEmitter {
520
525
  get signedLength () {
521
526
  if (this.opened === false) return 0
522
527
  if (this.state === this.core.state) return this.core.state.length
523
- const flushed = this.state.flushedLength()
524
-
525
- return flushed === -1 ? this.state.length : flushed
528
+ return this.state.flushedLength()
526
529
  }
527
530
 
528
531
  /**
package/lib/core.js CHANGED
@@ -240,6 +240,7 @@ module.exports = class Core {
240
240
 
241
241
  const treeInfo = {
242
242
  fork: header.tree.fork,
243
+ length: header.tree.length,
243
244
  signature: header.tree.signature,
244
245
  roots: header.tree.length ? await tree.getRoots(header.tree.length) : [],
245
246
  prologue
@@ -417,7 +418,9 @@ module.exports = class Core {
417
418
 
418
419
  if (this.preupdate !== null) await this.preupdate(batch, this.header.key)
419
420
 
420
- await this.state._verifyBlock(batch, bitfield, value, this.header.manifest ? null : manifest)
421
+ if (!(await this.state._verifyBlock(batch, bitfield, value, this.header.manifest ? null : manifest))) {
422
+ return false
423
+ }
421
424
 
422
425
  if (!batch.upgraded && bitfield) {
423
426
  this.replicator.onhave(bitfield.start, bitfield.length, bitfield.drop)
@@ -43,6 +43,7 @@ class MerkleTreeBatch {
43
43
  this.signature = session.signature
44
44
  this.ancestors = session.length
45
45
  this.byteLength = session.byteLength
46
+ this.prologue = session.prologue
46
47
  this.hashCached = null
47
48
 
48
49
  this.committed = false
@@ -4,7 +4,7 @@ const assert = require('nanoassert')
4
4
  const flat = require('flat-tree')
5
5
  const quickbit = require('quickbit-universal')
6
6
 
7
- const { STORAGE_CONFLICT, INVALID_OPERATION, INVALID_SIGNATURE } = require('hypercore-errors')
7
+ const { INVALID_OPERATION, INVALID_SIGNATURE } = require('hypercore-errors')
8
8
 
9
9
  const Mutex = require('./mutex')
10
10
  const Bitfield = require('./bitfield')
@@ -38,10 +38,6 @@ module.exports = class SessionState {
38
38
  this.prologue = treeInfo.prologue || null
39
39
  this.signature = treeInfo.signature || null
40
40
 
41
- const deps = this.storage.dependencies
42
- this.dependencyLength = deps.length ? deps[deps.length - 1].length : Infinity
43
-
44
- if (treeInfo.length < this.dependencyLength) this.dependencyLength = treeInfo.length
45
41
  if (treeInfo.roots.length) this.setRoots(treeInfo.roots)
46
42
 
47
43
  this.snapshotCompatLength = this.isSnapshot() ? this.length : -1
@@ -61,7 +57,11 @@ module.exports = class SessionState {
61
57
  }
62
58
 
63
59
  isDefault () {
64
- return this.core.state === this
60
+ return this.core.state === this || this.isAtomicDefault()
61
+ }
62
+
63
+ isAtomicDefault () {
64
+ return !!this.storage.atom && !!this.parent && this.parent.isDefault()
65
65
  }
66
66
 
67
67
  createTreeBatch () {
@@ -85,7 +85,9 @@ module.exports = class SessionState {
85
85
 
86
86
  flushedLength () {
87
87
  if (this.isDefault() || this.isSnapshot()) return this.length
88
- return this.dependencyLength
88
+ const deps = this.storage.dependencies
89
+ if (deps.length) return deps[deps.length - 1].length
90
+ return 0
89
91
  }
90
92
 
91
93
  unref () {
@@ -161,11 +163,8 @@ module.exports = class SessionState {
161
163
  }
162
164
 
163
165
  updateDependency (tx, length) {
164
- const dependency = updateDependency(this, length)
165
- if (dependency) {
166
- this.dependencyLength = dependency.length
167
- tx.setDependency(dependency)
168
- }
166
+ const dependency = updateDependency(this, length, false)
167
+ if (dependency) tx.setDependency(dependency)
169
168
 
170
169
  return dependency
171
170
  }
@@ -232,34 +231,19 @@ module.exports = class SessionState {
232
231
  signature: this.signature
233
232
  }
234
233
 
235
- if (src.dependencyLength > this.dependencyLength) {
236
- this.dependencyLength = src.dependencyLength
237
- this.storage.updateDependencyLength(src.dependencyLength)
238
- }
239
-
240
- // handle migration
241
- if (src.core !== this.core) {
242
- if (this.lingers === null) this.lingers = []
243
- this.lingers.push(this.storage)
234
+ const rx = src.storage.read()
235
+ const dependencyPromise = rx.getDependency()
244
236
 
245
- this.prologue = src.prologue
246
- this.storage = await src.core.state.storage.resumeSession(this.name)
247
- this.tree = new MerkleTree(this.storage)
248
-
249
- for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
250
- const state = this.core.sessionStates[i]
251
- if (state === this) continue
252
- if (state.name === this.name) state._moveToCore(src.core)
253
- }
237
+ rx.tryFlush()
254
238
 
255
- this._moveToCore(src.core)
239
+ const dependency = await dependencyPromise
240
+ if (dependency && dependency.length > this.flushedLength()) {
241
+ this.storage.updateDependencyLength(dependency.length, false, true)
256
242
  }
257
243
 
258
- const truncated = (bitfield && bitfield.truncated !== -1)
259
- ? bitfield.truncated
260
- : src.dependencyLength
244
+ const truncated = bitfield ? bitfield.truncated : -1
261
245
 
262
- if (truncated < currLength) {
246
+ if (truncated !== -1 && truncated < currLength) {
263
247
  this.ontruncate(tree, truncated, currLength, true)
264
248
  if (!bitfield || bitfield.length === 0) return
265
249
  }
@@ -296,6 +280,7 @@ module.exports = class SessionState {
296
280
  await this.mutex.lock()
297
281
 
298
282
  try {
283
+ if (!batch.commitable()) return false
299
284
  const tx = this.createWriteBatch()
300
285
  this.updating = true
301
286
 
@@ -307,9 +292,8 @@ module.exports = class SessionState {
307
292
 
308
293
  if (manifest) this.core._setManifest(tx, manifest, null)
309
294
 
310
- if (batch.commitable()) {
311
- batch.commit(tx)
312
- }
295
+ assert(batch.commitable(), 'Should still be commitable')
296
+ batch.commit(tx)
313
297
 
314
298
  const head = {
315
299
  fork: batch.fork,
@@ -335,6 +319,8 @@ module.exports = class SessionState {
335
319
  this.updating = false
336
320
  this.mutex.unlock()
337
321
  }
322
+
323
+ return true
338
324
  }
339
325
 
340
326
  async truncate (length, fork, { signature, keyPair } = {}) {
@@ -367,7 +353,7 @@ module.exports = class SessionState {
367
353
  this.roots = roots
368
354
  this.signature = tree.signature
369
355
 
370
- if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
356
+ if (dependency) this.storage.updateDependencyLength(dependency.length, true)
371
357
 
372
358
  this.ontruncate(tree, tree.length, batch.treeLength, flushed)
373
359
  } finally {
@@ -392,7 +378,7 @@ module.exports = class SessionState {
392
378
  this.roots = batch.roots
393
379
  this.signature = batch.signature
394
380
 
395
- if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
381
+ if (dependency) this.storage.updateDependencyLength(dependency.length, true)
396
382
 
397
383
  this.ontruncate(tree, batch.ancestors, batch.treeLength, flushed)
398
384
  } finally {
@@ -415,9 +401,9 @@ module.exports = class SessionState {
415
401
  if (tree) storage.setHead(tree)
416
402
 
417
403
  const truncated = batch.length < this.flushedLength()
418
- const dependency = truncated ? updateDependency(this, batch.length) : null
404
+ const dependency = truncated ? updateDependency(this, batch.length, true) : null
419
405
 
420
- if (dependency) this.dependencyLength = dependency.length
406
+ if (dependency) storage.setDependency(dependency)
421
407
 
422
408
  if (this.isDefault()) {
423
409
  await storeBitfieldRange(this.storage, storage, batch.ancestors, batch.treeLength, false)
@@ -436,13 +422,11 @@ module.exports = class SessionState {
436
422
 
437
423
  this.blocks.clear(tx, start, end)
438
424
 
439
- const dependency = start < this.flushedLength() ? updateDependency(this, start) : null
440
-
441
- if (dependency) this.dependencyLength = dependency.length
425
+ const dependency = start < this.flushedLength() ? updateDependency(this, start, true) : null
442
426
 
443
427
  const flushed = await this.flush()
444
428
 
445
- if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
429
+ if (dependency) this.storage.updateDependencyLength(dependency.length, true)
446
430
 
447
431
  // todo: atomic event handle
448
432
  if (this.isDefault() && flushed) {
@@ -578,6 +562,80 @@ module.exports = class SessionState {
578
562
  p.length += b.length
579
563
  }
580
564
 
565
+ async catchup (length) {
566
+ assert(!this.isDefault(), 'Cannot catchup signed state') // TODO: make this check better
567
+
568
+ await this.mutex.lock()
569
+
570
+ try {
571
+ if (length < this.length) throw INVALID_OPERATION('Can not catchup back in time')
572
+ if (length === this.length) return
573
+
574
+ const origLength = this.length
575
+
576
+ let sharedLength = 0
577
+ for (let i = this.storage.dependencies.length - 1; i >= 0; i--) {
578
+ const dep = this.storage.dependencies[i]
579
+ if (dep.dataPointer === this.core.state.storage.core.dataPointer) {
580
+ sharedLength = dep.length
581
+ break
582
+ }
583
+ }
584
+
585
+ const tx = this.createWriteBatch()
586
+ const rx = this.core.state.storage.read()
587
+ const rootPromises = []
588
+
589
+ for (const root of flat.fullRoots(length * 2)) {
590
+ rootPromises.push(rx.getTreeNode(root))
591
+ }
592
+
593
+ rx.tryFlush()
594
+
595
+ const roots = await Promise.all(rootPromises)
596
+ const truncating = sharedLength < origLength
597
+
598
+ const fork = truncating ? this.fork + 1 : this.fork
599
+
600
+ // overwrite it atm, TODO: keep what we can connect to the tree
601
+ tx.deleteBlockRange(0, -1)
602
+ tx.deleteTreeNodeRange(0, -1)
603
+ tx.deleteBitfieldPageRange(0, -1)
604
+
605
+ const tree = {
606
+ fork,
607
+ length,
608
+ rootHash: crypto.tree(roots),
609
+ signature: null
610
+ }
611
+
612
+ tx.setHead(tree)
613
+
614
+ // prop a better way to do this
615
+ const dep = updateDependency(this, sharedLength, true)
616
+ dep.length = length
617
+
618
+ tx.setDependency(dep)
619
+
620
+ const flushed = await this.flush()
621
+
622
+ // and prop a better way to do this tooo
623
+ this.storage.updateDependencyLength(sharedLength, true)
624
+ this.storage.updateDependencyLength(length, false)
625
+
626
+ const bitfield = { start: sharedLength, length: tree.length - sharedLength, drop: false }
627
+
628
+ this.fork = tree.fork
629
+ this.roots = roots
630
+ this.length = tree.length
631
+
632
+ if (truncating) this.ontruncate(tree, sharedLength, origLength, flushed)
633
+ if (sharedLength < length) this.onappend(tree, bitfield, flushed)
634
+ } finally {
635
+ this.mutex.unlock()
636
+ }
637
+ }
638
+
581
639
  async _overwrite (source, fork, length, treeLength, signature, isDependent, shallow) {
582
640
  const blockPromises = []
583
641
  const treePromises = []
@@ -675,9 +733,7 @@ module.exports = class SessionState {
675
733
 
676
734
  if (upgraded) tx.setHead(tree)
677
735
 
678
- const dependency = isDependent ? updateDependency(this, length) : null
679
-
680
- if (dependency) this.dependencyLength = dependency.length
736
+ const dependency = isDependent ? updateDependency(this, length, true) : null
681
737
 
682
738
  const flushed = await this.flush()
683
739
 
@@ -686,7 +742,7 @@ module.exports = class SessionState {
686
742
  this.length = length
687
743
  this.signature = signature
688
744
 
689
- if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
745
+ if (dependency) this.storage.updateDependencyLength(dependency.length, true)
690
746
 
691
747
  return { tree, flushed }
692
748
  }
@@ -748,10 +804,7 @@ module.exports = class SessionState {
748
804
 
749
805
  await state.flush(tx)
750
806
 
751
- if (dependency) {
752
- state.storage.updateDependencyLength(dependency.length)
753
- state.dependencyLength = dependency.length
754
- }
807
+ if (dependency) state.storage.updateDependencyLength(dependency.length, false)
755
808
  }
756
809
 
757
810
  const bitfield = { start: treeLength, length: length - treeLength, drop: false }
@@ -799,10 +852,6 @@ module.exports = class SessionState {
799
852
  this.core = core
800
853
  this.index = this.core.sessionStates.push(this) - 1
801
854
 
802
- // storage was updated
803
- const deps = this.storage.dependencies
804
- this.dependencyLength = deps[deps.length - 1].length
805
-
806
855
  for (let i = this.sessions.length - 1; i >= 0; i--) this.sessions[i].transferSession(this.core)
807
856
  }
808
857
 
@@ -812,9 +861,9 @@ module.exports = class SessionState {
812
861
  await this.mutex.lock()
813
862
 
814
863
  try {
815
- if (state.storage && (await state.storage.resumeSession(this.name)) !== null) {
816
- throw STORAGE_CONFLICT('Batch has already been created')
817
- }
864
+ // if (state.storage && (await state.storage.resumeSession(this.name)) !== null) {
865
+ // throw STORAGE_CONFLICT('Batch has already been created')
866
+ // }
818
867
 
819
868
  const treeLength = this.length
820
869
 
@@ -822,64 +871,60 @@ module.exports = class SessionState {
822
871
  if (this.lingers === null) this.lingers = []
823
872
  this.lingers.push(this.storage)
824
873
 
874
+ const resumed = await state.storage.resumeSession(this.name)
875
+
825
876
  const truncation = length < this.length ? await truncateAndFlush(this, length) : null
826
- const treeInfo = truncation ? truncation.tree : await state._getTreeHeadAt(this.length)
877
+ const treeInfo = truncation ? truncation.tree : resumed ? null : await state._getTreeHeadAt(this.length)
827
878
 
828
- treeInfo.fork = truncation ? this.fork + 1 : this.fork
879
+ const fork = truncation ? this.fork + 1 : this.fork
829
880
 
830
881
  // todo: validate treeInfo
831
882
 
832
- if (!this.storage.atom) {
833
- this.storage = await state.storage.createSession(this.name, treeInfo)
883
+ if (resumed) {
884
+ this.storage = resumed
834
885
  } else {
835
- const s = state.storage.atomize(this.storage.atom)
836
- this.storage = await s.createSession(this.name, treeInfo)
837
- await s.close()
886
+ treeInfo.fork = fork
887
+ this.storage = await state.storage.createSession(this.name, treeInfo)
838
888
  }
839
889
 
840
890
  this.tree = new MerkleTree(this.storage)
841
891
 
842
892
  this.prologue = state.prologue
843
- this.fork = treeInfo.fork
893
+ this.fork = fork
844
894
  this.length = length
845
895
  this.roots = await this.tree.getRoots(length)
846
896
 
847
897
  if (truncation) {
848
- const { dependency, tree, flushed } = truncation
898
+ const { dependency, tree } = truncation
849
899
 
850
- if (dependency) this.storage.updateDependencyLength(this.dependencyLength)
851
- this.ontruncate(tree, tree.length, treeLength, flushed)
900
+ if (dependency) this.storage.updateDependencyLength(dependency.length, true)
901
+ this.ontruncate(tree, tree.length, treeLength, true)
852
902
  }
853
903
  }
854
904
 
855
- if (!this.storage.atom) {
856
- for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
857
- const state = this.core.sessionStates[i]
858
- if (state === this) continue
859
- if (state.name === this.name) state._moveToCore(core)
860
- }
905
+ for (let i = this.core.sessionStates.length - 1; i >= 0; i--) {
906
+ const state = this.core.sessionStates[i]
907
+ if (state === this) continue
908
+ if (state.name === this.name) state._moveToCore(core.core)
861
909
  }
862
910
 
863
- this._moveToCore(core)
911
+ this._moveToCore(core.core)
864
912
  } finally {
865
913
  this.mutex.unlock()
866
914
  }
867
915
  }
868
916
 
869
- async createSession (name, length, overwrite, atom) {
917
+ async createSession (name, overwrite, atom) {
870
918
  let storage = null
871
919
  let treeInfo = null
872
920
 
873
921
  if (!atom && !overwrite && this.storage) {
874
922
  storage = await this.storage.resumeSession(name)
875
923
 
876
- if (storage !== null) {
877
- treeInfo = (await getCoreHead(storage)) || getDefaultTree()
878
- if (length !== -1 && treeInfo.length !== length) throw STORAGE_CONFLICT('Different batch stored here')
879
- }
924
+ if (storage !== null) treeInfo = (await getCoreHead(storage)) || getDefaultTree()
880
925
  }
881
926
 
882
- if (length === -1) length = treeInfo ? treeInfo.length : this.length
927
+ const length = treeInfo ? treeInfo.length : this.length
883
928
 
884
929
  if (storage === null) {
885
930
  treeInfo = await this._getTreeHeadAt(length)
@@ -980,25 +1025,16 @@ async function truncateAndFlush (s, length) {
980
1025
  }
981
1026
  }
982
1027
 
983
- function updateDependency (state, length) {
984
- const dependency = findDependency(state.storage, length)
985
- if (dependency === null) return null // skip default state and overlays of default
1028
+ function updateDependency (state, length, truncated) {
1029
+ const i = state.storage.findDependencyIndex(length, truncated)
1030
+ if (i === -1) return null // skip default state and overlays of default
986
1031
 
987
1032
  return {
988
- dataPointer: dependency.dataPointer,
1033
+ dataPointer: state.storage.dependencies[i].dataPointer,
989
1034
  length
990
1035
  }
991
1036
  }
992
1037
 
993
- function findDependency (storage, length) {
994
- for (let i = storage.dependencies.length - 1; i >= 0; i--) {
995
- const dep = storage.dependencies[i]
996
- if (dep.length < length) return dep
997
- }
998
-
999
- return null
1000
- }
1001
-
1002
1038
  function getDefaultTree () {
1003
1039
  return {
1004
1040
  fork: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "11.0.9",
3
+ "version": "11.0.11",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {