hypercore 11.16.2 → 11.18.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 +31 -29
- package/index.js +153 -155
- package/lib/audit.js +17 -6
- package/lib/bit-interlude.js +17 -7
- package/lib/bitfield.js +72 -52
- package/lib/caps.js +5 -1
- package/lib/copy-prologue.js +14 -10
- package/lib/core.js +109 -56
- package/lib/default-encryption.js +14 -28
- package/lib/download.js +10 -10
- package/lib/fully-remote-proof.js +3 -3
- package/lib/hotswap-queue.js +5 -5
- package/lib/info.js +4 -4
- package/lib/inspect.js +50 -0
- package/lib/merkle-tree.js +143 -104
- package/lib/messages.js +163 -143
- package/lib/multisig.js +19 -12
- package/lib/mutex.js +9 -7
- package/lib/receiver-queue.js +6 -6
- package/lib/remote-bitfield.js +30 -32
- package/lib/replicator.js +383 -265
- package/lib/session-state.js +112 -75
- package/lib/streams.js +16 -16
- package/lib/verifier.js +69 -43
- package/package.json +5 -3
package/lib/merkle-tree.js
CHANGED
|
@@ -3,17 +3,23 @@ const crypto = require('hypercore-crypto')
|
|
|
3
3
|
const b4a = require('b4a')
|
|
4
4
|
const unslab = require('unslab')
|
|
5
5
|
const caps = require('./caps')
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
INVALID_PROOF,
|
|
8
|
+
INVALID_CHECKSUM,
|
|
9
|
+
INVALID_OPERATION,
|
|
10
|
+
BAD_ARGUMENT,
|
|
11
|
+
ASSERTION
|
|
12
|
+
} = require('hypercore-errors')
|
|
7
13
|
|
|
8
14
|
class NodeQueue {
|
|
9
|
-
constructor
|
|
15
|
+
constructor(nodes, extra = null) {
|
|
10
16
|
this.i = 0
|
|
11
17
|
this.nodes = nodes
|
|
12
18
|
this.extra = extra
|
|
13
19
|
this.length = nodes.length + (this.extra === null ? 0 : 1)
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
shift
|
|
22
|
+
shift(index) {
|
|
17
23
|
if (this.extra !== null && this.extra.index === index) {
|
|
18
24
|
const node = this.extra
|
|
19
25
|
this.extra = null
|
|
@@ -36,7 +42,7 @@ class NodeQueue {
|
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
class MerkleTreeBatch {
|
|
39
|
-
constructor
|
|
45
|
+
constructor(session) {
|
|
40
46
|
this.fork = session.fork
|
|
41
47
|
this.roots = [...session.roots]
|
|
42
48
|
this.length = session.length
|
|
@@ -56,7 +62,7 @@ class MerkleTreeBatch {
|
|
|
56
62
|
this.upgraded = false
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
checkout
|
|
65
|
+
checkout(length, additionalRoots) {
|
|
60
66
|
const roots = []
|
|
61
67
|
let r = 0
|
|
62
68
|
|
|
@@ -98,7 +104,7 @@ class MerkleTreeBatch {
|
|
|
98
104
|
}
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
prune
|
|
107
|
+
prune(length) {
|
|
102
108
|
if (length === 0) return
|
|
103
109
|
|
|
104
110
|
const head = 2 * length - 2
|
|
@@ -118,7 +124,7 @@ class MerkleTreeBatch {
|
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
clone
|
|
127
|
+
clone() {
|
|
122
128
|
const b = new MerkleTreeBatch(this.session)
|
|
123
129
|
|
|
124
130
|
b.fork = this.fork
|
|
@@ -135,20 +141,20 @@ class MerkleTreeBatch {
|
|
|
135
141
|
return b
|
|
136
142
|
}
|
|
137
143
|
|
|
138
|
-
hash
|
|
144
|
+
hash() {
|
|
139
145
|
if (this.hashCached === null) this.hashCached = unslab(crypto.tree(this.roots))
|
|
140
146
|
return this.hashCached
|
|
141
147
|
}
|
|
142
148
|
|
|
143
|
-
signable
|
|
149
|
+
signable(manifestHash) {
|
|
144
150
|
return caps.treeSignable(manifestHash, this.hash(), this.length, this.fork)
|
|
145
151
|
}
|
|
146
152
|
|
|
147
|
-
signableCompat
|
|
153
|
+
signableCompat(noHeader) {
|
|
148
154
|
return caps.treeSignableCompat(this.hash(), this.length, this.fork, noHeader)
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
get
|
|
157
|
+
get(index) {
|
|
152
158
|
if (index >= this.length * 2) {
|
|
153
159
|
return null
|
|
154
160
|
}
|
|
@@ -161,11 +167,11 @@ class MerkleTreeBatch {
|
|
|
161
167
|
}
|
|
162
168
|
|
|
163
169
|
// deprecated, use sssion proof instead
|
|
164
|
-
proof
|
|
170
|
+
proof(batch, { block, hash, seek, upgrade }) {
|
|
165
171
|
return generateProof(this.session, batch, block, hash, seek, upgrade)
|
|
166
172
|
}
|
|
167
173
|
|
|
168
|
-
verifyUpgrade
|
|
174
|
+
verifyUpgrade(proof) {
|
|
169
175
|
const unverified = verifyTree(proof, this.nodes)
|
|
170
176
|
|
|
171
177
|
if (!proof.upgrade) throw INVALID_OPERATION('Expected upgrade proof')
|
|
@@ -173,13 +179,13 @@ class MerkleTreeBatch {
|
|
|
173
179
|
return verifyUpgrade(proof, unverified, this)
|
|
174
180
|
}
|
|
175
181
|
|
|
176
|
-
addNodesUnsafe
|
|
182
|
+
addNodesUnsafe(nodes) {
|
|
177
183
|
for (let i = 0; i < nodes.length; i++) {
|
|
178
184
|
this.nodes.push(nodes[i])
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
append
|
|
188
|
+
append(buf) {
|
|
183
189
|
const head = this.length * 2
|
|
184
190
|
const ite = flat.iterator(head)
|
|
185
191
|
const node = blockNode(head, buf)
|
|
@@ -187,7 +193,7 @@ class MerkleTreeBatch {
|
|
|
187
193
|
this.appendRoot(node, ite)
|
|
188
194
|
}
|
|
189
195
|
|
|
190
|
-
appendRoot
|
|
196
|
+
appendRoot(node, ite) {
|
|
191
197
|
node = unslabNode(node)
|
|
192
198
|
this.hashCached = null
|
|
193
199
|
this.upgraded = true
|
|
@@ -214,17 +220,19 @@ class MerkleTreeBatch {
|
|
|
214
220
|
}
|
|
215
221
|
}
|
|
216
222
|
|
|
217
|
-
commitable
|
|
218
|
-
return
|
|
219
|
-
this.
|
|
223
|
+
commitable() {
|
|
224
|
+
return (
|
|
225
|
+
this.treeFork === this.session.fork &&
|
|
226
|
+
(this.upgraded
|
|
220
227
|
? this.treeLength === this.session.length
|
|
221
|
-
: this.treeLength <= this.session.length
|
|
228
|
+
: this.treeLength <= this.session.length)
|
|
222
229
|
)
|
|
223
230
|
}
|
|
224
231
|
|
|
225
|
-
commit
|
|
232
|
+
commit(tx) {
|
|
226
233
|
if (tx === undefined) throw INVALID_OPERATION('No database batch was passed')
|
|
227
|
-
if (!this.commitable())
|
|
234
|
+
if (!this.commitable())
|
|
235
|
+
throw INVALID_OPERATION('Tree was modified during batch, refusing to commit')
|
|
228
236
|
|
|
229
237
|
if (this.upgraded) this._commitUpgrade(tx)
|
|
230
238
|
|
|
@@ -238,7 +246,7 @@ class MerkleTreeBatch {
|
|
|
238
246
|
return this
|
|
239
247
|
}
|
|
240
248
|
|
|
241
|
-
_commitUpgrade
|
|
249
|
+
_commitUpgrade(tx) {
|
|
242
250
|
// TODO: If easy to detect, we should refuse an trunc+append here without a fork id
|
|
243
251
|
// change. Will only happen on user error so mostly to prevent that.
|
|
244
252
|
|
|
@@ -262,11 +270,11 @@ class MerkleTreeBatch {
|
|
|
262
270
|
}
|
|
263
271
|
}
|
|
264
272
|
|
|
265
|
-
seek
|
|
273
|
+
seek(bytes, padding) {
|
|
266
274
|
return new ByteSeeker(this, this, bytes, padding)
|
|
267
275
|
}
|
|
268
276
|
|
|
269
|
-
byteRange
|
|
277
|
+
byteRange(index) {
|
|
270
278
|
const rx = this.storage.read()
|
|
271
279
|
const range = getByteRange(this, index, rx)
|
|
272
280
|
rx.tryFlush()
|
|
@@ -274,7 +282,7 @@ class MerkleTreeBatch {
|
|
|
274
282
|
return range
|
|
275
283
|
}
|
|
276
284
|
|
|
277
|
-
byteOffset
|
|
285
|
+
byteOffset(index) {
|
|
278
286
|
if (index === 2 * this.length) return this.byteLength
|
|
279
287
|
|
|
280
288
|
const rx = this.storage.read()
|
|
@@ -284,7 +292,7 @@ class MerkleTreeBatch {
|
|
|
284
292
|
return offset
|
|
285
293
|
}
|
|
286
294
|
|
|
287
|
-
async restore
|
|
295
|
+
async restore(length) {
|
|
288
296
|
if (length === this.length) return this
|
|
289
297
|
|
|
290
298
|
const roots = unslabNodes(await MerkleTree.getRootsFromStorage(this.storage, length))
|
|
@@ -301,7 +309,7 @@ class MerkleTreeBatch {
|
|
|
301
309
|
}
|
|
302
310
|
|
|
303
311
|
class ReorgBatch extends MerkleTreeBatch {
|
|
304
|
-
constructor
|
|
312
|
+
constructor(session) {
|
|
305
313
|
super(session)
|
|
306
314
|
|
|
307
315
|
this.roots = []
|
|
@@ -319,11 +327,11 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
319
327
|
}
|
|
320
328
|
}
|
|
321
329
|
|
|
322
|
-
get finished
|
|
330
|
+
get finished() {
|
|
323
331
|
return this.want === null
|
|
324
332
|
}
|
|
325
333
|
|
|
326
|
-
update
|
|
334
|
+
update(proof) {
|
|
327
335
|
if (this.want === null) return true
|
|
328
336
|
|
|
329
337
|
const nodes = []
|
|
@@ -335,7 +343,7 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
335
343
|
return this._update(nodes)
|
|
336
344
|
}
|
|
337
345
|
|
|
338
|
-
async _update
|
|
346
|
+
async _update(nodes) {
|
|
339
347
|
const n = new Map()
|
|
340
348
|
for (const node of nodes) n.set(node.index, node)
|
|
341
349
|
|
|
@@ -362,7 +370,7 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
362
370
|
return this._updateDiffRoot(diff)
|
|
363
371
|
}
|
|
364
372
|
|
|
365
|
-
_updateDiffRoot
|
|
373
|
+
_updateDiffRoot(diff) {
|
|
366
374
|
if (this.want === null) return true
|
|
367
375
|
|
|
368
376
|
const spans = flat.spans(diff.index)
|
|
@@ -387,21 +395,22 @@ class ReorgBatch extends MerkleTreeBatch {
|
|
|
387
395
|
}
|
|
388
396
|
|
|
389
397
|
class ByteSeeker {
|
|
390
|
-
constructor
|
|
398
|
+
constructor(session, bytes, padding = 0) {
|
|
391
399
|
this.session = session
|
|
392
400
|
this.bytes = bytes
|
|
393
401
|
this.padding = padding
|
|
394
402
|
|
|
395
|
-
const size = session.byteLength -
|
|
403
|
+
const size = session.byteLength - session.length * padding
|
|
396
404
|
|
|
397
405
|
this.start = bytes >= size ? session.length : 0
|
|
398
406
|
this.end = bytes < size ? session.length : 0
|
|
399
407
|
}
|
|
400
408
|
|
|
401
|
-
async _seek
|
|
409
|
+
async _seek(bytes) {
|
|
402
410
|
if (!bytes) return [0, 0]
|
|
403
411
|
|
|
404
|
-
for (const node of this.session.roots) {
|
|
412
|
+
for (const node of this.session.roots) {
|
|
413
|
+
// all async ticks happen once we find the root so safe
|
|
405
414
|
const size = getUnpaddedSize(node, this.padding, null)
|
|
406
415
|
|
|
407
416
|
if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]
|
|
@@ -434,7 +443,8 @@ class ByteSeeker {
|
|
|
434
443
|
return null
|
|
435
444
|
}
|
|
436
445
|
|
|
437
|
-
async update
|
|
446
|
+
async update() {
|
|
447
|
+
// TODO: combine _seek and this, much simpler
|
|
438
448
|
const res = await this._seek(this.bytes)
|
|
439
449
|
if (!res) return null
|
|
440
450
|
if ((res[0] & 1) === 0) return [res[0] / 2, res[1]]
|
|
@@ -448,7 +458,7 @@ class ByteSeeker {
|
|
|
448
458
|
}
|
|
449
459
|
|
|
450
460
|
class TreeProof {
|
|
451
|
-
constructor
|
|
461
|
+
constructor(session, block, hash, seek, upgrade) {
|
|
452
462
|
this.fork = session.fork
|
|
453
463
|
this.signature = session.signature
|
|
454
464
|
|
|
@@ -465,8 +475,15 @@ class TreeProof {
|
|
|
465
475
|
}
|
|
466
476
|
}
|
|
467
477
|
|
|
468
|
-
async settle
|
|
469
|
-
const result = {
|
|
478
|
+
async settle() {
|
|
479
|
+
const result = {
|
|
480
|
+
fork: this.fork,
|
|
481
|
+
block: null,
|
|
482
|
+
hash: null,
|
|
483
|
+
seek: null,
|
|
484
|
+
upgrade: null,
|
|
485
|
+
manifest: null
|
|
486
|
+
}
|
|
470
487
|
|
|
471
488
|
const [pNode, pSeek, pUpgrade, pAdditional] = await settleProof(this.pending)
|
|
472
489
|
|
|
@@ -507,27 +524,27 @@ class TreeProof {
|
|
|
507
524
|
}
|
|
508
525
|
|
|
509
526
|
class MerkleTree {
|
|
510
|
-
static hash
|
|
527
|
+
static hash(s) {
|
|
511
528
|
return unslab(crypto.tree(s.roots))
|
|
512
529
|
}
|
|
513
530
|
|
|
514
|
-
static signable
|
|
531
|
+
static signable(s, namespace) {
|
|
515
532
|
return caps.treeSignable(namespace, MerkleTree.hash(s), s.length, s.fork)
|
|
516
533
|
}
|
|
517
534
|
|
|
518
|
-
static size
|
|
535
|
+
static size(roots) {
|
|
519
536
|
return totalSize(roots)
|
|
520
537
|
}
|
|
521
538
|
|
|
522
|
-
static span
|
|
539
|
+
static span(roots) {
|
|
523
540
|
return totalSpan(roots)
|
|
524
541
|
}
|
|
525
542
|
|
|
526
|
-
static getRoots
|
|
543
|
+
static getRoots(session, length) {
|
|
527
544
|
return MerkleTree.getRootsFromStorage(session.storage, length)
|
|
528
545
|
}
|
|
529
546
|
|
|
530
|
-
static getRootsFromStorage
|
|
547
|
+
static getRootsFromStorage(storage, length) {
|
|
531
548
|
const indexes = flat.fullRoots(2 * length)
|
|
532
549
|
const roots = new Array(indexes.length)
|
|
533
550
|
const rx = storage.read()
|
|
@@ -541,7 +558,7 @@ class MerkleTree {
|
|
|
541
558
|
return Promise.all(roots)
|
|
542
559
|
}
|
|
543
560
|
|
|
544
|
-
static async upgradeable
|
|
561
|
+
static async upgradeable(session, length) {
|
|
545
562
|
const indexes = flat.fullRoots(2 * length)
|
|
546
563
|
const roots = new Array(indexes.length)
|
|
547
564
|
const rx = session.storage.read()
|
|
@@ -559,15 +576,15 @@ class MerkleTree {
|
|
|
559
576
|
return true
|
|
560
577
|
}
|
|
561
578
|
|
|
562
|
-
static seek
|
|
579
|
+
static seek(session, bytes, padding) {
|
|
563
580
|
return new ByteSeeker(session, bytes, padding)
|
|
564
581
|
}
|
|
565
582
|
|
|
566
|
-
static get
|
|
583
|
+
static get(session, index) {
|
|
567
584
|
return getTreeNodeFromStorage(session.storage, index)
|
|
568
585
|
}
|
|
569
586
|
|
|
570
|
-
static async truncate
|
|
587
|
+
static async truncate(session, length, batch, fork = batch.fork) {
|
|
571
588
|
const head = length * 2
|
|
572
589
|
const fullRoots = flat.fullRoots(head)
|
|
573
590
|
|
|
@@ -592,7 +609,7 @@ class MerkleTree {
|
|
|
592
609
|
return batch
|
|
593
610
|
}
|
|
594
611
|
|
|
595
|
-
static async reorg
|
|
612
|
+
static async reorg(session, proof, batch) {
|
|
596
613
|
let unverified = null
|
|
597
614
|
|
|
598
615
|
if (proof.block || proof.hash || proof.seek) {
|
|
@@ -620,7 +637,7 @@ class MerkleTree {
|
|
|
620
637
|
return batch
|
|
621
638
|
}
|
|
622
639
|
|
|
623
|
-
static verifyFullyRemote
|
|
640
|
+
static verifyFullyRemote(session, proof) {
|
|
624
641
|
// TODO: impl this less hackishly
|
|
625
642
|
const batch = new MerkleTreeBatch(session)
|
|
626
643
|
|
|
@@ -641,7 +658,7 @@ class MerkleTree {
|
|
|
641
658
|
return batch
|
|
642
659
|
}
|
|
643
660
|
|
|
644
|
-
static async verify
|
|
661
|
+
static async verify(session, proof) {
|
|
645
662
|
const batch = new MerkleTreeBatch(session)
|
|
646
663
|
|
|
647
664
|
let unverified = verifyTree(proof, batch.nodes)
|
|
@@ -662,11 +679,11 @@ class MerkleTree {
|
|
|
662
679
|
return batch
|
|
663
680
|
}
|
|
664
681
|
|
|
665
|
-
static proof
|
|
682
|
+
static proof(session, rx, { block, hash, seek, upgrade }) {
|
|
666
683
|
return generateProof(session, rx, block, hash, seek, upgrade)
|
|
667
684
|
}
|
|
668
685
|
|
|
669
|
-
static async missingNodes
|
|
686
|
+
static async missingNodes(session, index, length) {
|
|
670
687
|
const head = 2 * length
|
|
671
688
|
const ite = flat.iterator(index)
|
|
672
689
|
|
|
@@ -679,18 +696,19 @@ class MerkleTree {
|
|
|
679
696
|
// TODO: we could prop use a read batch here and do this in blocks of X for perf
|
|
680
697
|
while (!ite.contains(head) && !(await hasTreeNode(session.storage, ite.index))) {
|
|
681
698
|
cnt++
|
|
682
|
-
if (cnt >= 1024)
|
|
699
|
+
if (cnt >= 1024)
|
|
700
|
+
throw ASSERTION('Bad arguments to missingNodes index=' + index + ' at length=' + length)
|
|
683
701
|
ite.parent()
|
|
684
702
|
}
|
|
685
703
|
|
|
686
704
|
return cnt
|
|
687
705
|
}
|
|
688
706
|
|
|
689
|
-
static byteOffset
|
|
707
|
+
static byteOffset(session, index) {
|
|
690
708
|
return getByteOffsetSession(session, index, null)
|
|
691
709
|
}
|
|
692
710
|
|
|
693
|
-
static byteRange
|
|
711
|
+
static byteRange(session, index) {
|
|
694
712
|
const rx = session.storage.read()
|
|
695
713
|
const offset = getByteOffsetSession(session, index, rx)
|
|
696
714
|
const size = getNodeSize(index, rx)
|
|
@@ -705,16 +723,17 @@ module.exports = {
|
|
|
705
723
|
MerkleTree
|
|
706
724
|
}
|
|
707
725
|
|
|
708
|
-
async function getNodeSize
|
|
726
|
+
async function getNodeSize(index, rx) {
|
|
709
727
|
return (await getTreeNodeOrError(rx, index)).size
|
|
710
728
|
}
|
|
711
729
|
|
|
712
|
-
async function getByteOffsetSession
|
|
730
|
+
async function getByteOffsetSession(session, index, rx) {
|
|
713
731
|
if (index === 2 * session.length) return session.byteLength
|
|
714
732
|
|
|
715
|
-
const treeNodes =
|
|
716
|
-
|
|
717
|
-
|
|
733
|
+
const treeNodes =
|
|
734
|
+
rx === null
|
|
735
|
+
? await getByteOffsetBatchFlush(session.roots, index, session.storage.read())
|
|
736
|
+
: await getByteOffsetBatch(session.roots, index, rx)
|
|
718
737
|
|
|
719
738
|
let offset = 0
|
|
720
739
|
for (const node of treeNodes) offset += node.size
|
|
@@ -722,7 +741,7 @@ async function getByteOffsetSession (session, index, rx) {
|
|
|
722
741
|
return offset
|
|
723
742
|
}
|
|
724
743
|
|
|
725
|
-
async function getByteOffset
|
|
744
|
+
async function getByteOffset(tree, index, rx) {
|
|
726
745
|
if (index === 2 * tree.length) return tree.byteLength
|
|
727
746
|
|
|
728
747
|
const treeNodes = await getByteOffsetBatch(tree.roots, index, rx)
|
|
@@ -733,13 +752,13 @@ async function getByteOffset (tree, index, rx) {
|
|
|
733
752
|
return offset
|
|
734
753
|
}
|
|
735
754
|
|
|
736
|
-
function getByteOffsetBatchFlush
|
|
755
|
+
function getByteOffsetBatchFlush(roots, index, rx) {
|
|
737
756
|
const treeNodes = getByteOffsetBatch(roots, index, rx)
|
|
738
757
|
rx.tryFlush()
|
|
739
758
|
return treeNodes
|
|
740
759
|
}
|
|
741
760
|
|
|
742
|
-
function getByteOffsetBatch
|
|
761
|
+
function getByteOffsetBatch(roots, index, rx) {
|
|
743
762
|
if ((index & 1) === 1) index = flat.leftSpan(index)
|
|
744
763
|
|
|
745
764
|
let head = 0
|
|
@@ -747,8 +766,9 @@ function getByteOffsetBatch (roots, index, rx) {
|
|
|
747
766
|
|
|
748
767
|
const promises = []
|
|
749
768
|
|
|
750
|
-
for (const node of roots) {
|
|
751
|
-
|
|
769
|
+
for (const node of roots) {
|
|
770
|
+
// all async ticks happen once we find the root so safe
|
|
771
|
+
head += 2 * (node.index - head + 1)
|
|
752
772
|
|
|
753
773
|
if (index >= head) {
|
|
754
774
|
promises.push(node.size)
|
|
@@ -774,7 +794,7 @@ function getByteOffsetBatch (roots, index, rx) {
|
|
|
774
794
|
throw ASSERTION('Failed to find offset')
|
|
775
795
|
}
|
|
776
796
|
|
|
777
|
-
function getByteRange
|
|
797
|
+
function getByteRange(tree, index, rx) {
|
|
778
798
|
const head = 2 * tree.length
|
|
779
799
|
if (((index & 1) === 0 ? index : flat.rightSpan(index)) >= head) {
|
|
780
800
|
throw BAD_ARGUMENT('Index is out of bounds')
|
|
@@ -788,7 +808,7 @@ function getByteRange (tree, index, rx) {
|
|
|
788
808
|
|
|
789
809
|
// All the methods needed for proof verification
|
|
790
810
|
|
|
791
|
-
function verifyTree
|
|
811
|
+
function verifyTree({ block, hash, seek }, nodes) {
|
|
792
812
|
const untrustedNode = block
|
|
793
813
|
? { index: 2 * block.index, value: block.value, nodes: block.nodes }
|
|
794
814
|
: hash
|
|
@@ -836,7 +856,7 @@ function verifyTree ({ block, hash, seek }, nodes) {
|
|
|
836
856
|
return root
|
|
837
857
|
}
|
|
838
858
|
|
|
839
|
-
function verifyUpgrade
|
|
859
|
+
function verifyUpgrade({ fork, upgrade }, blockRoot, batch) {
|
|
840
860
|
const prologue = batch.prologue
|
|
841
861
|
|
|
842
862
|
if (prologue) {
|
|
@@ -908,7 +928,7 @@ function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
|
|
|
908
928
|
return q.extra === null
|
|
909
929
|
}
|
|
910
930
|
|
|
911
|
-
async function seekFromHead
|
|
931
|
+
async function seekFromHead(session, head, bytes, padding) {
|
|
912
932
|
const roots = flat.fullRoots(head)
|
|
913
933
|
|
|
914
934
|
for (let i = 0; i < roots.length; i++) {
|
|
@@ -930,7 +950,7 @@ async function seekFromHead (session, head, bytes, padding) {
|
|
|
930
950
|
|
|
931
951
|
// trust that bytes are within the root tree and find the block at bytes
|
|
932
952
|
|
|
933
|
-
async function seekTrustedTree
|
|
953
|
+
async function seekTrustedTree(session, root, bytes, padding) {
|
|
934
954
|
if (!bytes) return root
|
|
935
955
|
|
|
936
956
|
const ite = flat.iterator(root)
|
|
@@ -957,8 +977,10 @@ async function seekTrustedTree (session, root, bytes, padding) {
|
|
|
957
977
|
|
|
958
978
|
// try to find the block at bytes without trusting that is *is* within the root passed
|
|
959
979
|
|
|
960
|
-
async function seekUntrustedTree
|
|
961
|
-
const offset =
|
|
980
|
+
async function seekUntrustedTree(session, root, bytes, padding) {
|
|
981
|
+
const offset =
|
|
982
|
+
(await getByteOffsetSession(session, root, null)) -
|
|
983
|
+
(padding ? (padding * flat.leftSpan(root)) / 2 : 0)
|
|
962
984
|
|
|
963
985
|
if (offset > bytes) throw INVALID_OPERATION('Invalid seek')
|
|
964
986
|
if (offset === bytes) return root
|
|
@@ -976,7 +998,7 @@ async function seekUntrustedTree (session, root, bytes, padding) {
|
|
|
976
998
|
// Note, that all these methods are sync as we can statically infer which nodes
|
|
977
999
|
// are needed for the remote to verify given they arguments they passed us
|
|
978
1000
|
|
|
979
|
-
function seekProof
|
|
1001
|
+
function seekProof(session, rx, seekRoot, root, p) {
|
|
980
1002
|
const ite = flat.iterator(seekRoot)
|
|
981
1003
|
let cnt = 0
|
|
982
1004
|
|
|
@@ -991,7 +1013,7 @@ function seekProof (session, rx, seekRoot, root, p) {
|
|
|
991
1013
|
}
|
|
992
1014
|
}
|
|
993
1015
|
|
|
994
|
-
function blockAndSeekProof
|
|
1016
|
+
function blockAndSeekProof(session, rx, node, seek, seekRoot, root, p) {
|
|
995
1017
|
if (!node) return seekProof(session, rx, seekRoot, root, p)
|
|
996
1018
|
|
|
997
1019
|
const ite = flat.iterator(node.index)
|
|
@@ -1001,7 +1023,8 @@ function blockAndSeekProof (session, rx, node, seek, seekRoot, root, p) {
|
|
|
1001
1023
|
if (!node.value) p.node.push(getTreeNodeOrError(rx, ite.index))
|
|
1002
1024
|
|
|
1003
1025
|
while (ite.index !== root) {
|
|
1004
|
-
if (++cnt >= 1024)
|
|
1026
|
+
if (++cnt >= 1024)
|
|
1027
|
+
throw ASSERTION('Bad blockAndSeekProof seekRoot=' + seekRoot + ', root=' + root)
|
|
1005
1028
|
ite.sibling()
|
|
1006
1029
|
|
|
1007
1030
|
if (seek && ite.contains(seekRoot) && ite.index !== seekRoot) {
|
|
@@ -1014,7 +1037,7 @@ function blockAndSeekProof (session, rx, node, seek, seekRoot, root, p) {
|
|
|
1014
1037
|
}
|
|
1015
1038
|
}
|
|
1016
1039
|
|
|
1017
|
-
function upgradeProof
|
|
1040
|
+
function upgradeProof(session, rx, node, seek, from, to, subTree, p) {
|
|
1018
1041
|
if (from === 0) p.upgrade = []
|
|
1019
1042
|
|
|
1020
1043
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -1064,7 +1087,7 @@ function upgradeProof (session, rx, node, seek, from, to, subTree, p) {
|
|
|
1064
1087
|
}
|
|
1065
1088
|
}
|
|
1066
1089
|
|
|
1067
|
-
function additionalUpgradeProof
|
|
1090
|
+
function additionalUpgradeProof(session, rx, from, to, p) {
|
|
1068
1091
|
if (from === 0) p.additionalUpgrade = []
|
|
1069
1092
|
|
|
1070
1093
|
for (const ite = flat.iterator(0); ite.fullRoot(to); ite.nextTree()) {
|
|
@@ -1082,7 +1105,10 @@ function additionalUpgradeProof (session, rx, from, to, p) {
|
|
|
1082
1105
|
ite.seek(target)
|
|
1083
1106
|
|
|
1084
1107
|
while (ite.index !== root) {
|
|
1085
|
-
if (++cnt >= 1024)
|
|
1108
|
+
if (++cnt >= 1024)
|
|
1109
|
+
throw ASSERTION(
|
|
1110
|
+
'Bad arguments to additionalUpgradeProof root=' + root + ' target=' + target
|
|
1111
|
+
)
|
|
1086
1112
|
ite.sibling()
|
|
1087
1113
|
if (ite.index > target) {
|
|
1088
1114
|
p.additionalUpgrade.push(getTreeNodeOrError(rx, ite.index))
|
|
@@ -1102,7 +1128,7 @@ function additionalUpgradeProof (session, rx, from, to, p) {
|
|
|
1102
1128
|
}
|
|
1103
1129
|
}
|
|
1104
1130
|
|
|
1105
|
-
function nodesToRoot
|
|
1131
|
+
function nodesToRoot(index, nodes, head) {
|
|
1106
1132
|
const ite = flat.iterator(index)
|
|
1107
1133
|
|
|
1108
1134
|
for (let i = 0; i < nodes; i++) {
|
|
@@ -1113,27 +1139,27 @@ function nodesToRoot (index, nodes, head) {
|
|
|
1113
1139
|
return ite.index
|
|
1114
1140
|
}
|
|
1115
1141
|
|
|
1116
|
-
function totalSize
|
|
1142
|
+
function totalSize(nodes) {
|
|
1117
1143
|
let s = 0
|
|
1118
1144
|
for (const node of nodes) s += node.size
|
|
1119
1145
|
return s
|
|
1120
1146
|
}
|
|
1121
1147
|
|
|
1122
|
-
function totalSpan
|
|
1148
|
+
function totalSpan(nodes) {
|
|
1123
1149
|
let s = 0
|
|
1124
|
-
for (const node of nodes) s += 2 * (
|
|
1150
|
+
for (const node of nodes) s += 2 * (node.index - s + 1)
|
|
1125
1151
|
return s
|
|
1126
1152
|
}
|
|
1127
1153
|
|
|
1128
|
-
function blockNode
|
|
1154
|
+
function blockNode(index, value) {
|
|
1129
1155
|
return { index, size: value.byteLength, hash: crypto.data(value) }
|
|
1130
1156
|
}
|
|
1131
1157
|
|
|
1132
|
-
function parentNode
|
|
1158
|
+
function parentNode(index, a, b) {
|
|
1133
1159
|
return { index, size: a.size + b.size, hash: crypto.parent(a, b) }
|
|
1134
1160
|
}
|
|
1135
1161
|
|
|
1136
|
-
function log2
|
|
1162
|
+
function log2(n) {
|
|
1137
1163
|
let res = 1
|
|
1138
1164
|
|
|
1139
1165
|
while (n > 2) {
|
|
@@ -1144,40 +1170,48 @@ function log2 (n) {
|
|
|
1144
1170
|
return res
|
|
1145
1171
|
}
|
|
1146
1172
|
|
|
1147
|
-
function normalizeIndexed
|
|
1148
|
-
if (block)
|
|
1149
|
-
|
|
1173
|
+
function normalizeIndexed(block, hash) {
|
|
1174
|
+
if (block)
|
|
1175
|
+
return { value: true, index: block.index * 2, nodes: block.nodes, lastIndex: block.index }
|
|
1176
|
+
if (hash)
|
|
1177
|
+
return {
|
|
1178
|
+
value: false,
|
|
1179
|
+
index: hash.index,
|
|
1180
|
+
nodes: hash.nodes,
|
|
1181
|
+
lastIndex: flat.rightSpan(hash.index) / 2
|
|
1182
|
+
}
|
|
1150
1183
|
return null
|
|
1151
1184
|
}
|
|
1152
1185
|
|
|
1153
|
-
async function getTreeNodeOrError
|
|
1186
|
+
async function getTreeNodeOrError(rx, index) {
|
|
1154
1187
|
const node = await rx.getTreeNode(index)
|
|
1155
|
-
if (node === null)
|
|
1188
|
+
if (node === null)
|
|
1189
|
+
throw INVALID_OPERATION('Expected tree node ' + index + ' from storage, got (nil)')
|
|
1156
1190
|
return node
|
|
1157
1191
|
}
|
|
1158
1192
|
|
|
1159
|
-
function getTreeNodeFromStorageOrError
|
|
1193
|
+
function getTreeNodeFromStorageOrError(storage, index) {
|
|
1160
1194
|
const rx = storage.read()
|
|
1161
1195
|
const p = getTreeNodeOrError(rx, index)
|
|
1162
1196
|
rx.tryFlush()
|
|
1163
1197
|
return p
|
|
1164
1198
|
}
|
|
1165
1199
|
|
|
1166
|
-
function getTreeNodeFromStorage
|
|
1200
|
+
function getTreeNodeFromStorage(storage, index) {
|
|
1167
1201
|
const rx = storage.read()
|
|
1168
1202
|
const node = rx.getTreeNode(index)
|
|
1169
1203
|
rx.tryFlush()
|
|
1170
1204
|
return node
|
|
1171
1205
|
}
|
|
1172
1206
|
|
|
1173
|
-
function hasTreeNode
|
|
1207
|
+
function hasTreeNode(storage, index) {
|
|
1174
1208
|
const rx = storage.read()
|
|
1175
1209
|
const has = rx.hasTreeNode(index)
|
|
1176
1210
|
rx.tryFlush()
|
|
1177
1211
|
return has
|
|
1178
1212
|
}
|
|
1179
1213
|
|
|
1180
|
-
async function settleProof
|
|
1214
|
+
async function settleProof(p) {
|
|
1181
1215
|
const result = [
|
|
1182
1216
|
p.node && Promise.all(p.node),
|
|
1183
1217
|
p.seek && Promise.all(p.seek),
|
|
@@ -1197,13 +1231,14 @@ async function settleProof (p) {
|
|
|
1197
1231
|
}
|
|
1198
1232
|
|
|
1199
1233
|
// tree can be either the merkle tree or a merkle tree batch
|
|
1200
|
-
async function generateProof
|
|
1234
|
+
async function generateProof(session, rx, block, hash, seek, upgrade) {
|
|
1201
1235
|
// Important that this does not throw inbetween making the promise arrays
|
|
1202
1236
|
// and finalise being called, otherwise there will be lingering promises in the background
|
|
1203
1237
|
|
|
1204
1238
|
if (session.prologue && upgrade) {
|
|
1205
1239
|
upgrade.start = upgrade.start < session.prologue.length ? 0 : upgrade.start
|
|
1206
|
-
upgrade.length =
|
|
1240
|
+
upgrade.length =
|
|
1241
|
+
upgrade.start < session.prologue.length ? session.prologue.length : upgrade.length
|
|
1207
1242
|
}
|
|
1208
1243
|
|
|
1209
1244
|
const head = 2 * session.length
|
|
@@ -1227,7 +1262,9 @@ async function generateProof (session, rx, block, hash, seek, upgrade) {
|
|
|
1227
1262
|
|
|
1228
1263
|
if (node !== null && (!upgrade || node.lastIndex < upgrade.start)) {
|
|
1229
1264
|
subTree = nodesToRoot(node.index, node.nodes, to)
|
|
1230
|
-
const seekRoot = seek
|
|
1265
|
+
const seekRoot = seek
|
|
1266
|
+
? await seekUntrustedTree(session, subTree, seek.bytes, seek.padding)
|
|
1267
|
+
: head
|
|
1231
1268
|
blockAndSeekProof(session, rx, node, seek, seekRoot, subTree, p.pending)
|
|
1232
1269
|
} else if ((node || seek) && upgrade) {
|
|
1233
1270
|
subTree = seek ? await seekFromHead(session, to, seek.bytes, seek.padding) : node.index
|
|
@@ -1241,16 +1278,18 @@ async function generateProof (session, rx, block, hash, seek, upgrade) {
|
|
|
1241
1278
|
return p
|
|
1242
1279
|
}
|
|
1243
1280
|
|
|
1244
|
-
function getUnpaddedSize
|
|
1245
|
-
return padding === 0
|
|
1281
|
+
function getUnpaddedSize(node, padding, ite) {
|
|
1282
|
+
return padding === 0
|
|
1283
|
+
? node.size
|
|
1284
|
+
: node.size - padding * (ite ? ite.countLeaves() : flat.countLeaves(node.index))
|
|
1246
1285
|
}
|
|
1247
1286
|
|
|
1248
|
-
function unslabNodes
|
|
1287
|
+
function unslabNodes(nodes) {
|
|
1249
1288
|
for (const node of nodes) unslabNode(node)
|
|
1250
1289
|
return nodes
|
|
1251
1290
|
}
|
|
1252
1291
|
|
|
1253
|
-
function unslabNode
|
|
1292
|
+
function unslabNode(node) {
|
|
1254
1293
|
if (node === null) return node
|
|
1255
1294
|
node.hash = unslab(node.hash)
|
|
1256
1295
|
return node
|