hypercore 11.13.3 → 11.14.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
@@ -178,7 +178,7 @@ class Hypercore extends EventEmitter {
178
178
  noiseStream = new NoiseSecretStream(isInitiator, null, opts)
179
179
  outerStream = noiseStream.rawStream
180
180
  }
181
- if (!noiseStream) throw BAD_ARGUMENT('Invalid stream')
181
+ if (!noiseStream) throw BAD_ARGUMENT('Invalid stream', this.discoveryKey)
182
182
 
183
183
  if (!noiseStream.userData) {
184
184
  const protocol = Protomux.from(noiseStream)
@@ -211,10 +211,10 @@ class Hypercore extends EventEmitter {
211
211
  if (this.closing) {
212
212
  // This makes the closing logic a lot easier. If this turns out to be a problem
213
213
  // in practice, open an issue and we'll try to make a solution for it.
214
- throw SESSION_CLOSED('Cannot make sessions on a closing core')
214
+ throw SESSION_CLOSED('Cannot make sessions on a closing core', this.discoveryKey)
215
215
  }
216
216
  if (opts.checkout !== undefined && !opts.name && !opts.atom) {
217
- throw ASSERTION('Checkouts are only supported on atoms or named sessions')
217
+ throw ASSERTION('Checkouts are only supported on atoms or named sessions', this.discoveryKey)
218
218
  }
219
219
 
220
220
  const wait = opts.wait === false ? false : this.wait
@@ -253,7 +253,7 @@ class Hypercore extends EventEmitter {
253
253
  }
254
254
 
255
255
  if (!isEncryptionProvider(encryption)) {
256
- throw ASSERTION('Provider does not satisfy HypercoreEncryption interface')
256
+ throw ASSERTION('Provider does not satisfy HypercoreEncryption interface', this.discoveryKey)
257
257
  }
258
258
 
259
259
  this.encryption = encryption
@@ -383,10 +383,10 @@ class Hypercore extends EventEmitter {
383
383
  }
384
384
 
385
385
  if (this.state && checkout !== -1) {
386
- if (!opts.name && !opts.atom) throw ASSERTION('Checkouts must be named or atomized')
387
- if (checkout > this.state.length) throw ASSERTION('Invalid checkout ' + checkout + ' for ' + opts.name + ', length is ' + this.state.length)
386
+ if (!opts.name && !opts.atom) throw ASSERTION('Checkouts must be named or atomized', this.discoveryKey)
387
+ if (checkout > this.state.length) throw ASSERTION('Invalid checkout ' + checkout + ' for ' + opts.name + ', length is ' + this.state.length, this.discoveryKey)
388
388
  if (this.state.prologue && checkout < this.state.prologue.length) {
389
- throw ASSERTION('Invalid checkout ' + checkout + ' for ' + opts.name + ', prologue length is ' + this.state.prologue.length)
389
+ throw ASSERTION('Invalid checkout ' + checkout + ' for ' + opts.name + ', prologue length is ' + this.state.prologue.length, this.discoveryKey)
390
390
  }
391
391
  if (checkout < this.state.length) await this.state.truncate(checkout, this.fork)
392
392
  }
@@ -453,6 +453,11 @@ class Hypercore extends EventEmitter {
453
453
  return this.closing
454
454
  }
455
455
 
456
+ clearRequests (activeRequests, error) {
457
+ if (!activeRequests.length) return
458
+ if (this.core) this.core.replicator.clearRequests(activeRequests, error)
459
+ }
460
+
456
461
  async _close (error) {
457
462
  if (this.opened === false) {
458
463
  try {
@@ -697,7 +702,7 @@ class Hypercore extends EventEmitter {
697
702
 
698
703
  async seek (bytes, opts) {
699
704
  if (this.opened === false) await this.opening
700
- if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')
705
+ if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid', this.discoveryKey)
701
706
 
702
707
  const activeRequests = (opts && opts.activeRequests) || this.activeRequests
703
708
 
@@ -716,7 +721,7 @@ class Hypercore extends EventEmitter {
716
721
  const offset = await s.update()
717
722
  if (offset) return offset
718
723
 
719
- if (this.closing !== null) throw SESSION_CLOSED()
724
+ if (this.closing !== null) throw SESSION_CLOSED('cannot seek on a closed session', this.discoveryKey)
720
725
 
721
726
  if (!this._shouldWait(opts, this.wait)) return null
722
727
 
@@ -735,7 +740,7 @@ class Hypercore extends EventEmitter {
735
740
 
736
741
  async has (start, end = start + 1) {
737
742
  if (this.opened === false) await this.opening
738
- if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('has range is invalid')
743
+ if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('has range is invalid', this.discoveryKey)
739
744
 
740
745
  if (this.state.isDefault()) {
741
746
  if (end === start + 1) return this.core.bitfield.get(start)
@@ -765,9 +770,9 @@ class Hypercore extends EventEmitter {
765
770
 
766
771
  async get (index, opts) {
767
772
  if (this.opened === false) await this.opening
768
- if (!isValidIndex(index)) throw ASSERTION('block index is invalid')
773
+ if (!isValidIndex(index)) throw ASSERTION('block index is invalid', this.discoveryKey)
769
774
 
770
- if (this.closing !== null) throw SESSION_CLOSED()
775
+ if (this.closing !== null) throw SESSION_CLOSED('cannot get on a closed session', this.discoveryKey)
771
776
 
772
777
  const encoding = (opts && opts.valueEncoding && c.from(opts.valueEncoding)) || this.valueEncoding
773
778
 
@@ -792,14 +797,14 @@ class Hypercore extends EventEmitter {
792
797
 
793
798
  async clear (start, end = start + 1, opts) {
794
799
  if (this.opened === false) await this.opening
795
- if (this.closing !== null) throw SESSION_CLOSED()
800
+ if (this.closing !== null) throw SESSION_CLOSED('cannot clear on a closed session', this.discoveryKey)
796
801
 
797
802
  if (typeof end === 'object') {
798
803
  opts = end
799
804
  end = start + 1
800
805
  }
801
806
 
802
- if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('clear range is invalid')
807
+ if (!isValidIndex(start) || !isValidIndex(end)) throw ASSERTION('clear range is invalid', this.discoveryKey)
803
808
 
804
809
  const cleared = (opts && opts.diff) ? { blocks: 0 } : null
805
810
 
@@ -821,7 +826,7 @@ class Hypercore extends EventEmitter {
821
826
 
822
827
  if (block !== null) return block
823
828
 
824
- if (this.closing !== null) throw SESSION_CLOSED()
829
+ if (this.closing !== null) throw SESSION_CLOSED('cannot get on a closed session', this.discoveryKey)
825
830
 
826
831
  // snapshot should check if core has block
827
832
  if (this._snapshot !== null) {
@@ -911,7 +916,7 @@ class Hypercore extends EventEmitter {
911
916
 
912
917
  const isDefault = this.state === this.core.state
913
918
  const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
914
- if (isDefault && writable === false && (newLength > 0 || fork !== this.state.fork)) throw SESSION_NOT_WRITABLE()
919
+ if (isDefault && writable === false && (newLength > 0 || fork !== this.state.fork)) throw SESSION_NOT_WRITABLE('cannot append to a non-writable core', this.discoveryKey)
915
920
 
916
921
  await this.state.truncate(newLength, fork, { keyPair, signature })
917
922
 
@@ -928,7 +933,7 @@ class Hypercore extends EventEmitter {
928
933
  const { keyPair = defaultKeyPair, signature = null, maxLength } = opts
929
934
  const writable = !isDefault || !!signature || !!(keyPair && keyPair.secretKey) || opts.writable === true
930
935
 
931
- if (this._readonly || writable === false) throw SESSION_NOT_WRITABLE()
936
+ if (this._readonly || writable === false) throw SESSION_NOT_WRITABLE('cannot append to a readonly core', this.discoveryKey)
932
937
 
933
938
  blocks = Array.isArray(blocks) ? blocks : [blocks]
934
939
 
@@ -943,7 +948,7 @@ class Hypercore extends EventEmitter {
943
948
  }
944
949
  for (const b of buffers) {
945
950
  if (b.byteLength > MAX_SUGGESTED_BLOCK_SIZE) {
946
- throw BAD_ARGUMENT('Appended block exceeds the maximum suggested block size')
951
+ throw BAD_ARGUMENT('Appended block exceeds the maximum suggested block size', this.discoveryKey)
947
952
  }
948
953
  }
949
954
 
@@ -1055,8 +1060,8 @@ class Hypercore extends EventEmitter {
1055
1060
  if (this.encryption) block = block.subarray(this.encryption.padding(this.core, index))
1056
1061
  try {
1057
1062
  if (enc) return c.decode(enc, block)
1058
- } catch {
1059
- throw DECODING_ERROR()
1063
+ } catch (err) {
1064
+ throw DECODING_ERROR(err.message, this.discoveryKey)
1060
1065
  }
1061
1066
  return block
1062
1067
  }
@@ -1097,7 +1102,7 @@ function maybeUnslab (block) {
1097
1102
  }
1098
1103
 
1099
1104
  function checkSnapshot (snapshot, index) {
1100
- if (index >= snapshot.state.snapshotCompatLength) throw SNAPSHOT_NOT_AVAILABLE()
1105
+ if (index >= snapshot.state.snapshotCompatLength) throw SNAPSHOT_NOT_AVAILABLE(`snapshot at index ${index} not available (max compat length ${snapshot.state.snapshotCompatLength})`, snapshot.discoveryKey)
1101
1106
  }
1102
1107
 
1103
1108
  function readBlock (rx, index) {
package/lib/core.js CHANGED
@@ -146,17 +146,17 @@ module.exports = class Core {
146
146
  }
147
147
 
148
148
  if (!header && (opts.discoveryKey && !(opts.key || opts.manifest))) {
149
- throw STORAGE_EMPTY('No Hypercore is stored here')
149
+ throw STORAGE_EMPTY('No Hypercore is stored here', this.discoveryKey)
150
150
  }
151
151
 
152
152
  if (!header || overwrite) {
153
153
  if (!createIfMissing) {
154
- throw STORAGE_EMPTY('No Hypercore is stored here')
154
+ throw STORAGE_EMPTY('No Hypercore is stored here', this.discoveryKey)
155
155
  }
156
156
 
157
157
  if (compat) {
158
158
  if (opts.key && opts.keyPair && !b4a.equals(opts.key, opts.keyPair.publicKey)) {
159
- throw BAD_ARGUMENT('Key must match publicKey when in compat mode')
159
+ throw BAD_ARGUMENT('Key must match publicKey when in compat mode', this.discoveryKey)
160
160
  }
161
161
  }
162
162
 
@@ -216,14 +216,14 @@ module.exports = class Core {
216
216
  if (opts.manifest) {
217
217
  // if we provide a manifest and no key, verify that the stored key is the same
218
218
  if (!opts.key && !Verifier.isValidManifest(header.key, Verifier.createManifest(opts.manifest))) {
219
- throw STORAGE_CONFLICT('Manifest does not hash to provided key')
219
+ throw STORAGE_CONFLICT('Manifest does not hash to provided key', this.discoveryKey)
220
220
  }
221
221
 
222
222
  if (!header.manifest) header.manifest = opts.manifest
223
223
  }
224
224
 
225
225
  if (opts.key && !b4a.equals(header.key, opts.key)) {
226
- throw STORAGE_CONFLICT('Another Hypercore is stored here')
226
+ throw STORAGE_CONFLICT('Another Hypercore is stored here', this.discoveryKey)
227
227
  }
228
228
 
229
229
  // if we signalled compat, but already now this core isn't disable it
@@ -298,7 +298,7 @@ module.exports = class Core {
298
298
 
299
299
  try {
300
300
  if (manifest && this.header.manifest === null) {
301
- if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match')
301
+ if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match', this.discoveryKey)
302
302
 
303
303
  const tx = this.state.createWriteBatch()
304
304
  this._setManifest(tx, Verifier.createManifest(manifest), null)
@@ -385,13 +385,13 @@ module.exports = class Core {
385
385
  if (!manifest) manifest = Verifier.defaultSignerManifest(this.header.key)
386
386
 
387
387
  if (!manifest || !(Verifier.isValidManifest(this.header.key, manifest) || Verifier.isCompat(this.header.key, manifest))) {
388
- throw INVALID_SIGNATURE('Proof contains an invalid manifest') // TODO: proper error type
388
+ throw INVALID_SIGNATURE('Proof contains an invalid manifest', this.discoveryKey) // TODO: proper error type
389
389
  }
390
390
  }
391
391
 
392
392
  const verifier = this.verifier || new Verifier(this.header.key, Verifier.createManifest(manifest), { legacy: this._legacy })
393
393
  if (!verifier.verify(batch, batch.signature)) {
394
- throw INVALID_SIGNATURE('Proof contains an invalid signature')
394
+ throw INVALID_SIGNATURE('Proof contains an invalid signature', this.discoveryKey)
395
395
  }
396
396
 
397
397
  return manifest
@@ -451,7 +451,7 @@ module.exports = class Core {
451
451
 
452
452
  // if we got a manifest AND its strictly a non compat one, lets store it
453
453
  if (manifest && this.header.manifest === null) {
454
- if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match')
454
+ if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match', this.discoveryKey)
455
455
  this._setManifest(tx, manifest, null)
456
456
  }
457
457
 
package/lib/replicator.js CHANGED
@@ -1325,6 +1325,8 @@ class Peer {
1325
1325
  }
1326
1326
 
1327
1327
  _requestRange (r) {
1328
+ if (this.syncsProcessing > 0) return false
1329
+
1328
1330
  const { length, fork } = this.core.state
1329
1331
 
1330
1332
  if (r.blocks) {
@@ -1343,31 +1345,39 @@ class Peer {
1343
1345
  return false
1344
1346
  }
1345
1347
 
1346
- const end = Math.min(this.core.state.length, Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength))
1348
+ // if we can upgrade the remote, or the remote is ahead, then all the remotes blocks are valid
1349
+ // otherwise truncate to the last length the remote has acked for us
1350
+ const maxLocalLength = this.canUpgrade || this.remoteLength >= this.core.state.length
1351
+ ? this.core.state.length
1352
+ : fork === this.lastUpgradableFork ? Math.min(this.lastUpgradableLength, this.core.state.length) : 0
1353
+
1354
+ const end = Math.min(maxLocalLength, Math.min(r.end === -1 ? this.remoteLength : r.end, this.remoteLength))
1347
1355
  if (end <= r.start || fork !== this.remoteFork) return false
1348
1356
 
1349
1357
  const len = end - r.start
1350
1358
  const off = r.start + (r.linear ? 0 : Math.floor(Math.random() * len))
1351
1359
 
1352
1360
  let i = off
1361
+ // should be way less than this, but this is worst case upper bound for the skiplist
1362
+ let tries = this.inflight
1353
1363
 
1354
- while (true) {
1364
+ do {
1355
1365
  i = this._findNext(i)
1356
1366
  if (i === -1 || i >= end) break
1357
1367
 
1358
1368
  if (this._requestRangeBlock(i, length)) return true
1359
1369
  i++
1360
- }
1370
+ } while (tries-- > 0)
1361
1371
 
1362
1372
  i = r.start
1363
1373
 
1364
- while (true) {
1374
+ do {
1365
1375
  i = this._findNext(i)
1366
1376
  if (i === -1 || i >= off) break
1367
1377
 
1368
1378
  if (this._requestRangeBlock(i, length)) return true
1369
1379
  i++
1370
- }
1380
+ } while (tries-- > 0)
1371
1381
 
1372
1382
  this._maybeWant(r.start, len)
1373
1383
  return false
@@ -373,10 +373,10 @@ module.exports = class SessionState {
373
373
 
374
374
  try {
375
375
  if (this.prologue && length < this.prologue.length) {
376
- throw INVALID_OPERATION('Truncation breaks prologue')
376
+ throw INVALID_OPERATION('Truncation breaks prologue', this.core.discoveryKey)
377
377
  }
378
378
  if (length > this.length) {
379
- throw INVALID_OPERATION('Not a truncation, ' + length + ' must be less or equal to ' + this.length)
379
+ throw INVALID_OPERATION('Not a truncation, ' + length + ' must be less or equal to ' + this.length, this.core.discoveryKey)
380
380
  }
381
381
 
382
382
  const batch = this.createTreeBatch()
@@ -531,7 +531,7 @@ module.exports = class SessionState {
531
531
 
532
532
  // only multisig can have prologue so signature is always present
533
533
  if (this.prologue && batch.length < this.prologue.length) {
534
- throw INVALID_OPERATION('Append is not consistent with prologue')
534
+ throw INVALID_OPERATION('Append is not consistent with prologue', this.core.discoveryKey)
535
535
  }
536
536
 
537
537
  if (!signature && keyPair) signature = this.core.verifier.sign(batch, keyPair)
@@ -618,7 +618,7 @@ module.exports = class SessionState {
618
618
  if (b.drop) {
619
619
  // truncation must be from end
620
620
  if (p && (b.start + b.length !== p.start + p.appends)) {
621
- throw INVALID_OPERATION('Atomic truncations must be contiguous')
621
+ throw INVALID_OPERATION('Atomic truncations must be contiguous', this.core.discoveryKey)
622
622
  }
623
623
 
624
624
  // actual truncation
@@ -642,7 +642,7 @@ module.exports = class SessionState {
642
642
  }
643
643
 
644
644
  if (b.start !== p.start + p.appends) {
645
- throw INVALID_OPERATION('Atomic operations must be contiguous')
645
+ throw INVALID_OPERATION('Atomic operations must be contiguous', this.core.discoveryKey)
646
646
  }
647
647
 
648
648
  p.appends += b.length
@@ -679,7 +679,7 @@ module.exports = class SessionState {
679
679
  const truncating = sharedLength < origLength
680
680
 
681
681
  for (const node of roots) {
682
- if (node === null) throw INVALID_OPERATION('Invalid catchup length, tree nodes not available')
682
+ if (node === null) throw INVALID_OPERATION('Invalid catchup length, tree nodes not available', this.core.discoveryKey)
683
683
  }
684
684
 
685
685
  const fork = truncating ? this.fork + 1 : this.fork
@@ -755,7 +755,7 @@ module.exports = class SessionState {
755
755
  batch.length = length
756
756
 
757
757
  if (!this.core.verifier.verify(batch, signature)) {
758
- throw INVALID_SIGNATURE('Signature is not valid over committed tree')
758
+ throw INVALID_SIGNATURE('Signature is not valid over committed tree', this.core.discoveryKey)
759
759
  }
760
760
  }
761
761
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "11.13.3",
3
+ "version": "11.14.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -50,7 +50,7 @@
50
50
  "fast-fifo": "^1.3.0",
51
51
  "flat-tree": "^1.9.0",
52
52
  "hypercore-crypto": "^3.2.1",
53
- "hypercore-errors": "^1.2.0",
53
+ "hypercore-errors": "^1.5.0",
54
54
  "hypercore-id-encoding": "^1.2.0",
55
55
  "hypercore-storage": "^1.0.0",
56
56
  "is-options": "^1.0.1",