hypercore 11.0.29 → 11.0.31
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 +30 -28
- package/lib/copy-prologue.js +1 -1
- package/lib/core.js +13 -28
- package/lib/merkle-tree.js +123 -126
- package/lib/multisig.js +2 -2
- package/lib/replicator.js +10 -10
- package/lib/session-state.js +38 -62
- package/package.json +1 -1
- package/lib/block-store.js +0 -25
package/index.js
CHANGED
|
@@ -14,8 +14,10 @@ const Core = require('./lib/core')
|
|
|
14
14
|
const BlockEncryption = require('./lib/block-encryption')
|
|
15
15
|
const Info = require('./lib/info')
|
|
16
16
|
const Download = require('./lib/download')
|
|
17
|
+
const caps = require('./lib/caps')
|
|
17
18
|
const { manifestHash, createManifest } = require('./lib/verifier')
|
|
18
19
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
20
|
+
const { MerkleTree } = require('./lib/merkle-tree')
|
|
19
21
|
const {
|
|
20
22
|
ASSERTION,
|
|
21
23
|
BAD_ARGUMENT,
|
|
@@ -617,24 +619,6 @@ class Hypercore extends EventEmitter {
|
|
|
617
619
|
this.emit('migrate', this.key)
|
|
618
620
|
}
|
|
619
621
|
|
|
620
|
-
createTreeBatch () {
|
|
621
|
-
return this.state.createTreeBatch()
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
async restoreBatch (length, additionalBlocks = []) {
|
|
625
|
-
if (this.opened === false) await this.opening
|
|
626
|
-
const batch = this.state.createTreeBatch()
|
|
627
|
-
|
|
628
|
-
if (length > batch.length + additionalBlocks.length) {
|
|
629
|
-
throw BAD_ARGUMENT('Insufficient additional blocks were passed')
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
let i = 0
|
|
633
|
-
while (batch.length < length) batch.append(additionalBlocks[i++])
|
|
634
|
-
|
|
635
|
-
return length < batch.length ? batch.restore(length) : batch
|
|
636
|
-
}
|
|
637
|
-
|
|
638
622
|
findingPeers () {
|
|
639
623
|
this._findingPeers++
|
|
640
624
|
if (this.core !== null && !this.closing) this.core.replicator.findingPeers++
|
|
@@ -692,8 +676,7 @@ class Hypercore extends EventEmitter {
|
|
|
692
676
|
if (this.opened === false) await this.opening
|
|
693
677
|
if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')
|
|
694
678
|
|
|
695
|
-
const
|
|
696
|
-
const s = tree.seek(this.state, bytes, this.padding)
|
|
679
|
+
const s = MerkleTree.seek(this.state, bytes, this.padding)
|
|
697
680
|
|
|
698
681
|
const offset = await s.update()
|
|
699
682
|
if (offset) return offset
|
|
@@ -800,8 +783,6 @@ class Hypercore extends EventEmitter {
|
|
|
800
783
|
}
|
|
801
784
|
|
|
802
785
|
async _get (index, opts) {
|
|
803
|
-
if (this.core.isFlushing) await this.core.flushed()
|
|
804
|
-
|
|
805
786
|
const block = await readBlock(this.state.storage.read(), index)
|
|
806
787
|
|
|
807
788
|
if (block !== null) return block
|
|
@@ -935,16 +916,37 @@ class Hypercore extends EventEmitter {
|
|
|
935
916
|
return this.state.append(buffers, { keyPair, signature, preappend })
|
|
936
917
|
}
|
|
937
918
|
|
|
938
|
-
async
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
919
|
+
async signable (length = -1, fork = -1) {
|
|
920
|
+
if (this.opened === false) await this.opening
|
|
921
|
+
if (length === -1) length = this.length
|
|
922
|
+
if (fork === -1) fork = this.fork
|
|
923
|
+
|
|
924
|
+
return caps.treeSignable(this.key, await this.treeHash(length), length, fork)
|
|
925
|
+
}
|
|
943
926
|
|
|
944
|
-
|
|
927
|
+
async treeHash (length = -1) {
|
|
928
|
+
if (this.opened === false) await this.opening
|
|
929
|
+
if (length === -1) length = this.length
|
|
930
|
+
|
|
931
|
+
const roots = await MerkleTree.getRoots(this.state, length)
|
|
945
932
|
return crypto.tree(roots)
|
|
946
933
|
}
|
|
947
934
|
|
|
935
|
+
async proof (opts) {
|
|
936
|
+
if (this.opened === false) await this.opening
|
|
937
|
+
const rx = this.state.storage.read()
|
|
938
|
+
const promise = MerkleTree.proof(this.state, rx, opts)
|
|
939
|
+
rx.tryFlush()
|
|
940
|
+
return promise
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
async verifyFullyRemote (proof) {
|
|
944
|
+
if (this.opened === false) await this.opening
|
|
945
|
+
const batch = await MerkleTree.verifyFullyRemote(this.state, proof)
|
|
946
|
+
await this.core._verifyBatchUpgrade(batch, proof.manifest)
|
|
947
|
+
return batch
|
|
948
|
+
}
|
|
949
|
+
|
|
948
950
|
registerExtension (name, handlers = {}) {
|
|
949
951
|
if (this.extensions.has(name)) {
|
|
950
952
|
const ext = this.extensions.get(name)
|
package/lib/copy-prologue.js
CHANGED
|
@@ -14,7 +14,7 @@ module.exports = copyPrologue
|
|
|
14
14
|
async function copyPrologue (src, dst) {
|
|
15
15
|
const prologue = dst.header.manifest.prologue
|
|
16
16
|
|
|
17
|
-
if (src.
|
|
17
|
+
if (src.length < prologue.length || prologue.length === 0) return
|
|
18
18
|
|
|
19
19
|
const stack = []
|
|
20
20
|
const roots = flat.fullRoots(prologue.length * 2)
|
package/lib/core.js
CHANGED
|
@@ -4,7 +4,6 @@ const unslab = require('unslab')
|
|
|
4
4
|
const z32 = require('z32')
|
|
5
5
|
const Mutex = require('./mutex')
|
|
6
6
|
const { MerkleTree, ReorgBatch } = require('./merkle-tree')
|
|
7
|
-
const BlockStore = require('./block-store')
|
|
8
7
|
const BitInterlude = require('./bit-interlude')
|
|
9
8
|
const Bitfield = require('./bitfield')
|
|
10
9
|
const RemoteBitfield = require('./remote-bitfield')
|
|
@@ -35,8 +34,6 @@ module.exports = class Core {
|
|
|
35
34
|
this.preupdate = null
|
|
36
35
|
this.header = null
|
|
37
36
|
this.compat = false
|
|
38
|
-
this.tree = null
|
|
39
|
-
this.blocks = null
|
|
40
37
|
this.bitfield = null
|
|
41
38
|
this.verifier = null
|
|
42
39
|
this.truncating = 0
|
|
@@ -54,9 +51,6 @@ module.exports = class Core {
|
|
|
54
51
|
this.closed = false
|
|
55
52
|
|
|
56
53
|
this._manifestFlushed = false
|
|
57
|
-
this._onflush = null
|
|
58
|
-
this._flushing = null
|
|
59
|
-
this._activeBatch = null
|
|
60
54
|
this._bitfield = null
|
|
61
55
|
this._verifies = null
|
|
62
56
|
this._verifiesFlushed = null
|
|
@@ -239,22 +233,20 @@ module.exports = class Core {
|
|
|
239
233
|
|
|
240
234
|
const prologue = header.manifest ? header.manifest.prologue : null
|
|
241
235
|
|
|
242
|
-
const tree = await MerkleTree.open(storage)
|
|
243
236
|
const bitfield = await Bitfield.open(storage, header.tree.length)
|
|
244
|
-
const blocks = new BlockStore(storage)
|
|
245
237
|
|
|
246
238
|
const treeInfo = {
|
|
247
239
|
fork: header.tree.fork,
|
|
248
240
|
length: header.tree.length,
|
|
249
241
|
signature: header.tree.signature,
|
|
250
|
-
roots: header.tree.length ? await
|
|
242
|
+
roots: header.tree.length ? await MerkleTree.getRootsFromStorage(storage, header.tree.length) : [],
|
|
251
243
|
prologue
|
|
252
244
|
}
|
|
253
245
|
|
|
254
246
|
if (overwrite) {
|
|
255
247
|
const tx = storage.write()
|
|
256
|
-
|
|
257
|
-
|
|
248
|
+
tx.deleteTreeNodeRange(0, -1)
|
|
249
|
+
tx.deleteBlockRange(0, -1)
|
|
258
250
|
bitfield.clear(tx)
|
|
259
251
|
await tx.flush()
|
|
260
252
|
}
|
|
@@ -282,11 +274,9 @@ module.exports = class Core {
|
|
|
282
274
|
this.storage = storage
|
|
283
275
|
this.header = header
|
|
284
276
|
this.compat = compat
|
|
285
|
-
this.tree = tree
|
|
286
|
-
this.blocks = blocks
|
|
287
277
|
this.bitfield = bitfield
|
|
288
278
|
this.verifier = verifier
|
|
289
|
-
this.state = new SessionState(this, null, storage,
|
|
279
|
+
this.state = new SessionState(this, null, storage, treeInfo, null)
|
|
290
280
|
|
|
291
281
|
if (this.key === null) this.key = this.header.key
|
|
292
282
|
if (this.discoveryKey === null) this.discoveryKey = crypto.discoveryKey(this.key)
|
|
@@ -323,8 +313,7 @@ module.exports = class Core {
|
|
|
323
313
|
|
|
324
314
|
const tx = this.state.createWriteBatch()
|
|
325
315
|
this._setManifest(tx, Verifier.createManifest(manifest), null)
|
|
326
|
-
|
|
327
|
-
if (await this.state.flush(tx)) this.replicator.onupgrade()
|
|
316
|
+
await this.state.flush()
|
|
328
317
|
}
|
|
329
318
|
} finally {
|
|
330
319
|
this.state._unlock()
|
|
@@ -377,10 +366,6 @@ module.exports = class Core {
|
|
|
377
366
|
}
|
|
378
367
|
}
|
|
379
368
|
|
|
380
|
-
get isFlushing () {
|
|
381
|
-
return !!(this._flushing || this.state._activeBatch)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
369
|
flushed () {
|
|
385
370
|
return this.state.flushed()
|
|
386
371
|
}
|
|
@@ -392,7 +377,7 @@ module.exports = class Core {
|
|
|
392
377
|
|
|
393
378
|
if (this.state.length > treeLength) {
|
|
394
379
|
for (const root of this.state.roots) {
|
|
395
|
-
const batchRoot = await
|
|
380
|
+
const batchRoot = await MerkleTree.get(state, root.index)
|
|
396
381
|
if (batchRoot.size !== root.size || !b4a.equals(batchRoot.hash, root.hash)) {
|
|
397
382
|
return false
|
|
398
383
|
}
|
|
@@ -487,7 +472,7 @@ module.exports = class Core {
|
|
|
487
472
|
|
|
488
473
|
const ranges = bits.flush(tx, this.bitfield)
|
|
489
474
|
|
|
490
|
-
await this.state.flush(
|
|
475
|
+
await this.state.flush()
|
|
491
476
|
|
|
492
477
|
for (const { start, end, value } of ranges) {
|
|
493
478
|
this._setBitfieldRanges(start, end, value)
|
|
@@ -514,7 +499,7 @@ module.exports = class Core {
|
|
|
514
499
|
return false
|
|
515
500
|
}
|
|
516
501
|
|
|
517
|
-
const batch =
|
|
502
|
+
const batch = MerkleTree.verifyFullyRemote(this.state, proof)
|
|
518
503
|
|
|
519
504
|
try {
|
|
520
505
|
this._verifyBatchUpgrade(batch, proof.manifest)
|
|
@@ -530,13 +515,13 @@ module.exports = class Core {
|
|
|
530
515
|
this._setManifest(tx, proof.manifest, null)
|
|
531
516
|
}
|
|
532
517
|
|
|
533
|
-
await this.state.flush(
|
|
518
|
+
await this.state.flush()
|
|
534
519
|
} finally {
|
|
535
520
|
this.state.mutex.unlock()
|
|
536
521
|
}
|
|
537
522
|
|
|
538
523
|
const remoteTreeHash = crypto.tree(proof.upgrade.nodes)
|
|
539
|
-
const localTreeHash = crypto.tree(await this.
|
|
524
|
+
const localTreeHash = crypto.tree(await MerkleTree.getRootsFromStorage(this.storage, proof.upgrade.length))
|
|
540
525
|
|
|
541
526
|
if (b4a.equals(localTreeHash, remoteTreeHash)) return false
|
|
542
527
|
|
|
@@ -545,8 +530,8 @@ module.exports = class Core {
|
|
|
545
530
|
}
|
|
546
531
|
|
|
547
532
|
async verifyReorg (proof) {
|
|
548
|
-
const batch = new ReorgBatch(this.
|
|
549
|
-
await
|
|
533
|
+
const batch = new ReorgBatch(this.state)
|
|
534
|
+
await MerkleTree.reorg(this.state, proof, batch)
|
|
550
535
|
this._verifyBatchUpgrade(batch, proof.manifest)
|
|
551
536
|
return batch
|
|
552
537
|
}
|
|
@@ -557,7 +542,7 @@ module.exports = class Core {
|
|
|
557
542
|
// but this is easy atm (and the above layer will just retry)
|
|
558
543
|
if (proof.fork !== this.state.fork) return false
|
|
559
544
|
|
|
560
|
-
const batch = await
|
|
545
|
+
const batch = await MerkleTree.verify(this.state, proof)
|
|
561
546
|
if (!batch.commitable()) return false
|
|
562
547
|
|
|
563
548
|
const value = (proof.block && proof.block.value) || null
|
package/lib/merkle-tree.js
CHANGED
|
@@ -36,7 +36,7 @@ class NodeQueue {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
class MerkleTreeBatch {
|
|
39
|
-
constructor (
|
|
39
|
+
constructor (session) {
|
|
40
40
|
this.fork = session.fork
|
|
41
41
|
this.roots = [...session.roots]
|
|
42
42
|
this.length = session.length
|
|
@@ -50,8 +50,7 @@ class MerkleTreeBatch {
|
|
|
50
50
|
this.truncated = false
|
|
51
51
|
this.treeLength = session.length
|
|
52
52
|
this.treeFork = session.fork
|
|
53
|
-
this.
|
|
54
|
-
this.storage = tree.storage
|
|
53
|
+
this.storage = session.storage
|
|
55
54
|
this.session = session
|
|
56
55
|
this.nodes = []
|
|
57
56
|
this.upgraded = false
|
|
@@ -120,7 +119,7 @@ class MerkleTreeBatch {
|
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
clone () {
|
|
123
|
-
const b = new MerkleTreeBatch(this.
|
|
122
|
+
const b = new MerkleTreeBatch(this.session)
|
|
124
123
|
|
|
125
124
|
b.fork = this.fork
|
|
126
125
|
b.roots = [...this.roots]
|
|
@@ -158,11 +157,12 @@ class MerkleTreeBatch {
|
|
|
158
157
|
if (n.index === index) return n
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
return
|
|
160
|
+
return MerkleTree.get(this.session, index, error)
|
|
162
161
|
}
|
|
163
162
|
|
|
163
|
+
// deprecated, use sssion proof instead
|
|
164
164
|
proof (batch, { block, hash, seek, upgrade }) {
|
|
165
|
-
return generateProof(
|
|
165
|
+
return generateProof(this.session, batch, block, hash, seek, upgrade)
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
verifyUpgrade (proof) {
|
|
@@ -287,7 +287,7 @@ class MerkleTreeBatch {
|
|
|
287
287
|
async restore (length) {
|
|
288
288
|
if (length === this.length) return this
|
|
289
289
|
|
|
290
|
-
const roots = unslabNodes(await this.
|
|
290
|
+
const roots = unslabNodes(await MerkleTree.getRootsFromStorage(this.storage, length))
|
|
291
291
|
|
|
292
292
|
this.roots = roots
|
|
293
293
|
this.length = length
|
|
@@ -301,8 +301,8 @@ class MerkleTreeBatch {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
class ReorgBatch extends MerkleTreeBatch {
|
|
304
|
-
constructor (
|
|
305
|
-
super(
|
|
304
|
+
constructor (session) {
|
|
305
|
+
super(session)
|
|
306
306
|
|
|
307
307
|
this.roots = []
|
|
308
308
|
this.length = 0
|
|
@@ -347,7 +347,7 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
347
347
|
const left = n.get(ite.leftChild())
|
|
348
348
|
if (!left) break
|
|
349
349
|
|
|
350
|
-
const existing = await
|
|
350
|
+
const existing = await MerkleTree.get(this.session, left.index, false)
|
|
351
351
|
if (!existing || !b4a.equals(existing.hash, left.hash)) {
|
|
352
352
|
diff = left
|
|
353
353
|
} else {
|
|
@@ -387,16 +387,15 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
class ByteSeeker {
|
|
390
|
-
constructor (
|
|
391
|
-
this.tree = tree
|
|
390
|
+
constructor (session, bytes, padding = 0) {
|
|
392
391
|
this.session = session
|
|
393
392
|
this.bytes = bytes
|
|
394
393
|
this.padding = padding
|
|
395
394
|
|
|
396
|
-
const size =
|
|
395
|
+
const size = session.byteLength - (session.length * padding)
|
|
397
396
|
|
|
398
|
-
this.start = bytes >= size ?
|
|
399
|
-
this.end = bytes < size ?
|
|
397
|
+
this.start = bytes >= size ? session.length : 0
|
|
398
|
+
this.end = bytes < size ? session.length : 0
|
|
400
399
|
}
|
|
401
400
|
|
|
402
401
|
async _seek (bytes) {
|
|
@@ -414,7 +413,7 @@ class ByteSeeker {
|
|
|
414
413
|
const ite = flat.iterator(node.index)
|
|
415
414
|
|
|
416
415
|
while ((ite.index & 1) !== 0) {
|
|
417
|
-
const l = await
|
|
416
|
+
const l = await MerkleTree.get(this.session, ite.leftChild(), false)
|
|
418
417
|
|
|
419
418
|
if (l) {
|
|
420
419
|
const size = getUnpaddedSize(l, this.padding, ite)
|
|
@@ -449,9 +448,9 @@ class ByteSeeker {
|
|
|
449
448
|
}
|
|
450
449
|
|
|
451
450
|
class TreeProof {
|
|
452
|
-
constructor (
|
|
453
|
-
this.fork =
|
|
454
|
-
this.signature =
|
|
451
|
+
constructor (session, block, hash, seek, upgrade) {
|
|
452
|
+
this.fork = session.fork
|
|
453
|
+
this.signature = session.signature
|
|
455
454
|
|
|
456
455
|
this.block = block
|
|
457
456
|
this.hash = hash
|
|
@@ -508,10 +507,6 @@ class TreeProof {
|
|
|
508
507
|
}
|
|
509
508
|
|
|
510
509
|
class MerkleTree {
|
|
511
|
-
constructor (storage) {
|
|
512
|
-
this.storage = storage
|
|
513
|
-
}
|
|
514
|
-
|
|
515
510
|
static hash (s) {
|
|
516
511
|
return unslab(crypto.tree(s.roots))
|
|
517
512
|
}
|
|
@@ -528,14 +523,14 @@ class MerkleTree {
|
|
|
528
523
|
return totalSpan(roots)
|
|
529
524
|
}
|
|
530
525
|
|
|
531
|
-
|
|
532
|
-
return
|
|
526
|
+
static getRoots (session, length) {
|
|
527
|
+
return MerkleTree.getRootsFromStorage(session.storage, length)
|
|
533
528
|
}
|
|
534
529
|
|
|
535
|
-
|
|
530
|
+
static getRootsFromStorage (storage, length) {
|
|
536
531
|
const indexes = flat.fullRoots(2 * length)
|
|
537
532
|
const roots = new Array(indexes.length)
|
|
538
|
-
const readBatch =
|
|
533
|
+
const readBatch = storage.read()
|
|
539
534
|
|
|
540
535
|
for (let i = 0; i < indexes.length; i++) {
|
|
541
536
|
roots[i] = readBatch.getTreeNode(indexes[i], true)
|
|
@@ -546,7 +541,7 @@ class MerkleTree {
|
|
|
546
541
|
return Promise.all(roots)
|
|
547
542
|
}
|
|
548
543
|
|
|
549
|
-
getNeededNodes (length, start, end) {
|
|
544
|
+
static getNeededNodes (session, length, start, end) {
|
|
550
545
|
const nodes = new Map()
|
|
551
546
|
const head = length * 2
|
|
552
547
|
|
|
@@ -555,7 +550,7 @@ class MerkleTree {
|
|
|
555
550
|
|
|
556
551
|
while (true) {
|
|
557
552
|
if (nodes.has(ite.index)) break
|
|
558
|
-
nodes.set(ite.index, this.get(ite.index, true))
|
|
553
|
+
nodes.set(ite.index, this.get(session, ite.index, true))
|
|
559
554
|
|
|
560
555
|
const sibling = ite.sibling()
|
|
561
556
|
|
|
@@ -563,17 +558,17 @@ class MerkleTree {
|
|
|
563
558
|
if (ite.contains(head)) break
|
|
564
559
|
|
|
565
560
|
if (nodes.has(sibling)) break
|
|
566
|
-
nodes.set(sibling, this.get(sibling, true))
|
|
561
|
+
nodes.set(sibling, this.get(session, sibling, true))
|
|
567
562
|
}
|
|
568
563
|
}
|
|
569
564
|
|
|
570
565
|
return Promise.all([...nodes.values()])
|
|
571
566
|
}
|
|
572
567
|
|
|
573
|
-
async upgradeable (length) {
|
|
568
|
+
static async upgradeable (session, length) {
|
|
574
569
|
const indexes = flat.fullRoots(2 * length)
|
|
575
570
|
const roots = new Array(indexes.length)
|
|
576
|
-
const readBatch =
|
|
571
|
+
const readBatch = session.storage.read()
|
|
577
572
|
|
|
578
573
|
for (let i = 0; i < indexes.length; i++) {
|
|
579
574
|
roots[i] = readBatch.getTreeNode(indexes[i], false)
|
|
@@ -588,24 +583,17 @@ class MerkleTree {
|
|
|
588
583
|
return true
|
|
589
584
|
}
|
|
590
585
|
|
|
591
|
-
seek (session, bytes, padding) {
|
|
592
|
-
return new ByteSeeker(
|
|
586
|
+
static seek (session, bytes, padding) {
|
|
587
|
+
return new ByteSeeker(session, bytes, padding)
|
|
593
588
|
}
|
|
594
589
|
|
|
595
|
-
get (index, error = true, readBatch = null) {
|
|
590
|
+
static get (session, index, error = true, readBatch = null) {
|
|
596
591
|
if (readBatch) return readBatch.getTreeNode(index, error)
|
|
597
592
|
|
|
598
|
-
return getTreeNode(
|
|
593
|
+
return getTreeNode(session.storage, index, error)
|
|
599
594
|
}
|
|
600
595
|
|
|
601
|
-
|
|
602
|
-
this.truncated = true
|
|
603
|
-
this.truncateTo = 0
|
|
604
|
-
|
|
605
|
-
return tx.deleteTreeNodeRange(0, -1)
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
async truncate (length, batch, fork = batch.fork) {
|
|
596
|
+
static async truncate (session, length, batch, fork = batch.fork) {
|
|
609
597
|
const head = length * 2
|
|
610
598
|
const fullRoots = flat.fullRoots(head)
|
|
611
599
|
|
|
@@ -614,7 +602,7 @@ class MerkleTree {
|
|
|
614
602
|
if (i < batch.roots.length && batch.roots[i].index === root) continue
|
|
615
603
|
|
|
616
604
|
while (batch.roots.length > i) batch.roots.pop()
|
|
617
|
-
batch.roots.push(unslabNode(await this.get(root)))
|
|
605
|
+
batch.roots.push(unslabNode(await this.get(session, root)))
|
|
618
606
|
}
|
|
619
607
|
|
|
620
608
|
while (batch.roots.length > fullRoots.length) {
|
|
@@ -630,7 +618,7 @@ class MerkleTree {
|
|
|
630
618
|
return batch
|
|
631
619
|
}
|
|
632
620
|
|
|
633
|
-
async reorg (proof, batch) {
|
|
621
|
+
static async reorg (session, proof, batch) {
|
|
634
622
|
let unverified = null
|
|
635
623
|
|
|
636
624
|
if (proof.block || proof.hash || proof.seek) {
|
|
@@ -642,7 +630,7 @@ class MerkleTree {
|
|
|
642
630
|
}
|
|
643
631
|
|
|
644
632
|
for (const root of batch.roots) {
|
|
645
|
-
const existing = await
|
|
633
|
+
const existing = await MerkleTree.get(session, root.index, false)
|
|
646
634
|
if (existing && b4a.equals(existing.hash, root.hash)) continue
|
|
647
635
|
batch._updateDiffRoot(root)
|
|
648
636
|
break
|
|
@@ -658,9 +646,9 @@ class MerkleTree {
|
|
|
658
646
|
return batch
|
|
659
647
|
}
|
|
660
648
|
|
|
661
|
-
verifyFullyRemote (
|
|
649
|
+
static verifyFullyRemote (session, proof) {
|
|
662
650
|
// TODO: impl this less hackishly
|
|
663
|
-
const batch = new MerkleTreeBatch(
|
|
651
|
+
const batch = new MerkleTreeBatch(session)
|
|
664
652
|
|
|
665
653
|
batch.fork = proof.fork
|
|
666
654
|
batch.roots = []
|
|
@@ -679,8 +667,8 @@ class MerkleTree {
|
|
|
679
667
|
return batch
|
|
680
668
|
}
|
|
681
669
|
|
|
682
|
-
async verify (
|
|
683
|
-
const batch = new MerkleTreeBatch(
|
|
670
|
+
static async verify (session, proof) {
|
|
671
|
+
const batch = new MerkleTreeBatch(session)
|
|
684
672
|
|
|
685
673
|
let unverified = verifyTree(proof, batch.nodes)
|
|
686
674
|
|
|
@@ -691,7 +679,7 @@ class MerkleTree {
|
|
|
691
679
|
}
|
|
692
680
|
|
|
693
681
|
if (unverified) {
|
|
694
|
-
const verified = await
|
|
682
|
+
const verified = await MerkleTree.get(session, unverified.index)
|
|
695
683
|
if (!b4a.equals(verified.hash, unverified.hash)) {
|
|
696
684
|
throw INVALID_CHECKSUM('Invalid checksum at node ' + unverified.index)
|
|
697
685
|
}
|
|
@@ -700,12 +688,11 @@ class MerkleTree {
|
|
|
700
688
|
return batch
|
|
701
689
|
}
|
|
702
690
|
|
|
703
|
-
proof (
|
|
704
|
-
return generateProof(
|
|
691
|
+
static proof (session, rx, { block, hash, seek, upgrade }) {
|
|
692
|
+
return generateProof(session, rx, block, hash, seek, upgrade)
|
|
705
693
|
}
|
|
706
694
|
|
|
707
|
-
|
|
708
|
-
async missingNodes (index, length) {
|
|
695
|
+
static async missingNodes (session, index, length) {
|
|
709
696
|
const head = 2 * length
|
|
710
697
|
const ite = flat.iterator(index)
|
|
711
698
|
|
|
@@ -716,7 +703,7 @@ class MerkleTree {
|
|
|
716
703
|
|
|
717
704
|
let cnt = 0
|
|
718
705
|
// TODO: we could prop use a read batch here and do this in blocks of X for perf
|
|
719
|
-
while (!ite.contains(head) && !(await hasTreeNode(
|
|
706
|
+
while (!ite.contains(head) && !(await hasTreeNode(session.storage, ite.index))) {
|
|
720
707
|
cnt++
|
|
721
708
|
ite.parent()
|
|
722
709
|
}
|
|
@@ -724,27 +711,16 @@ class MerkleTree {
|
|
|
724
711
|
return cnt
|
|
725
712
|
}
|
|
726
713
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
const head = 2 * this.length
|
|
730
|
-
const ite = flat.iterator(index)
|
|
731
|
-
|
|
732
|
-
let cnt = 0
|
|
733
|
-
while (!ite.contains(head) && (await this.get(ite.index, false)) === null) {
|
|
734
|
-
cnt++
|
|
735
|
-
ite.parent()
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
return cnt
|
|
714
|
+
static byteOffset (session, index) {
|
|
715
|
+
return getByteOffsetSession(session, index, null)
|
|
739
716
|
}
|
|
740
717
|
|
|
741
|
-
static
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null, opts.prologue || null)
|
|
718
|
+
static byteRange (session, index) {
|
|
719
|
+
const rx = session.storage.read()
|
|
720
|
+
const offset = getByteOffsetSession(session, index, rx)
|
|
721
|
+
const size = getNodeSize(index, rx)
|
|
722
|
+
rx.tryFlush()
|
|
723
|
+
return Promise.all([offset, size])
|
|
748
724
|
}
|
|
749
725
|
}
|
|
750
726
|
|
|
@@ -758,14 +734,44 @@ async function getNodeSize (index, readBatch) {
|
|
|
758
734
|
return (await readBatch.getTreeNode(index, true)).size
|
|
759
735
|
}
|
|
760
736
|
|
|
761
|
-
function
|
|
737
|
+
async function getByteOffsetSession (session, index, readBatch) {
|
|
738
|
+
if (index === 2 * session.length) return session.byteLength
|
|
739
|
+
|
|
740
|
+
const treeNodes = readBatch === null
|
|
741
|
+
? await getByteOffsetBatchFlush(session.roots, index, session.storage.read())
|
|
742
|
+
: await getByteOffsetBatch(session.roots, index, readBatch)
|
|
743
|
+
|
|
744
|
+
let offset = 0
|
|
745
|
+
for (const node of treeNodes) offset += node.size
|
|
746
|
+
|
|
747
|
+
return offset
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
async function getByteOffset (tree, index, readBatch) {
|
|
751
|
+
if (index === 2 * tree.length) return tree.byteLength
|
|
752
|
+
|
|
753
|
+
const treeNodes = await getByteOffsetBatch(tree.roots, index, readBatch)
|
|
754
|
+
|
|
755
|
+
let offset = 0
|
|
756
|
+
for (const node of treeNodes) offset += node.size
|
|
757
|
+
|
|
758
|
+
return offset
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function getByteOffsetBatchFlush (roots, index, readBatch) {
|
|
762
|
+
const treeNodes = getByteOffsetBatch(roots, index, readBatch)
|
|
763
|
+
readBatch.tryFlush()
|
|
764
|
+
return treeNodes
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function getByteOffsetBatch (roots, index, readBatch) {
|
|
762
768
|
if ((index & 1) === 1) index = flat.leftSpan(index)
|
|
763
769
|
|
|
764
770
|
let head = 0
|
|
765
771
|
|
|
766
772
|
const promises = []
|
|
767
773
|
|
|
768
|
-
for (const node of
|
|
774
|
+
for (const node of roots) { // all async ticks happen once we find the root so safe
|
|
769
775
|
head += 2 * ((node.index - head) + 1)
|
|
770
776
|
|
|
771
777
|
if (index >= head) {
|
|
@@ -779,7 +785,7 @@ function getByteOffsetTree (tree, index, readBatch) {
|
|
|
779
785
|
if (index < ite.index) {
|
|
780
786
|
ite.leftChild()
|
|
781
787
|
} else {
|
|
782
|
-
promises.push(
|
|
788
|
+
promises.push(readBatch.getTreeNode(ite.leftChild(), true))
|
|
783
789
|
ite.sibling()
|
|
784
790
|
}
|
|
785
791
|
}
|
|
@@ -790,17 +796,6 @@ function getByteOffsetTree (tree, index, readBatch) {
|
|
|
790
796
|
throw ASSERTION('Failed to find offset')
|
|
791
797
|
}
|
|
792
798
|
|
|
793
|
-
async function getByteOffset (tree, index, readBatch) {
|
|
794
|
-
if (index === 2 * tree.length) return tree.byteLength
|
|
795
|
-
|
|
796
|
-
const treeNodes = await getByteOffsetTree(tree, index, readBatch)
|
|
797
|
-
|
|
798
|
-
let offset = 0
|
|
799
|
-
for (const node of treeNodes) offset += node.size
|
|
800
|
-
|
|
801
|
-
return offset
|
|
802
|
-
}
|
|
803
|
-
|
|
804
799
|
function getByteRange (tree, index, readBatch) {
|
|
805
800
|
const head = 2 * tree.length
|
|
806
801
|
if (((index & 1) === 0 ? index : flat.rightSpan(index)) >= head) {
|
|
@@ -935,12 +930,13 @@ function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
|
|
|
935
930
|
return q.extra === null
|
|
936
931
|
}
|
|
937
932
|
|
|
938
|
-
async function seekFromHead (
|
|
933
|
+
async function seekFromHead (session, head, bytes, padding) {
|
|
939
934
|
const roots = flat.fullRoots(head)
|
|
940
935
|
|
|
941
936
|
for (let i = 0; i < roots.length; i++) {
|
|
942
937
|
const root = roots[i]
|
|
943
|
-
const node = await
|
|
938
|
+
const node = await MerkleTree.get(session, root, false)
|
|
939
|
+
if (node === null) throw new Error('Tree node missing')
|
|
944
940
|
const size = getUnpaddedSize(node, padding, null)
|
|
945
941
|
|
|
946
942
|
if (bytes === size) return root
|
|
@@ -949,7 +945,7 @@ async function seekFromHead (tree, head, bytes, padding) {
|
|
|
949
945
|
continue
|
|
950
946
|
}
|
|
951
947
|
|
|
952
|
-
return seekTrustedTree(
|
|
948
|
+
return seekTrustedTree(session, root, bytes, padding)
|
|
953
949
|
}
|
|
954
950
|
|
|
955
951
|
return head
|
|
@@ -957,13 +953,13 @@ async function seekFromHead (tree, head, bytes, padding) {
|
|
|
957
953
|
|
|
958
954
|
// trust that bytes are within the root tree and find the block at bytes
|
|
959
955
|
|
|
960
|
-
async function seekTrustedTree (
|
|
956
|
+
async function seekTrustedTree (session, root, bytes, padding) {
|
|
961
957
|
if (!bytes) return root
|
|
962
958
|
|
|
963
959
|
const ite = flat.iterator(root)
|
|
964
960
|
|
|
965
961
|
while ((ite.index & 1) !== 0) {
|
|
966
|
-
const l = await
|
|
962
|
+
const l = await MerkleTree.get(session, ite.leftChild(), false)
|
|
967
963
|
|
|
968
964
|
if (l) {
|
|
969
965
|
const size = getUnpaddedSize(l, padding, ite)
|
|
@@ -982,60 +978,61 @@ async function seekTrustedTree (tree, root, bytes, padding) {
|
|
|
982
978
|
|
|
983
979
|
// try to find the block at bytes without trusting that is *is* within the root passed
|
|
984
980
|
|
|
985
|
-
async function seekUntrustedTree (
|
|
986
|
-
const offset = await
|
|
981
|
+
async function seekUntrustedTree (session, root, bytes, padding) {
|
|
982
|
+
const offset = await getByteOffsetSession(session, root, null) - (padding ? padding * flat.leftSpan(root) / 2 : 0)
|
|
987
983
|
|
|
988
984
|
if (offset > bytes) throw INVALID_OPERATION('Invalid seek')
|
|
989
985
|
if (offset === bytes) return root
|
|
990
986
|
|
|
991
987
|
bytes -= offset
|
|
992
988
|
|
|
993
|
-
const node = await
|
|
989
|
+
const node = await MerkleTree.get(session, root, false)
|
|
990
|
+
if (node === null) throw new Error('Tree node missing')
|
|
994
991
|
|
|
995
992
|
if (getUnpaddedSize(node, padding, null) <= bytes) throw INVALID_OPERATION('Invalid seek')
|
|
996
993
|
|
|
997
|
-
return seekTrustedTree(
|
|
994
|
+
return seekTrustedTree(session, root, bytes, padding)
|
|
998
995
|
}
|
|
999
996
|
|
|
1000
997
|
// Below is proof production, ie, construct proofs to verify a request
|
|
1001
998
|
// Note, that all these methods are sync as we can statically infer which nodes
|
|
1002
999
|
// are needed for the remote to verify given they arguments they passed us
|
|
1003
1000
|
|
|
1004
|
-
function seekProof (
|
|
1001
|
+
function seekProof (session, batch, seekRoot, root, p) {
|
|
1005
1002
|
const ite = flat.iterator(seekRoot)
|
|
1006
1003
|
|
|
1007
1004
|
p.seek = []
|
|
1008
|
-
p.seek.push(
|
|
1005
|
+
p.seek.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1009
1006
|
|
|
1010
1007
|
while (ite.index !== root) {
|
|
1011
1008
|
ite.sibling()
|
|
1012
|
-
p.seek.push(
|
|
1009
|
+
p.seek.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1013
1010
|
ite.parent()
|
|
1014
1011
|
}
|
|
1015
1012
|
}
|
|
1016
1013
|
|
|
1017
|
-
function blockAndSeekProof (
|
|
1018
|
-
if (!node) return seekProof(
|
|
1014
|
+
function blockAndSeekProof (session, batch, node, seek, seekRoot, root, p) {
|
|
1015
|
+
if (!node) return seekProof(session, batch, seekRoot, root, p)
|
|
1019
1016
|
|
|
1020
1017
|
const ite = flat.iterator(node.index)
|
|
1021
1018
|
|
|
1022
1019
|
p.node = []
|
|
1023
|
-
if (!node.value) p.node.push(
|
|
1020
|
+
if (!node.value) p.node.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1024
1021
|
|
|
1025
1022
|
while (ite.index !== root) {
|
|
1026
1023
|
ite.sibling()
|
|
1027
1024
|
|
|
1028
1025
|
if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {
|
|
1029
|
-
seekProof(
|
|
1026
|
+
seekProof(session, batch, seekRoot, ite.index, p)
|
|
1030
1027
|
} else {
|
|
1031
|
-
p.node.push(
|
|
1028
|
+
p.node.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1032
1029
|
}
|
|
1033
1030
|
|
|
1034
1031
|
ite.parent()
|
|
1035
1032
|
}
|
|
1036
1033
|
}
|
|
1037
1034
|
|
|
1038
|
-
function upgradeProof (
|
|
1035
|
+
function upgradeProof (session, batch, node, seek, from, to, subTree, p) {
|
|
1039
1036
|
if (from === 0) p.upgrade = []
|
|
1040
1037
|
|
|
1041
1038
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -1055,7 +1052,7 @@ function upgradeProof (tree, batch, node, seek, from, to, subTree, p) {
|
|
|
1055
1052
|
ite.sibling()
|
|
1056
1053
|
if (ite.index > target) {
|
|
1057
1054
|
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
1058
|
-
blockAndSeekProof(
|
|
1055
|
+
blockAndSeekProof(session, batch, node, seek, subTree, ite.index, p)
|
|
1059
1056
|
} else {
|
|
1060
1057
|
p.upgrade.push(batch.getTreeNode(ite.index, true))
|
|
1061
1058
|
}
|
|
@@ -1073,16 +1070,16 @@ function upgradeProof (tree, batch, node, seek, from, to, subTree, p) {
|
|
|
1073
1070
|
// if the subtree included is a child of this tree, include that one
|
|
1074
1071
|
// instead of a dup node
|
|
1075
1072
|
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
1076
|
-
blockAndSeekProof(
|
|
1073
|
+
blockAndSeekProof(session, batch, node, seek, subTree, ite.index, p)
|
|
1077
1074
|
continue
|
|
1078
1075
|
}
|
|
1079
1076
|
|
|
1080
1077
|
// add root (can be optimised since the root might be in tree.roots)
|
|
1081
|
-
p.upgrade.push(
|
|
1078
|
+
p.upgrade.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1082
1079
|
}
|
|
1083
1080
|
}
|
|
1084
1081
|
|
|
1085
|
-
function additionalUpgradeProof (
|
|
1082
|
+
function additionalUpgradeProof (session, batch, from, to, p) {
|
|
1086
1083
|
if (from === 0) p.additionalUpgrade = []
|
|
1087
1084
|
|
|
1088
1085
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -1101,7 +1098,7 @@ function additionalUpgradeProof (tree, batch, from, to, p) {
|
|
|
1101
1098
|
while (ite.index !== root) {
|
|
1102
1099
|
ite.sibling()
|
|
1103
1100
|
if (ite.index > target) {
|
|
1104
|
-
p.additionalUpgrade.push(
|
|
1101
|
+
p.additionalUpgrade.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1105
1102
|
}
|
|
1106
1103
|
ite.parent()
|
|
1107
1104
|
}
|
|
@@ -1114,7 +1111,7 @@ function additionalUpgradeProof (tree, batch, from, to, p) {
|
|
|
1114
1111
|
}
|
|
1115
1112
|
|
|
1116
1113
|
// add root (can be optimised since the root is in tree.roots)
|
|
1117
|
-
p.additionalUpgrade.push(
|
|
1114
|
+
p.additionalUpgrade.push(MerkleTree.get(session, ite.index, true, batch))
|
|
1118
1115
|
}
|
|
1119
1116
|
}
|
|
1120
1117
|
|
|
@@ -1200,22 +1197,22 @@ async function settleProof (p) {
|
|
|
1200
1197
|
}
|
|
1201
1198
|
|
|
1202
1199
|
// tree can be either the merkle tree or a merkle tree batch
|
|
1203
|
-
async function generateProof (
|
|
1200
|
+
async function generateProof (session, readBatch, block, hash, seek, upgrade) {
|
|
1204
1201
|
// Important that this does not throw inbetween making the promise arrays
|
|
1205
1202
|
// and finalise being called, otherwise there will be lingering promises in the background
|
|
1206
1203
|
|
|
1207
|
-
if (
|
|
1208
|
-
upgrade.start = upgrade.start <
|
|
1209
|
-
upgrade.length = upgrade.start <
|
|
1204
|
+
if (session.prologue && upgrade) {
|
|
1205
|
+
upgrade.start = upgrade.start < session.prologue.length ? 0 : upgrade.start
|
|
1206
|
+
upgrade.length = upgrade.start < session.prologue.length ? session.prologue.length : upgrade.length
|
|
1210
1207
|
}
|
|
1211
1208
|
|
|
1212
|
-
const head = 2 *
|
|
1209
|
+
const head = 2 * session.length
|
|
1213
1210
|
const from = upgrade ? upgrade.start * 2 : 0
|
|
1214
1211
|
const to = upgrade ? from + upgrade.length * 2 : head
|
|
1215
1212
|
const node = normalizeIndexed(block, hash)
|
|
1216
1213
|
|
|
1217
1214
|
// can't do anything as we have no data...
|
|
1218
|
-
if (head === 0) return new TreeProof(
|
|
1215
|
+
if (head === 0) return new TreeProof(session, null, null, null, null)
|
|
1219
1216
|
|
|
1220
1217
|
if (from >= to || to > head) {
|
|
1221
1218
|
throw INVALID_OPERATION('Invalid upgrade')
|
|
@@ -1226,19 +1223,19 @@ async function generateProof (readBatch, tree, block, hash, seek, upgrade) {
|
|
|
1226
1223
|
|
|
1227
1224
|
let subTree = head
|
|
1228
1225
|
|
|
1229
|
-
const p = new TreeProof(
|
|
1226
|
+
const p = new TreeProof(session, block, hash, seek, upgrade)
|
|
1230
1227
|
|
|
1231
1228
|
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
1232
1229
|
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
1233
|
-
const seekRoot = seek ? await seekUntrustedTree(
|
|
1234
|
-
blockAndSeekProof(
|
|
1230
|
+
const seekRoot = seek ? await seekUntrustedTree(session, subTree, seek.bytes, seek.padding) : head
|
|
1231
|
+
blockAndSeekProof(session, readBatch, node, seek, seekRoot, subTree, p.pending)
|
|
1235
1232
|
} else if ((node || seek) && upgrade) {
|
|
1236
|
-
subTree = seek ? await seekFromHead(
|
|
1233
|
+
subTree = seek ? await seekFromHead(session, to, seek.bytes, seek.padding) : node.index
|
|
1237
1234
|
}
|
|
1238
1235
|
|
|
1239
1236
|
if (upgrade) {
|
|
1240
|
-
upgradeProof(
|
|
1241
|
-
if (head > to) additionalUpgradeProof(
|
|
1237
|
+
upgradeProof(session, readBatch, node, seek, from, to, subTree, p.pending)
|
|
1238
|
+
if (head > to) additionalUpgradeProof(session, readBatch, to, head, p.pending)
|
|
1242
1239
|
}
|
|
1243
1240
|
|
|
1244
1241
|
return p
|
package/lib/multisig.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const c = require('compact-encoding')
|
|
2
2
|
const b4a = require('b4a')
|
|
3
3
|
const flat = require('flat-tree')
|
|
4
|
+
const { MerkleTree } = require('./merkle-tree')
|
|
4
5
|
const { multiSignature, multiSignaturev0 } = require('./messages')
|
|
5
6
|
|
|
6
7
|
module.exports = {
|
|
@@ -36,8 +37,7 @@ async function partialSignature (core, signer, from, to = core.state.length, sig
|
|
|
36
37
|
|
|
37
38
|
async function upgradeNodes (core, from, to) {
|
|
38
39
|
const rx = core.state.storage.read()
|
|
39
|
-
const
|
|
40
|
-
const p = await core.state.tree.proof(rx, treeBatch, { upgrade: { start: from, length: to - from } })
|
|
40
|
+
const p = await MerkleTree.proof(core.state, rx, { upgrade: { start: from, length: to - from } })
|
|
41
41
|
rx.tryFlush()
|
|
42
42
|
return (await p.settle()).upgrade.nodes
|
|
43
43
|
}
|
package/lib/replicator.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
- Which blocks does the Peer have available (tracked in remoteBitfield)
|
|
20
20
|
- Which blocks are you actively looking for from this peer (tracked in missingBlocks)
|
|
21
21
|
- How many blocks are currently inflight (tracked in inflight)
|
|
22
|
-
The Peer uses this information to decide which blocks to request
|
|
22
|
+
The Peer uses this information to decide which blocks to request from the peer in response to _requestRange requests and the like.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
const b4a = require('b4a')
|
|
@@ -29,6 +29,7 @@ const flatTree = require('flat-tree')
|
|
|
29
29
|
const ReceiverQueue = require('./receiver-queue')
|
|
30
30
|
const HotswapQueue = require('./hotswap-queue')
|
|
31
31
|
const RemoteBitfield = require('./remote-bitfield')
|
|
32
|
+
const { MerkleTree } = require('./merkle-tree')
|
|
32
33
|
const { REQUEST_CANCELLED, REQUEST_TIMEOUT, INVALID_CAPABILITY, SNAPSHOT_NOT_AVAILABLE } = require('hypercore-errors')
|
|
33
34
|
const m = require('./messages')
|
|
34
35
|
const caps = require('./caps')
|
|
@@ -676,7 +677,7 @@ class Peer {
|
|
|
676
677
|
|
|
677
678
|
try {
|
|
678
679
|
// Rely on caching to make sure this is cheap...
|
|
679
|
-
const canUpgrade = await this.core.
|
|
680
|
+
const canUpgrade = await MerkleTree.upgradeable(this.core.state, remoteLength)
|
|
680
681
|
|
|
681
682
|
if (remoteFork !== this.core.state.fork) return false
|
|
682
683
|
|
|
@@ -696,12 +697,11 @@ class Peer {
|
|
|
696
697
|
return new ProofRequest(msg, null, null, null)
|
|
697
698
|
}
|
|
698
699
|
|
|
699
|
-
block =
|
|
700
|
+
block = batch.getBlock(index)
|
|
700
701
|
}
|
|
701
702
|
|
|
702
703
|
const manifest = (msg.manifest && !this.core.compat) ? this.core.header.manifest : null
|
|
703
|
-
const
|
|
704
|
-
const proof = await this.core.tree.proof(batch, treeBatch, msg)
|
|
704
|
+
const proof = await MerkleTree.proof(this.core.state, batch, msg)
|
|
705
705
|
|
|
706
706
|
return new ProofRequest(msg, proof, block, manifest)
|
|
707
707
|
}
|
|
@@ -1404,11 +1404,11 @@ class Peer {
|
|
|
1404
1404
|
|
|
1405
1405
|
try {
|
|
1406
1406
|
if (req.block !== null && req.fork === fork) {
|
|
1407
|
-
req.block.nodes = await this.core.
|
|
1407
|
+
req.block.nodes = await MerkleTree.missingNodes(this.core.state, 2 * req.block.index, this.core.state.length)
|
|
1408
1408
|
if (req.priority === PRIORITY.CANCELLED) return
|
|
1409
1409
|
}
|
|
1410
1410
|
if (req.hash !== null && req.fork === fork && req.hash.nodes === 0) {
|
|
1411
|
-
req.hash.nodes = await this.core.
|
|
1411
|
+
req.hash.nodes = await MerkleTree.missingNodes(this.core.state, req.hash.index, this.core.state.length)
|
|
1412
1412
|
if (req.priority === PRIORITY.CANCELLED) return
|
|
1413
1413
|
|
|
1414
1414
|
// nodes === 0, we already have it, bail
|
|
@@ -1442,7 +1442,7 @@ module.exports = class Replicator {
|
|
|
1442
1442
|
this.allowFork = allowFork
|
|
1443
1443
|
this.ondownloading = null // optional external hook for monitoring downloading status
|
|
1444
1444
|
this.peers = []
|
|
1445
|
-
this.findingPeers = 0 //
|
|
1445
|
+
this.findingPeers = 0 // updatable from the outside
|
|
1446
1446
|
this.destroyed = false
|
|
1447
1447
|
this.downloading = false
|
|
1448
1448
|
this.activeSessions = 0
|
|
@@ -1856,7 +1856,7 @@ module.exports = class Replicator {
|
|
|
1856
1856
|
|
|
1857
1857
|
async _resolveLocalBlock (b, reader, resolved) {
|
|
1858
1858
|
try {
|
|
1859
|
-
b.resolve(await
|
|
1859
|
+
b.resolve(await reader.getBlock(b.index))
|
|
1860
1860
|
} catch (err) {
|
|
1861
1861
|
b.reject(err)
|
|
1862
1862
|
return
|
|
@@ -2538,7 +2538,7 @@ function clampRange (core, r) {
|
|
|
2538
2538
|
if (r.blocks === null) {
|
|
2539
2539
|
const start = core.bitfield.firstUnset(r.start)
|
|
2540
2540
|
|
|
2541
|
-
if (r.end === -1) r.start = start === -1 ? core.
|
|
2541
|
+
if (r.end === -1) r.start = start === -1 ? core.state.length : start
|
|
2542
2542
|
else if (start === -1 || start >= r.end) r.start = r.end
|
|
2543
2543
|
else {
|
|
2544
2544
|
r.start = start
|
package/lib/session-state.js
CHANGED
|
@@ -11,7 +11,7 @@ const Bitfield = require('./bitfield')
|
|
|
11
11
|
const { MerkleTree, MerkleTreeBatch } = require('./merkle-tree')
|
|
12
12
|
|
|
13
13
|
module.exports = class SessionState {
|
|
14
|
-
constructor (core, parent, storage,
|
|
14
|
+
constructor (core, parent, storage, treeInfo, name) {
|
|
15
15
|
this.core = core
|
|
16
16
|
this.index = this.core.sessionStates.push(this) - 1
|
|
17
17
|
|
|
@@ -27,26 +27,20 @@ module.exports = class SessionState {
|
|
|
27
27
|
this.atomized = null
|
|
28
28
|
this.mutex = new Mutex()
|
|
29
29
|
|
|
30
|
-
this.blocks = blocks
|
|
31
|
-
this.tree = tree
|
|
32
|
-
|
|
33
30
|
this.treeFork = -1 // only updated if truncated below dependency
|
|
34
31
|
|
|
35
32
|
// merkle state
|
|
36
|
-
this.roots = []
|
|
37
|
-
this.length = 0
|
|
33
|
+
this.roots = treeInfo.roots.length ? treeInfo.roots : []
|
|
38
34
|
this.fork = treeInfo.fork || 0
|
|
35
|
+
this.length = MerkleTree.span(this.roots) / 2
|
|
36
|
+
this.byteLength = MerkleTree.size(this.roots)
|
|
39
37
|
this.prologue = treeInfo.prologue || null
|
|
40
38
|
this.signature = treeInfo.signature || null
|
|
41
39
|
|
|
42
|
-
if (treeInfo.roots.length) this.setRoots(treeInfo.roots)
|
|
43
|
-
|
|
44
40
|
this.snapshotCompatLength = this.isSnapshot() ? Math.min(this.length, this.core.state.length) : -1
|
|
45
41
|
|
|
46
42
|
this.active = 0
|
|
47
43
|
|
|
48
|
-
this._onflush = null
|
|
49
|
-
this._flushing = null
|
|
50
44
|
this._activeTx = null
|
|
51
45
|
this._pendingBitfield = null
|
|
52
46
|
|
|
@@ -66,7 +60,7 @@ module.exports = class SessionState {
|
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
createTreeBatch () {
|
|
69
|
-
return new MerkleTreeBatch(this
|
|
63
|
+
return new MerkleTreeBatch(this)
|
|
70
64
|
}
|
|
71
65
|
|
|
72
66
|
addSession (s) {
|
|
@@ -113,16 +107,13 @@ module.exports = class SessionState {
|
|
|
113
107
|
setRoots (roots) {
|
|
114
108
|
this.roots = roots
|
|
115
109
|
this.length = MerkleTree.span(roots) / 2
|
|
110
|
+
this.byteLength = MerkleTree.size(roots)
|
|
116
111
|
}
|
|
117
112
|
|
|
118
113
|
get encryptionFork () {
|
|
119
114
|
return this.treeFork === -1 ? this.core.header.tree.fork : this.treeFork
|
|
120
115
|
}
|
|
121
116
|
|
|
122
|
-
get byteLength () {
|
|
123
|
-
return MerkleTree.size(this.roots)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
117
|
async getFlushedTreeInfo (storage) {
|
|
127
118
|
if (!this.atomized || !this.atomized.flushing) return this.treeInfo()
|
|
128
119
|
await this.atomized.flushed()
|
|
@@ -203,8 +194,6 @@ module.exports = class SessionState {
|
|
|
203
194
|
this.core,
|
|
204
195
|
null,
|
|
205
196
|
storage,
|
|
206
|
-
this.blocks,
|
|
207
|
-
this.tree.clone(storage),
|
|
208
197
|
treeInfo,
|
|
209
198
|
this.name
|
|
210
199
|
)
|
|
@@ -215,19 +204,10 @@ module.exports = class SessionState {
|
|
|
215
204
|
updateDependency (tx, length) {
|
|
216
205
|
const dependency = updateDependency(this, length, false)
|
|
217
206
|
if (dependency) tx.setDependency(dependency)
|
|
218
|
-
|
|
219
207
|
return dependency
|
|
220
208
|
}
|
|
221
209
|
|
|
222
|
-
_clearActiveBatch (
|
|
223
|
-
if (!this._activeTx) return
|
|
224
|
-
this._activeTx = null
|
|
225
|
-
|
|
226
|
-
if (this._onflush) this._onflush(err)
|
|
227
|
-
|
|
228
|
-
this._onflush = null
|
|
229
|
-
this._flushing = null
|
|
230
|
-
|
|
210
|
+
_clearActiveBatch () {
|
|
231
211
|
this._activeTx = null
|
|
232
212
|
}
|
|
233
213
|
|
|
@@ -238,7 +218,7 @@ module.exports = class SessionState {
|
|
|
238
218
|
return this._activeTx
|
|
239
219
|
}
|
|
240
220
|
|
|
241
|
-
_unlock (
|
|
221
|
+
_unlock () {
|
|
242
222
|
this._clearActiveBatch()
|
|
243
223
|
this.mutex.unlock()
|
|
244
224
|
this.core.checkIfIdle()
|
|
@@ -248,12 +228,8 @@ module.exports = class SessionState {
|
|
|
248
228
|
const tx = this._activeTx
|
|
249
229
|
this._activeTx = null
|
|
250
230
|
|
|
251
|
-
const flushing = tx.flush()
|
|
252
|
-
|
|
253
231
|
try {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return flushing
|
|
232
|
+
return await tx.flush()
|
|
257
233
|
} finally {
|
|
258
234
|
this._clearActiveBatch()
|
|
259
235
|
}
|
|
@@ -284,6 +260,7 @@ module.exports = class SessionState {
|
|
|
284
260
|
|
|
285
261
|
this.fork = src.fork
|
|
286
262
|
this.length = src.length
|
|
263
|
+
this.byteLength = src.byteLength
|
|
287
264
|
this.roots = src.roots.slice()
|
|
288
265
|
this.signature = src.signature
|
|
289
266
|
|
|
@@ -314,21 +291,10 @@ module.exports = class SessionState {
|
|
|
314
291
|
this.onappend(tree, bitfield, true)
|
|
315
292
|
} finally {
|
|
316
293
|
this.mutex.unlock()
|
|
294
|
+
this.core.checkIfIdle()
|
|
317
295
|
}
|
|
318
296
|
}
|
|
319
297
|
|
|
320
|
-
flushed () {
|
|
321
|
-
if (!this._activeTx) return
|
|
322
|
-
|
|
323
|
-
if (this._flushing) return this._flushing
|
|
324
|
-
|
|
325
|
-
this._flushing = new Promise(resolve => {
|
|
326
|
-
this._onflush = resolve
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
return this._flushing
|
|
330
|
-
}
|
|
331
|
-
|
|
332
298
|
async setUserData (key, value) {
|
|
333
299
|
await this.mutex.lock()
|
|
334
300
|
|
|
@@ -350,7 +316,9 @@ module.exports = class SessionState {
|
|
|
350
316
|
const tx = this.createWriteBatch()
|
|
351
317
|
this.updating = true
|
|
352
318
|
|
|
353
|
-
if (bitfield)
|
|
319
|
+
if (bitfield) {
|
|
320
|
+
tx.putBlock(bitfield.start, value)
|
|
321
|
+
}
|
|
354
322
|
|
|
355
323
|
if (bitfield && this.isDefault()) {
|
|
356
324
|
await storeBitfieldRange(this.storage, tx, bitfield.start, bitfield.start + 1, true)
|
|
@@ -375,6 +343,7 @@ module.exports = class SessionState {
|
|
|
375
343
|
if (batch.upgraded) {
|
|
376
344
|
this.roots = batch.roots
|
|
377
345
|
this.length = batch.length
|
|
346
|
+
this.byteLength = batch.byteLength
|
|
378
347
|
this.fork = batch.fork
|
|
379
348
|
this.signature = batch.signature
|
|
380
349
|
|
|
@@ -400,7 +369,7 @@ module.exports = class SessionState {
|
|
|
400
369
|
|
|
401
370
|
try {
|
|
402
371
|
const batch = this.createTreeBatch()
|
|
403
|
-
await
|
|
372
|
+
await MerkleTree.truncate(this, length, batch, fork)
|
|
404
373
|
|
|
405
374
|
if (!signature && keyPair && length > 0) signature = this.core.verifier.sign(batch, keyPair)
|
|
406
375
|
if (signature) batch.signature = signature
|
|
@@ -422,6 +391,7 @@ module.exports = class SessionState {
|
|
|
422
391
|
|
|
423
392
|
this.fork = tree.fork
|
|
424
393
|
this.length = tree.length
|
|
394
|
+
this.byteLength = MerkleTree.size(roots)
|
|
425
395
|
this.roots = roots
|
|
426
396
|
this.signature = tree.signature
|
|
427
397
|
|
|
@@ -447,6 +417,7 @@ module.exports = class SessionState {
|
|
|
447
417
|
|
|
448
418
|
this.fork = batch.fork
|
|
449
419
|
this.length = batch.length
|
|
420
|
+
this.byteLength = batch.byteLength
|
|
450
421
|
this.roots = batch.roots
|
|
451
422
|
this.signature = batch.signature
|
|
452
423
|
|
|
@@ -501,7 +472,7 @@ module.exports = class SessionState {
|
|
|
501
472
|
}
|
|
502
473
|
}
|
|
503
474
|
|
|
504
|
-
|
|
475
|
+
tx.deleteBlockRange(start, end)
|
|
505
476
|
|
|
506
477
|
const dependency = start < this.flushedLength() ? updateDependency(this, start, true) : null
|
|
507
478
|
|
|
@@ -563,7 +534,9 @@ module.exports = class SessionState {
|
|
|
563
534
|
|
|
564
535
|
if (this.isDefault()) await storeBitfieldRange(this.storage, tx, batch.ancestors, batch.length, true)
|
|
565
536
|
|
|
566
|
-
|
|
537
|
+
for (let i = 0; i < values.length; i++) {
|
|
538
|
+
tx.putBlock(this.length + i, values[i])
|
|
539
|
+
}
|
|
567
540
|
|
|
568
541
|
const bitfield = {
|
|
569
542
|
drop: false,
|
|
@@ -576,6 +549,7 @@ module.exports = class SessionState {
|
|
|
576
549
|
this.fork = batch.fork
|
|
577
550
|
this.roots = batch.roots
|
|
578
551
|
this.length = batch.length
|
|
552
|
+
this.byteLength = batch.byteLength
|
|
579
553
|
this.signature = batch.signature
|
|
580
554
|
|
|
581
555
|
this.onappend(tree, bitfield, flushed)
|
|
@@ -717,6 +691,7 @@ module.exports = class SessionState {
|
|
|
717
691
|
this.fork = tree.fork
|
|
718
692
|
this.roots = roots
|
|
719
693
|
this.length = tree.length
|
|
694
|
+
this.byteLength = MerkleTree.size(roots)
|
|
720
695
|
|
|
721
696
|
if (truncating) this.ontruncate(tree, sharedLength, origLength, flushed)
|
|
722
697
|
if (sharedLength < length) this.onappend(tree, null, flushed)
|
|
@@ -819,7 +794,7 @@ module.exports = class SessionState {
|
|
|
819
794
|
signature
|
|
820
795
|
}
|
|
821
796
|
|
|
822
|
-
const upgraded = treeLength < this.length || this.length < length || tree.fork !== this.
|
|
797
|
+
const upgraded = treeLength < this.length || this.length < length || tree.fork !== this.fork
|
|
823
798
|
|
|
824
799
|
if (upgraded) tx.setHead(tree)
|
|
825
800
|
|
|
@@ -828,6 +803,7 @@ module.exports = class SessionState {
|
|
|
828
803
|
this.fork = tree.fork
|
|
829
804
|
this.roots = roots
|
|
830
805
|
this.length = length
|
|
806
|
+
this.byteLength = MerkleTree.size(roots)
|
|
831
807
|
this.signature = signature
|
|
832
808
|
|
|
833
809
|
return { tree, flushed }
|
|
@@ -861,7 +837,7 @@ module.exports = class SessionState {
|
|
|
861
837
|
if (treeLength < length) {
|
|
862
838
|
const tx = state.createWriteBatch()
|
|
863
839
|
|
|
864
|
-
|
|
840
|
+
tx.deleteBlockRange(treeLength, length)
|
|
865
841
|
const dependency = state.updateDependency(tx, length)
|
|
866
842
|
|
|
867
843
|
await state.flush(tx)
|
|
@@ -896,7 +872,7 @@ module.exports = class SessionState {
|
|
|
896
872
|
|
|
897
873
|
head.length = length
|
|
898
874
|
|
|
899
|
-
const roots = await this.
|
|
875
|
+
const roots = await MerkleTree.getRootsFromStorage(this.storage, length)
|
|
900
876
|
const rootHash = crypto.tree(roots)
|
|
901
877
|
|
|
902
878
|
head.fork = this.fork
|
|
@@ -952,19 +928,23 @@ module.exports = class SessionState {
|
|
|
952
928
|
|
|
953
929
|
// todo: validate treeInfo
|
|
954
930
|
|
|
931
|
+
let storage = null
|
|
932
|
+
|
|
955
933
|
if (resumed) {
|
|
956
|
-
|
|
934
|
+
storage = resumed
|
|
957
935
|
} else {
|
|
958
936
|
treeInfo.fork = fork
|
|
959
|
-
|
|
937
|
+
storage = await state.storage.createSession(this.name, treeInfo)
|
|
960
938
|
}
|
|
961
939
|
|
|
962
|
-
|
|
940
|
+
const roots = await MerkleTree.getRootsFromStorage(storage, length)
|
|
963
941
|
|
|
942
|
+
this.storage = storage
|
|
964
943
|
this.prologue = state.prologue
|
|
965
944
|
this.fork = fork
|
|
966
945
|
this.length = length
|
|
967
|
-
this.
|
|
946
|
+
this.byteLength = MerkleTree.size(roots)
|
|
947
|
+
this.roots = roots
|
|
968
948
|
|
|
969
949
|
if (truncation) {
|
|
970
950
|
const { dependency } = truncation
|
|
@@ -1016,11 +996,9 @@ module.exports = class SessionState {
|
|
|
1016
996
|
throw new Error('Session already atomized')
|
|
1017
997
|
}
|
|
1018
998
|
|
|
1019
|
-
const tree = new MerkleTree(storage)
|
|
1020
|
-
|
|
1021
999
|
const head = {
|
|
1022
1000
|
fork: this.fork,
|
|
1023
|
-
roots: length === this.length ? this.roots.slice() : await
|
|
1001
|
+
roots: length === this.length ? this.roots.slice() : await MerkleTree.getRootsFromStorage(storage, length),
|
|
1024
1002
|
length,
|
|
1025
1003
|
prologue: this.prologue,
|
|
1026
1004
|
signature: length === this.length ? this.signature : null
|
|
@@ -1030,8 +1008,6 @@ module.exports = class SessionState {
|
|
|
1030
1008
|
this.core,
|
|
1031
1009
|
atom ? this : null,
|
|
1032
1010
|
storage,
|
|
1033
|
-
this.core.blocks,
|
|
1034
|
-
tree,
|
|
1035
1011
|
head,
|
|
1036
1012
|
atom ? this.name : name
|
|
1037
1013
|
)
|
|
@@ -1094,7 +1070,7 @@ async function storeBitfieldRange (storage, tx, from, to, value) {
|
|
|
1094
1070
|
|
|
1095
1071
|
async function truncateAndFlush (s, length) {
|
|
1096
1072
|
const batch = s.createTreeBatch()
|
|
1097
|
-
await
|
|
1073
|
+
await MerkleTree.truncate(s, length, batch, s.fork)
|
|
1098
1074
|
const tx = s.createWriteBatch()
|
|
1099
1075
|
|
|
1100
1076
|
const info = await s._truncate(tx, batch)
|
package/package.json
CHANGED
package/lib/block-store.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module.exports = class BlockStore {
|
|
2
|
-
constructor (storage) {
|
|
3
|
-
this.storage = storage
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
async get (rx, i) {
|
|
7
|
-
return rx.getBlock(i)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
put (tx, i, data) {
|
|
11
|
-
tx.putBlock(i, data)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
putBatch (tx, i, blocks) {
|
|
15
|
-
if (blocks.length === 0) return Promise.resolve()
|
|
16
|
-
|
|
17
|
-
for (let j = 0; j < blocks.length; j++) {
|
|
18
|
-
tx.putBlock(i + j, blocks[j])
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
clear (tx, start = 0, end = -1) {
|
|
23
|
-
tx.deleteBlockRange(start, end)
|
|
24
|
-
}
|
|
25
|
-
}
|