hypercore 10.0.0-alpha.9 → 10.0.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/README.md +83 -22
- package/index.js +583 -212
- package/lib/bitfield.js +109 -41
- package/lib/block-encryption.js +3 -2
- package/lib/block-store.js +6 -4
- package/lib/caps.js +32 -0
- package/lib/core.js +166 -35
- package/lib/errors.js +50 -0
- package/lib/info.js +23 -0
- package/lib/merkle-tree.js +181 -105
- package/lib/messages.js +249 -168
- package/lib/oplog.js +4 -3
- package/lib/remote-bitfield.js +28 -7
- package/lib/replicator.js +1415 -624
- package/lib/streams.js +56 -0
- package/package.json +20 -15
- package/.github/workflows/test-node.yml +0 -23
- package/CHANGELOG.md +0 -37
- package/UPGRADE.md +0 -9
- package/examples/announce.js +0 -19
- package/examples/basic.js +0 -10
- package/examples/http.js +0 -123
- package/examples/lookup.js +0 -20
- package/lib/extensions.js +0 -76
- package/lib/protocol.js +0 -524
- package/lib/random-iterator.js +0 -46
- package/test/basic.js +0 -90
- package/test/bitfield.js +0 -71
- package/test/core.js +0 -290
- package/test/encodings.js +0 -18
- package/test/encryption.js +0 -121
- package/test/extension.js +0 -71
- package/test/helpers/index.js +0 -23
- package/test/merkle-tree.js +0 -518
- package/test/mutex.js +0 -137
- package/test/oplog.js +0 -399
- package/test/preload.js +0 -72
- package/test/replicate.js +0 -372
- package/test/sessions.js +0 -173
- package/test/user-data.js +0 -47
package/lib/errors.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module.exports = class HypercoreError extends Error {
|
|
2
|
+
constructor (msg, code, fn = HypercoreError) {
|
|
3
|
+
super(`${code}: ${msg}`)
|
|
4
|
+
this.code = code
|
|
5
|
+
|
|
6
|
+
if (Error.captureStackTrace) {
|
|
7
|
+
Error.captureStackTrace(this, fn)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
get name () {
|
|
12
|
+
return 'HypercoreError'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static BAD_ARGUMENT (msg) {
|
|
16
|
+
return new HypercoreError(msg, 'BAD_ARGUMENT', HypercoreError.BAD_ARGUMENT)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static STORAGE_EMPTY (msg) {
|
|
20
|
+
return new HypercoreError(msg, 'STORAGE_EMPTY', HypercoreError.STORAGE_EMPTY)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static STORAGE_CONFLICT (msg) {
|
|
24
|
+
return new HypercoreError(msg, 'STORAGE_CONFLICT', HypercoreError.STORAGE_CONFLICT)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static INVALID_SIGNATURE (msg) {
|
|
28
|
+
return new HypercoreError(msg, 'INVALID_SIGNATURE', HypercoreError.INVALID_SIGNATURE)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static INVALID_CAPABILITY (msg) {
|
|
32
|
+
return new HypercoreError(msg, 'INVALID_CAPABILITY', HypercoreError.INVALID_CAPABILITY)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static SNAPSHOT_NOT_AVAILABLE (msg = 'Snapshot is not available') {
|
|
36
|
+
return new HypercoreError(msg, 'SNAPSHOT_NOT_AVAILABLE', HypercoreError.SNAPSHOT_NOT_AVAILABLE)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static REQUEST_CANCELLED (msg = 'Request was cancelled') {
|
|
40
|
+
return new HypercoreError(msg, 'REQUEST_CANCELLED', HypercoreError.REQUEST_CANCELLED)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static SESSION_NOT_WRITABLE (msg = 'Session is not writable') {
|
|
44
|
+
return new HypercoreError(msg, 'SESSION_NOT_WRITABLE', HypercoreError.SESSION_NOT_WRITABLE)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static SESSION_CLOSED (msg = 'Session is closed') {
|
|
48
|
+
return new HypercoreError(msg, 'SESSION_CLOSED', HypercoreError.SESSION_CLOSED)
|
|
49
|
+
}
|
|
50
|
+
}
|
package/lib/info.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module.exports = class Info {
|
|
2
|
+
constructor (opts = {}) {
|
|
3
|
+
this.length = opts.length || 0
|
|
4
|
+
this.contiguousLength = opts.contiguousLength || 0
|
|
5
|
+
this.byteLength = opts.byteLength || 0
|
|
6
|
+
this.padding = opts.padding || 0
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
static async from (core, padding, snapshot) {
|
|
10
|
+
return new Info({
|
|
11
|
+
key: core.key,
|
|
12
|
+
length: snapshot
|
|
13
|
+
? snapshot.length
|
|
14
|
+
: core.tree.length,
|
|
15
|
+
contiguousLength: core.header.contiguousLength,
|
|
16
|
+
byteLength: snapshot
|
|
17
|
+
? snapshot.byteLength
|
|
18
|
+
: (core.tree.byteLength - (core.tree.length * padding)),
|
|
19
|
+
fork: core.tree.fork,
|
|
20
|
+
padding
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
}
|
package/lib/merkle-tree.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
const flat = require('flat-tree')
|
|
2
2
|
const crypto = require('hypercore-crypto')
|
|
3
|
-
const
|
|
3
|
+
const c = require('compact-encoding')
|
|
4
|
+
const Xache = require('xache')
|
|
5
|
+
const b4a = require('b4a')
|
|
6
|
+
const caps = require('./caps')
|
|
4
7
|
|
|
5
|
-
const BLANK_HASH =
|
|
6
|
-
const OLD_TREE =
|
|
8
|
+
const BLANK_HASH = b4a.alloc(32)
|
|
9
|
+
const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
|
|
10
|
+
const TREE_CACHE = 128 // speeds up linear scans by A LOT
|
|
7
11
|
|
|
8
12
|
class NodeQueue {
|
|
9
13
|
constructor (nodes, extra = null) {
|
|
@@ -56,11 +60,11 @@ class MerkleTreeBatch {
|
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
signable (hash = this.hash()) {
|
|
59
|
-
return
|
|
63
|
+
return caps.treeSignable(hash, this.length, this.fork)
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
return
|
|
66
|
+
signableLegacy (hash = this.hash()) {
|
|
67
|
+
return caps.treeSignableLegacy(hash, this.length, this.fork)
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
append (buf) {
|
|
@@ -138,6 +142,7 @@ class MerkleTreeBatch {
|
|
|
138
142
|
: this.ancestors
|
|
139
143
|
|
|
140
144
|
this.tree.truncated = true
|
|
145
|
+
this.tree.cache = new Xache({ maxSize: this.tree.cache.maxSize })
|
|
141
146
|
truncateMap(this.tree.unflushed, this.ancestors)
|
|
142
147
|
if (this.tree.flushing !== null) truncateMap(this.tree.flushing, this.ancestors)
|
|
143
148
|
}
|
|
@@ -212,9 +217,9 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
212
217
|
if (this.want === null) return true
|
|
213
218
|
|
|
214
219
|
const nodes = []
|
|
215
|
-
const root =
|
|
220
|
+
const root = verifyTree(proof, this.tree.crypto, nodes)
|
|
216
221
|
|
|
217
|
-
if (root === null || !root.hash
|
|
222
|
+
if (root === null || !b4a.equals(root.hash, this.diff.hash)) return false
|
|
218
223
|
|
|
219
224
|
this.nodes.push(...nodes)
|
|
220
225
|
return this._update(nodes)
|
|
@@ -226,13 +231,14 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
226
231
|
|
|
227
232
|
let diff = null
|
|
228
233
|
const ite = flat.iterator(this.diff.index)
|
|
234
|
+
const startingDiff = this.diff
|
|
229
235
|
|
|
230
236
|
while ((ite.index & 1) !== 0) {
|
|
231
237
|
const left = n.get(ite.leftChild())
|
|
232
238
|
if (!left) break
|
|
233
239
|
|
|
234
240
|
const existing = await this.tree.get(left.index, false)
|
|
235
|
-
if (!existing || !existing.hash
|
|
241
|
+
if (!existing || !b4a.equals(existing.hash, left.hash)) {
|
|
236
242
|
diff = left
|
|
237
243
|
} else {
|
|
238
244
|
diff = n.get(ite.sibling())
|
|
@@ -241,20 +247,19 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
241
247
|
|
|
242
248
|
if ((this.diff.index & 1) === 0) return true
|
|
243
249
|
if (diff === null) return false
|
|
250
|
+
if (startingDiff !== this.diff) return false
|
|
244
251
|
|
|
245
252
|
return this._updateDiffRoot(diff)
|
|
246
253
|
}
|
|
247
254
|
|
|
248
255
|
_updateDiffRoot (diff) {
|
|
256
|
+
if (this.want === null) return true
|
|
257
|
+
|
|
249
258
|
const spans = flat.spans(diff.index)
|
|
250
259
|
const start = spans[0] / 2
|
|
251
260
|
const end = Math.min(this.treeLength, spans[1] / 2 + 1)
|
|
252
261
|
const len = end - start
|
|
253
262
|
|
|
254
|
-
if (this.diff !== null && len >= this.want.end - this.want.start) {
|
|
255
|
-
return false
|
|
256
|
-
}
|
|
257
|
-
|
|
258
263
|
this.ancestors = start
|
|
259
264
|
this.diff = diff
|
|
260
265
|
|
|
@@ -348,13 +353,14 @@ module.exports = class MerkleTree {
|
|
|
348
353
|
|
|
349
354
|
this.storage = storage
|
|
350
355
|
this.unflushed = new Map()
|
|
356
|
+
this.cache = new Xache({ maxSize: TREE_CACHE })
|
|
351
357
|
this.flushing = null
|
|
352
358
|
this.truncated = false
|
|
353
359
|
this.truncateTo = 0
|
|
354
360
|
}
|
|
355
361
|
|
|
356
362
|
addNode (node) {
|
|
357
|
-
if (node.size === 0 && node.hash
|
|
363
|
+
if (node.size === 0 && b4a.equals(node.hash, BLANK_HASH)) node = blankNode(node.index)
|
|
358
364
|
this.unflushed.set(node.index, node)
|
|
359
365
|
}
|
|
360
366
|
|
|
@@ -371,14 +377,39 @@ module.exports = class MerkleTree {
|
|
|
371
377
|
}
|
|
372
378
|
|
|
373
379
|
signable (hash = this.hash()) {
|
|
374
|
-
return
|
|
380
|
+
return caps.treeSignable(hash, this.length, this.fork)
|
|
375
381
|
}
|
|
376
382
|
|
|
377
|
-
|
|
378
|
-
|
|
383
|
+
getRoots (length) {
|
|
384
|
+
const indexes = flat.fullRoots(2 * length)
|
|
385
|
+
const roots = new Array(indexes.length)
|
|
386
|
+
|
|
387
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
388
|
+
roots[i] = this.get(indexes[i], true)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return Promise.all(roots)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async upgradeable (length) {
|
|
395
|
+
const indexes = flat.fullRoots(2 * length)
|
|
396
|
+
const roots = new Array(indexes.length)
|
|
397
|
+
|
|
398
|
+
for (let i = 0; i < indexes.length; i++) {
|
|
399
|
+
roots[i] = this.get(indexes[i], false)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (const node of await Promise.all(roots)) {
|
|
403
|
+
if (node === null) return false
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return true
|
|
379
407
|
}
|
|
380
408
|
|
|
381
409
|
get (index, error = true) {
|
|
410
|
+
const c = this.cache.get(index)
|
|
411
|
+
if (c) return c
|
|
412
|
+
|
|
382
413
|
let node = this.unflushed.get(index)
|
|
383
414
|
|
|
384
415
|
if (this.flushing !== null && node === undefined) {
|
|
@@ -398,7 +429,7 @@ module.exports = class MerkleTree {
|
|
|
398
429
|
return Promise.resolve(node)
|
|
399
430
|
}
|
|
400
431
|
|
|
401
|
-
return getStoredNode(this.storage, index, error)
|
|
432
|
+
return getStoredNode(this.storage, index, this.cache, error)
|
|
402
433
|
}
|
|
403
434
|
|
|
404
435
|
async flush () {
|
|
@@ -440,17 +471,23 @@ module.exports = class MerkleTree {
|
|
|
440
471
|
// TODO: write neighbors together etc etc
|
|
441
472
|
// TODO: bench loading a full disk page and copy to that instead
|
|
442
473
|
return new Promise((resolve, reject) => {
|
|
443
|
-
const slab =
|
|
474
|
+
const slab = b4a.allocUnsafe(40 * this.flushing.size)
|
|
444
475
|
|
|
445
476
|
let error = null
|
|
446
477
|
let missing = this.flushing.size + 1
|
|
447
478
|
let offset = 0
|
|
448
479
|
|
|
449
480
|
for (const node of this.flushing.values()) {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
481
|
+
const state = {
|
|
482
|
+
start: 0,
|
|
483
|
+
end: 40,
|
|
484
|
+
buffer: slab.subarray(offset, offset += 40)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
c.uint64.encode(state, node.size)
|
|
488
|
+
c.raw.encode(state, node.hash)
|
|
489
|
+
|
|
490
|
+
this.storage.write(node.index * 40, state.buffer, done)
|
|
454
491
|
}
|
|
455
492
|
|
|
456
493
|
done(null)
|
|
@@ -465,6 +502,7 @@ module.exports = class MerkleTree {
|
|
|
465
502
|
}
|
|
466
503
|
|
|
467
504
|
clear () {
|
|
505
|
+
this.cache = new Xache({ maxSize: this.cache.maxSize })
|
|
468
506
|
this.truncated = true
|
|
469
507
|
this.truncateTo = 0
|
|
470
508
|
this.roots = []
|
|
@@ -517,8 +555,8 @@ module.exports = class MerkleTree {
|
|
|
517
555
|
|
|
518
556
|
let unverified = null
|
|
519
557
|
|
|
520
|
-
if (proof.block || proof.seek) {
|
|
521
|
-
unverified =
|
|
558
|
+
if (proof.block || proof.hash || proof.seek) {
|
|
559
|
+
unverified = verifyTree(proof, this.crypto, batch.nodes)
|
|
522
560
|
}
|
|
523
561
|
|
|
524
562
|
if (!verifyUpgrade(proof, unverified, batch)) {
|
|
@@ -527,7 +565,7 @@ module.exports = class MerkleTree {
|
|
|
527
565
|
|
|
528
566
|
for (const root of batch.roots) {
|
|
529
567
|
const existing = await this.get(root.index, false)
|
|
530
|
-
if (existing && existing.hash
|
|
568
|
+
if (existing && b4a.equals(existing.hash, root.hash)) continue
|
|
531
569
|
batch._updateDiffRoot(root)
|
|
532
570
|
break
|
|
533
571
|
}
|
|
@@ -545,7 +583,7 @@ module.exports = class MerkleTree {
|
|
|
545
583
|
async verify (proof) {
|
|
546
584
|
const batch = new MerkleTreeBatch(this)
|
|
547
585
|
|
|
548
|
-
let unverified =
|
|
586
|
+
let unverified = verifyTree(proof, this.crypto, batch.nodes)
|
|
549
587
|
|
|
550
588
|
if (proof.upgrade) {
|
|
551
589
|
if (verifyUpgrade(proof, unverified, batch)) {
|
|
@@ -555,7 +593,7 @@ module.exports = class MerkleTree {
|
|
|
555
593
|
|
|
556
594
|
if (unverified) {
|
|
557
595
|
const verified = await this.get(unverified.index)
|
|
558
|
-
if (!verified.hash
|
|
596
|
+
if (!b4a.equals(verified.hash, unverified.hash)) {
|
|
559
597
|
throw new Error('Invalid checksum at node ' + unverified.index)
|
|
560
598
|
}
|
|
561
599
|
}
|
|
@@ -563,89 +601,102 @@ module.exports = class MerkleTree {
|
|
|
563
601
|
return batch
|
|
564
602
|
}
|
|
565
603
|
|
|
566
|
-
async proof ({ block, seek, upgrade }) {
|
|
604
|
+
async proof ({ block, hash, seek, upgrade }) {
|
|
567
605
|
// Important that this does not throw inbetween making the promise arrays
|
|
568
606
|
// and finalise being called, otherwise there will be lingering promises in the background
|
|
569
607
|
|
|
570
|
-
const signature = this.signature
|
|
571
608
|
const fork = this.fork
|
|
609
|
+
const signature = this.signature
|
|
572
610
|
const head = 2 * this.length
|
|
573
611
|
const from = upgrade ? upgrade.start * 2 : 0
|
|
574
612
|
const to = upgrade ? from + upgrade.length * 2 : head
|
|
613
|
+
const node = normalizeIndexed(block, hash)
|
|
575
614
|
|
|
576
615
|
if (from >= to || to > head) {
|
|
577
616
|
throw new Error('Invalid upgrade')
|
|
578
617
|
}
|
|
579
|
-
if (seek &&
|
|
580
|
-
throw new Error('Cannot both do a seek and block request when upgrading')
|
|
618
|
+
if (seek && upgrade && node !== null && node.index >= from) {
|
|
619
|
+
throw new Error('Cannot both do a seek and block/hash request when upgrading')
|
|
581
620
|
}
|
|
582
621
|
|
|
583
622
|
let subTree = head
|
|
584
623
|
|
|
585
624
|
const p = {
|
|
586
|
-
|
|
625
|
+
node: null,
|
|
587
626
|
seek: null,
|
|
588
627
|
upgrade: null,
|
|
589
628
|
additionalUpgrade: null
|
|
590
629
|
}
|
|
591
630
|
|
|
592
|
-
if (
|
|
593
|
-
subTree = nodesToRoot(
|
|
631
|
+
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
632
|
+
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
594
633
|
const seekRoot = seek ? await seekUntrustedTree(this, subTree, seek.bytes) : head
|
|
595
|
-
blockAndSeekProof(this,
|
|
596
|
-
} else if ((
|
|
597
|
-
subTree = seek ? await seekFromHead(this, to, seek.bytes) :
|
|
634
|
+
blockAndSeekProof(this, node, seek, seekRoot, subTree, p)
|
|
635
|
+
} else if ((node || seek) && upgrade) {
|
|
636
|
+
subTree = seek ? await seekFromHead(this, to, seek.bytes) : node.index
|
|
598
637
|
}
|
|
599
638
|
|
|
600
639
|
if (upgrade) {
|
|
601
|
-
upgradeProof(this,
|
|
640
|
+
upgradeProof(this, node, seek, from, to, subTree, p)
|
|
602
641
|
if (head > to) additionalUpgradeProof(this, to, head, p)
|
|
603
642
|
}
|
|
604
643
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
if (block) {
|
|
609
|
-
const nodes = await Promise.all(p.block)
|
|
644
|
+
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(p)
|
|
645
|
+
const result = { fork, block: null, hash: null, seek: null, upgrade: null }
|
|
610
646
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
647
|
+
if (block) {
|
|
648
|
+
result.block = {
|
|
649
|
+
index: block.index,
|
|
650
|
+
value: null, // populated upstream, alloc it here for simplicity
|
|
651
|
+
nodes: pNode
|
|
652
|
+
}
|
|
653
|
+
} else if (hash) {
|
|
654
|
+
result.hash = {
|
|
655
|
+
index: hash.index,
|
|
656
|
+
nodes: pNode
|
|
616
657
|
}
|
|
617
|
-
|
|
618
|
-
const nodes = await Promise.all(p.seek)
|
|
658
|
+
}
|
|
619
659
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
660
|
+
if (seek && pSeek !== null) {
|
|
661
|
+
result.seek = {
|
|
662
|
+
bytes: seek.bytes,
|
|
663
|
+
nodes: pSeek
|
|
624
664
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
signature
|
|
635
|
-
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (upgrade) {
|
|
668
|
+
result.upgrade = {
|
|
669
|
+
start: upgrade.start,
|
|
670
|
+
length: upgrade.length,
|
|
671
|
+
nodes: pUpgrade,
|
|
672
|
+
additionalNodes: pAdditional || [],
|
|
673
|
+
signature
|
|
636
674
|
}
|
|
675
|
+
}
|
|
637
676
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
677
|
+
return result
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Successor to .nodes()
|
|
681
|
+
async missingNodes (index) {
|
|
682
|
+
const head = 2 * this.length
|
|
683
|
+
const ite = flat.iterator(index)
|
|
684
|
+
|
|
685
|
+
// See iterator.rightSpan()
|
|
686
|
+
const iteRightSpan = ite.index + ite.factor / 2 - 1
|
|
687
|
+
// If the index is not in the current tree, we do not know how many missing nodes there are...
|
|
688
|
+
if (iteRightSpan >= head) return 0
|
|
689
|
+
|
|
690
|
+
let cnt = 0
|
|
691
|
+
while (!ite.contains(head) && (await this.get(ite.index, false)) === null) {
|
|
692
|
+
cnt++
|
|
693
|
+
ite.parent()
|
|
646
694
|
}
|
|
695
|
+
|
|
696
|
+
return cnt
|
|
647
697
|
}
|
|
648
698
|
|
|
699
|
+
// Deprecated
|
|
649
700
|
async nodes (index) {
|
|
650
701
|
const head = 2 * this.length
|
|
651
702
|
const ite = flat.iterator(index)
|
|
@@ -700,7 +751,7 @@ module.exports = class MerkleTree {
|
|
|
700
751
|
await new Promise((resolve, reject) => {
|
|
701
752
|
storage.read(0, OLD_TREE.length, (err, buf) => {
|
|
702
753
|
if (err) return resolve()
|
|
703
|
-
if (
|
|
754
|
+
if (b4a.equals(buf, OLD_TREE)) return reject(new Error('Storage contains an incompatible merkle tree'))
|
|
704
755
|
resolve()
|
|
705
756
|
})
|
|
706
757
|
})
|
|
@@ -711,7 +762,7 @@ module.exports = class MerkleTree {
|
|
|
711
762
|
|
|
712
763
|
const roots = []
|
|
713
764
|
for (const index of flat.fullRoots(2 * length)) {
|
|
714
|
-
roots.push(await getStoredNode(storage, index, true))
|
|
765
|
+
roots.push(await getStoredNode(storage, index, null, true))
|
|
715
766
|
}
|
|
716
767
|
|
|
717
768
|
return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null)
|
|
@@ -720,8 +771,14 @@ module.exports = class MerkleTree {
|
|
|
720
771
|
|
|
721
772
|
// All the methods needed for proof verification
|
|
722
773
|
|
|
723
|
-
function
|
|
724
|
-
|
|
774
|
+
function verifyTree ({ block, hash, seek }, crypto, nodes) {
|
|
775
|
+
const untrustedNode = block
|
|
776
|
+
? { index: 2 * block.index, value: block.value, nodes: block.nodes }
|
|
777
|
+
: hash
|
|
778
|
+
? { index: hash.index, value: null, nodes: hash.nodes }
|
|
779
|
+
: null
|
|
780
|
+
|
|
781
|
+
if (untrustedNode === null && (!seek || !seek.nodes.length)) return null
|
|
725
782
|
|
|
726
783
|
let root = null
|
|
727
784
|
|
|
@@ -741,12 +798,12 @@ function verifyBlock ({ block, seek }, crypto, nodes) {
|
|
|
741
798
|
}
|
|
742
799
|
}
|
|
743
800
|
|
|
744
|
-
if (
|
|
801
|
+
if (untrustedNode === null) return root
|
|
745
802
|
|
|
746
|
-
const ite = flat.iterator(
|
|
747
|
-
const blockHash =
|
|
803
|
+
const ite = flat.iterator(untrustedNode.index)
|
|
804
|
+
const blockHash = untrustedNode.value && blockNode(crypto, ite.index, untrustedNode.value)
|
|
748
805
|
|
|
749
|
-
const q = new NodeQueue(
|
|
806
|
+
const q = new NodeQueue(untrustedNode.nodes, root)
|
|
750
807
|
|
|
751
808
|
root = blockHash || q.shift(ite.index)
|
|
752
809
|
nodes.push(root)
|
|
@@ -895,13 +952,13 @@ function seekProof (tree, seekRoot, root, p) {
|
|
|
895
952
|
}
|
|
896
953
|
}
|
|
897
954
|
|
|
898
|
-
function blockAndSeekProof (tree,
|
|
899
|
-
if (!
|
|
955
|
+
function blockAndSeekProof (tree, node, seek, seekRoot, root, p) {
|
|
956
|
+
if (!node) return seekProof(tree, seekRoot, root, p)
|
|
900
957
|
|
|
901
|
-
const ite = flat.iterator(
|
|
958
|
+
const ite = flat.iterator(node.index)
|
|
902
959
|
|
|
903
|
-
p.
|
|
904
|
-
if (!
|
|
960
|
+
p.node = []
|
|
961
|
+
if (!node.value) p.node.push(tree.get(ite.index))
|
|
905
962
|
|
|
906
963
|
while (ite.index !== root) {
|
|
907
964
|
ite.sibling()
|
|
@@ -909,14 +966,14 @@ function blockAndSeekProof (tree, block, seek, seekRoot, root, p) {
|
|
|
909
966
|
if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {
|
|
910
967
|
seekProof(tree, seekRoot, ite.index, p)
|
|
911
968
|
} else {
|
|
912
|
-
p.
|
|
969
|
+
p.node.push(tree.get(ite.index))
|
|
913
970
|
}
|
|
914
971
|
|
|
915
972
|
ite.parent()
|
|
916
973
|
}
|
|
917
974
|
}
|
|
918
975
|
|
|
919
|
-
function upgradeProof (tree,
|
|
976
|
+
function upgradeProof (tree, node, seek, from, to, subTree, p) {
|
|
920
977
|
if (from === 0) p.upgrade = []
|
|
921
978
|
|
|
922
979
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -935,8 +992,8 @@ function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
|
935
992
|
while (ite.index !== root) {
|
|
936
993
|
ite.sibling()
|
|
937
994
|
if (ite.index > target) {
|
|
938
|
-
if (p.
|
|
939
|
-
blockAndSeekProof(tree,
|
|
995
|
+
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
996
|
+
blockAndSeekProof(tree, node, seek, subTree, ite.index, p)
|
|
940
997
|
} else {
|
|
941
998
|
p.upgrade.push(tree.get(ite.index))
|
|
942
999
|
}
|
|
@@ -953,8 +1010,8 @@ function upgradeProof (tree, block, seek, from, to, subTree, p) {
|
|
|
953
1010
|
|
|
954
1011
|
// if the subtree included is a child of this tree, include that one
|
|
955
1012
|
// instead of a dup node
|
|
956
|
-
if (p.
|
|
957
|
-
blockAndSeekProof(tree,
|
|
1013
|
+
if (p.node === null && p.seek === null && ite.contains(subTree)) {
|
|
1014
|
+
blockAndSeekProof(tree, node, seek, subTree, ite.index, p)
|
|
958
1015
|
continue
|
|
959
1016
|
}
|
|
960
1017
|
|
|
@@ -1036,7 +1093,7 @@ function blankNode (index) {
|
|
|
1036
1093
|
|
|
1037
1094
|
// Storage methods
|
|
1038
1095
|
|
|
1039
|
-
function getStoredNode (storage, index, error) {
|
|
1096
|
+
function getStoredNode (storage, index, cache, error) {
|
|
1040
1097
|
return new Promise((resolve, reject) => {
|
|
1041
1098
|
storage.read(40 * index, 40, (err, data) => {
|
|
1042
1099
|
if (err) {
|
|
@@ -1045,16 +1102,18 @@ function getStoredNode (storage, index, error) {
|
|
|
1045
1102
|
return
|
|
1046
1103
|
}
|
|
1047
1104
|
|
|
1048
|
-
const hash = data.
|
|
1049
|
-
const size =
|
|
1105
|
+
const hash = data.subarray(8)
|
|
1106
|
+
const size = c.decode(c.uint64, data)
|
|
1050
1107
|
|
|
1051
|
-
if (size === 0 &&
|
|
1108
|
+
if (size === 0 && b4a.compare(hash, BLANK_HASH) === 0) {
|
|
1052
1109
|
if (error) reject(new Error('Could not load node: ' + index))
|
|
1053
1110
|
else resolve(null)
|
|
1054
1111
|
return
|
|
1055
1112
|
}
|
|
1056
1113
|
|
|
1057
|
-
|
|
1114
|
+
const node = { index, size, hash }
|
|
1115
|
+
if (cache !== null) cache.set(index, node)
|
|
1116
|
+
resolve(node)
|
|
1058
1117
|
})
|
|
1059
1118
|
})
|
|
1060
1119
|
}
|
|
@@ -1073,7 +1132,7 @@ async function autoLength (storage) {
|
|
|
1073
1132
|
if (!nodes) return 0
|
|
1074
1133
|
const ite = flat.iterator(nodes - 1)
|
|
1075
1134
|
let index = nodes - 1
|
|
1076
|
-
while (await getStoredNode(storage, ite.parent(), false)) index = ite.index
|
|
1135
|
+
while (await getStoredNode(storage, ite.parent(), null, false)) index = ite.index
|
|
1077
1136
|
return flat.rightSpan(index) / 2 + 1
|
|
1078
1137
|
}
|
|
1079
1138
|
|
|
@@ -1094,10 +1153,27 @@ function log2 (n) {
|
|
|
1094
1153
|
return res
|
|
1095
1154
|
}
|
|
1096
1155
|
|
|
1097
|
-
function
|
|
1098
|
-
|
|
1099
|
-
hash.
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1156
|
+
function normalizeIndexed (block, hash) {
|
|
1157
|
+
if (block) return { value: true, index: block.index * 2, nodes: block.nodes, lastIndex: block.index }
|
|
1158
|
+
if (hash) return { value: false, index: hash.index, nodes: hash.nodes, lastIndex: flat.rightSpan(hash.index) / 2 }
|
|
1159
|
+
return null
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
async function settleProof (p) {
|
|
1163
|
+
const result = [
|
|
1164
|
+
p.node && Promise.all(p.node),
|
|
1165
|
+
p.seek && Promise.all(p.seek),
|
|
1166
|
+
p.upgrade && Promise.all(p.upgrade),
|
|
1167
|
+
p.additionalUpgrade && Promise.all(p.additionalUpgrade)
|
|
1168
|
+
]
|
|
1169
|
+
|
|
1170
|
+
try {
|
|
1171
|
+
return await Promise.all(result)
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
if (p.node) await Promise.allSettled(p.node)
|
|
1174
|
+
if (p.seek) await Promise.allSettled(p.seek)
|
|
1175
|
+
if (p.upgrade) await Promise.allSettled(p.upgrade)
|
|
1176
|
+
if (p.additionalUpgrade) await Promise.allSettled(p.additionalUpgrade)
|
|
1177
|
+
throw err
|
|
1178
|
+
}
|
|
1103
1179
|
}
|