hypercore 10.24.4 → 10.24.6
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 +3 -7
- package/lib/batch.js +13 -53
- package/lib/core.js +57 -26
- package/lib/merkle-tree.js +6 -1
- package/lib/mutex.js +2 -2
- package/lib/replicator.js +26 -7
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -629,7 +629,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
629
629
|
this.replicator.ontruncate(bitfield.start)
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
-
if ((status &
|
|
632
|
+
if ((status & 0b10011) !== 0) {
|
|
633
633
|
this.replicator.onupgrade()
|
|
634
634
|
}
|
|
635
635
|
|
|
@@ -958,12 +958,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
958
958
|
}
|
|
959
959
|
|
|
960
960
|
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
961
|
if (this.opened === false) await this.opening
|
|
962
|
+
if (this._batch) throw BATCH_UNFLUSHED()
|
|
967
963
|
|
|
968
964
|
const { keyPair = this.keyPair, signature = null } = opts
|
|
969
965
|
const writable = !this._readonly && !!(signature || (keyPair && keyPair.secretKey))
|
|
@@ -982,7 +978,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
982
978
|
}
|
|
983
979
|
}
|
|
984
980
|
|
|
985
|
-
return this.core.append(buffers, {
|
|
981
|
+
return this.core.append(buffers, { keyPair, signature, preappend })
|
|
986
982
|
}
|
|
987
983
|
|
|
988
984
|
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
|
-
|
|
290
|
-
if (
|
|
255
|
+
const flushingLength = Math.min(length - this._sessionLength, this._appends.length)
|
|
256
|
+
if (flushingLength <= 0) return true
|
|
291
257
|
|
|
292
|
-
|
|
258
|
+
const batch = this.createTreeBatch(this._sessionLength + flushingLength)
|
|
259
|
+
if (batch === null) return false
|
|
293
260
|
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
264
|
+
const delta = info.byteLength - this._sessionByteLength
|
|
299
265
|
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
308
|
-
|
|
270
|
+
this._appends = this._appends.slice(flushingLength)
|
|
271
|
+
this._byteLength -= delta
|
|
309
272
|
|
|
310
|
-
|
|
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
|
-
|
|
436
|
-
|
|
437
|
-
if (!values.length) return
|
|
435
|
+
async insertBatch (batch, values, { signature, keyPair = this.header.keyPair } = {}) {
|
|
436
|
+
await this._mutex.lock()
|
|
438
437
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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
|
-
|
|
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
|
-
|
|
454
|
-
|
|
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
|
-
|
|
457
|
-
|
|
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
|
-
|
|
460
|
-
|
|
455
|
+
const offset = batch.treeLength
|
|
456
|
+
const adding = batch.length - batch.treeLength
|
|
461
457
|
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
466
|
-
|
|
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
|
-
|
|
540
|
+
this._mutex.unlock()
|
|
510
541
|
}
|
|
511
542
|
}
|
|
512
543
|
|
package/lib/merkle-tree.js
CHANGED
|
@@ -1188,6 +1188,12 @@ async function generateProof (tree, block, hash, seek, upgrade) {
|
|
|
1188
1188
|
const from = upgrade ? upgrade.start * 2 : 0
|
|
1189
1189
|
const to = upgrade ? from + upgrade.length * 2 : head
|
|
1190
1190
|
const node = normalizeIndexed(block, hash)
|
|
1191
|
+
|
|
1192
|
+
const result = { fork, block: null, hash: null, seek: null, upgrade: null, manifest: null }
|
|
1193
|
+
|
|
1194
|
+
// can't do anything as we have no data...
|
|
1195
|
+
if (head === 0) return result
|
|
1196
|
+
|
|
1191
1197
|
if (from >= to || to > head) {
|
|
1192
1198
|
throw INVALID_OPERATION('Invalid upgrade')
|
|
1193
1199
|
}
|
|
@@ -1218,7 +1224,6 @@ async function generateProof (tree, block, hash, seek, upgrade) {
|
|
|
1218
1224
|
}
|
|
1219
1225
|
|
|
1220
1226
|
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p)
|
|
1221
|
-
const result = { fork, block: null, hash: null, seek: null, upgrade: null, manifest: null }
|
|
1222
1227
|
|
|
1223
1228
|
if (block) {
|
|
1224
1229
|
result.block = {
|
package/lib/mutex.js
CHANGED
|
@@ -10,7 +10,7 @@ module.exports = class Mutex {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
lock () {
|
|
13
|
-
if (this.destroyed) return Promise.reject(this._destroyError)
|
|
13
|
+
if (this.destroyed) return Promise.reject(this._destroyError || new Error('Mutex has been destroyed'))
|
|
14
14
|
if (this.locked) return new Promise(this._enqueue)
|
|
15
15
|
this.locked = true
|
|
16
16
|
return Promise.resolve()
|
|
@@ -28,7 +28,7 @@ module.exports = class Mutex {
|
|
|
28
28
|
if (!this._destroying) this._destroying = this.locked ? this.lock().catch(() => {}) : Promise.resolve()
|
|
29
29
|
|
|
30
30
|
this.destroyed = true
|
|
31
|
-
this._destroyError = err
|
|
31
|
+
if (err) this._destroyError = err
|
|
32
32
|
|
|
33
33
|
if (err) {
|
|
34
34
|
while (this._queue.length) this._queue.shift()[1](err)
|
package/lib/replicator.js
CHANGED
|
@@ -384,7 +384,7 @@ class Peer {
|
|
|
384
384
|
remoteLength: this.core.tree.fork === this.remoteFork ? this.remoteLength : 0,
|
|
385
385
|
canUpgrade: this.canUpgrade,
|
|
386
386
|
uploading: true,
|
|
387
|
-
downloading: this.replicator.
|
|
387
|
+
downloading: this.replicator.isDownloading(),
|
|
388
388
|
hasManifest: !!this.core.header.manifest && this.core.compat === false
|
|
389
389
|
})
|
|
390
390
|
}
|
|
@@ -582,9 +582,9 @@ class Peer {
|
|
|
582
582
|
if (msg.fork === this.core.tree.fork) {
|
|
583
583
|
try {
|
|
584
584
|
proof = await this._getProof(msg)
|
|
585
|
-
} catch (err) {
|
|
585
|
+
} catch (err) {
|
|
586
586
|
safetyCatch(err)
|
|
587
|
-
if (
|
|
587
|
+
if (isCriticalError(err)) throw err
|
|
588
588
|
}
|
|
589
589
|
}
|
|
590
590
|
|
|
@@ -619,7 +619,7 @@ class Peer {
|
|
|
619
619
|
if (!exists) return
|
|
620
620
|
|
|
621
621
|
this.inflight--
|
|
622
|
-
this.replicator.
|
|
622
|
+
this.replicator._removeInflight(id)
|
|
623
623
|
|
|
624
624
|
this.wireCancel.send({ request: id })
|
|
625
625
|
}
|
|
@@ -660,7 +660,7 @@ class Peer {
|
|
|
660
660
|
if (req !== null) {
|
|
661
661
|
if (req.peer !== this) return
|
|
662
662
|
this.inflight--
|
|
663
|
-
this.replicator.
|
|
663
|
+
this.replicator._removeInflight(req.id)
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
if (reorg === true) return this.replicator._onreorgdata(this, req, data)
|
|
@@ -674,6 +674,8 @@ class Peer {
|
|
|
674
674
|
}
|
|
675
675
|
} catch (err) {
|
|
676
676
|
safetyCatch(err)
|
|
677
|
+
if (this.core.closed && !isCriticalError(err)) return
|
|
678
|
+
|
|
677
679
|
// might be a fork, verify
|
|
678
680
|
this._checkIfConflict(err)
|
|
679
681
|
this.replicator._onnodata(this, req)
|
|
@@ -695,7 +697,7 @@ class Peer {
|
|
|
695
697
|
if (req === null || req.peer !== this) return
|
|
696
698
|
|
|
697
699
|
this.inflight--
|
|
698
|
-
this.replicator.
|
|
700
|
+
this.replicator._removeInflight(req.id)
|
|
699
701
|
this.replicator._onnodata(this, req)
|
|
700
702
|
}
|
|
701
703
|
|
|
@@ -1072,10 +1074,15 @@ module.exports = class Replicator {
|
|
|
1072
1074
|
}
|
|
1073
1075
|
}
|
|
1074
1076
|
|
|
1077
|
+
isDownloading () {
|
|
1078
|
+
return this.downloading || !this._inflight.idle
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1075
1081
|
setDownloading (downloading) {
|
|
1076
1082
|
if (this.downloading === downloading) return
|
|
1077
1083
|
this.downloading = downloading
|
|
1078
|
-
|
|
1084
|
+
if (!downloading && this.isDownloading()) return
|
|
1085
|
+
for (const peer of this.peers) peer.signalUpgrade()
|
|
1079
1086
|
}
|
|
1080
1087
|
|
|
1081
1088
|
cork () {
|
|
@@ -1319,6 +1326,13 @@ module.exports = class Replicator {
|
|
|
1319
1326
|
this.onpeerupdate(true, peer)
|
|
1320
1327
|
}
|
|
1321
1328
|
|
|
1329
|
+
_removeInflight (id) {
|
|
1330
|
+
this._inflight.remove(id)
|
|
1331
|
+
if (this.isDownloading() === false) {
|
|
1332
|
+
for (const peer of this.peers) peer.signalUpgrade()
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1322
1336
|
_removePeer (peer) {
|
|
1323
1337
|
this.peers.splice(this.peers.indexOf(peer), 1)
|
|
1324
1338
|
|
|
@@ -1904,6 +1918,11 @@ function destroyRequestTimeout (req) {
|
|
|
1904
1918
|
}
|
|
1905
1919
|
}
|
|
1906
1920
|
|
|
1921
|
+
function isCriticalError (err) {
|
|
1922
|
+
// TODO: expose .critical or similar on the hypercore errors that are critical (if all not are)
|
|
1923
|
+
return err.name === 'HypercoreError'
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1907
1926
|
function onwireopen (m, c) {
|
|
1908
1927
|
return c.userData.onopen(m)
|
|
1909
1928
|
}
|