hypercore 10.31.12 → 10.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +15 -4
- package/lib/batch.js +2 -2
- package/lib/core.js +87 -41
- package/lib/merkle-tree.js +27 -2
- package/lib/messages.js +139 -55
- package/lib/multisig.js +46 -45
- package/lib/replicator.js +13 -0
- package/lib/verifier.js +272 -0
- package/package.json +2 -1
- package/lib/manifest.js +0 -222
package/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
|
9
9
|
const Protomux = require('protomux')
|
|
10
10
|
const z32 = require('z32')
|
|
11
11
|
const id = require('hypercore-id-encoding')
|
|
12
|
+
const { createTracer } = require('hypertrace')
|
|
12
13
|
|
|
13
14
|
const Replicator = require('./lib/replicator')
|
|
14
15
|
const Core = require('./lib/core')
|
|
@@ -16,7 +17,7 @@ const BlockEncryption = require('./lib/block-encryption')
|
|
|
16
17
|
const Info = require('./lib/info')
|
|
17
18
|
const Download = require('./lib/download')
|
|
18
19
|
const Batch = require('./lib/batch')
|
|
19
|
-
const { manifestHash, defaultSignerManifest,
|
|
20
|
+
const { manifestHash, defaultSignerManifest, createManifest, isCompat, sign } = require('./lib/verifier')
|
|
20
21
|
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
21
22
|
const {
|
|
22
23
|
BAD_ARGUMENT,
|
|
@@ -48,6 +49,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
48
49
|
|
|
49
50
|
this[promises] = true
|
|
50
51
|
|
|
52
|
+
this.tracer = createTracer(this)
|
|
51
53
|
this.storage = null
|
|
52
54
|
this.crypto = opts.crypto || hypercoreCrypto
|
|
53
55
|
this.core = null
|
|
@@ -133,7 +135,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
static key (manifest, { compat } = {}) {
|
|
136
|
-
return compat ? manifest.
|
|
138
|
+
return compat ? manifest.signers[0].publicKey : manifestHash(createManifest(manifest))
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
static discoveryKey (key) {
|
|
@@ -266,6 +268,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
266
268
|
this.writable = this._isWritable()
|
|
267
269
|
this.autoClose = o.autoClose
|
|
268
270
|
|
|
271
|
+
if (o.core) this.tracer.setParent(o.core.tracer)
|
|
272
|
+
|
|
269
273
|
if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
|
|
270
274
|
}
|
|
271
275
|
|
|
@@ -376,6 +380,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
376
380
|
onupdate: this._oncoreupdate.bind(this),
|
|
377
381
|
onconflict: this._oncoreconflict.bind(this)
|
|
378
382
|
})
|
|
383
|
+
this.tracer.setParent(this.core.tracer)
|
|
379
384
|
|
|
380
385
|
if (opts.userData) {
|
|
381
386
|
for (const [key, value] of Object.entries(opts.userData)) {
|
|
@@ -489,14 +494,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
489
494
|
}
|
|
490
495
|
|
|
491
496
|
const manifest = opts.manifest || defaultSignerManifest(keyPair.publicKey)
|
|
492
|
-
const key = opts.key || (opts.compat !== false ? manifest.
|
|
497
|
+
const key = opts.key || (opts.compat !== false ? manifest.signers[0].publicKey : manifestHash(manifest))
|
|
493
498
|
|
|
494
499
|
if (b4a.equals(key, this.key)) {
|
|
495
500
|
throw BAD_ARGUMENT('Clone cannot share verification information')
|
|
496
501
|
}
|
|
497
502
|
|
|
498
503
|
const signature = opts.signature === undefined
|
|
499
|
-
?
|
|
504
|
+
? sign(createManifest(manifest), this.core.tree.batch(), keyPair, { compat: isCompat(key, manifest) })
|
|
500
505
|
: opts.signature
|
|
501
506
|
|
|
502
507
|
const sparse = opts.sparse === false ? false : this.sparse
|
|
@@ -847,6 +852,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
847
852
|
|
|
848
853
|
async get (index, opts) {
|
|
849
854
|
if (this.opened === false) await this.opening
|
|
855
|
+
|
|
856
|
+
this.tracer.trace('get', { index })
|
|
857
|
+
|
|
850
858
|
if (this.closing !== null) throw SESSION_CLOSED()
|
|
851
859
|
if (this._snapshot !== null && index >= this._snapshot.compatLength) throw SNAPSHOT_NOT_AVAILABLE()
|
|
852
860
|
|
|
@@ -961,6 +969,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
961
969
|
async _download (range) {
|
|
962
970
|
if (this.opened === false) await this.opening
|
|
963
971
|
|
|
972
|
+
this.tracer.trace('download', { range })
|
|
973
|
+
|
|
964
974
|
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
965
975
|
|
|
966
976
|
return this.replicator.addRange(activeRequests, range)
|
|
@@ -1003,6 +1013,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
1003
1013
|
if (writable === false) throw SESSION_NOT_WRITABLE()
|
|
1004
1014
|
|
|
1005
1015
|
blocks = Array.isArray(blocks) ? blocks : [blocks]
|
|
1016
|
+
this.tracer.trace('append', { blocks })
|
|
1006
1017
|
|
|
1007
1018
|
const preappend = this.encryption && this._preappend
|
|
1008
1019
|
|
package/lib/batch.js
CHANGED
|
@@ -187,9 +187,9 @@ module.exports = class HypercoreBatch extends EventEmitter {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
async restoreBatch (length) {
|
|
190
|
+
async restoreBatch (length, blocks) {
|
|
191
191
|
if (this.opened === false) await this.opening
|
|
192
|
-
if (length >= this._sessionLength) return this.createTreeBatch(length)
|
|
192
|
+
if (length >= this._sessionLength) return this.createTreeBatch(length, blocks)
|
|
193
193
|
return this.session.core.tree.restoreBatch(length)
|
|
194
194
|
}
|
|
195
195
|
|
package/lib/core.js
CHANGED
|
@@ -7,12 +7,14 @@ const MerkleTree = require('./merkle-tree')
|
|
|
7
7
|
const BlockStore = require('./block-store')
|
|
8
8
|
const Bitfield = require('./bitfield')
|
|
9
9
|
const Info = require('./info')
|
|
10
|
-
const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_SIGNATURE, INVALID_CHECKSUM } = require('hypercore-errors')
|
|
10
|
+
const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_OPERATION, INVALID_SIGNATURE, INVALID_CHECKSUM } = require('hypercore-errors')
|
|
11
11
|
const m = require('./messages')
|
|
12
|
-
const
|
|
12
|
+
const Verifier = require('./verifier')
|
|
13
|
+
const { createTracer } = require('hypertrace')
|
|
13
14
|
|
|
14
15
|
module.exports = class Core {
|
|
15
16
|
constructor (header, compat, crypto, oplog, bigHeader, tree, blocks, bitfield, verifier, legacy, onupdate, onconflict) {
|
|
17
|
+
this.tracer = createTracer(this)
|
|
16
18
|
this.onupdate = onupdate
|
|
17
19
|
this.onconflict = onconflict
|
|
18
20
|
this.preupdate = null
|
|
@@ -92,11 +94,11 @@ module.exports = class Core {
|
|
|
92
94
|
|
|
93
95
|
const keyPair = opts.keyPair || (opts.key ? null : crypto.keyPair())
|
|
94
96
|
const defaultManifest = !opts.manifest && (!!opts.compat || !opts.key || !!(keyPair && b4a.equals(opts.key, keyPair.publicKey)))
|
|
95
|
-
const manifest = defaultManifest ? defaultSignerManifest(opts.key || keyPair.publicKey) : createManifest(opts.manifest)
|
|
97
|
+
const manifest = defaultManifest ? Verifier.defaultSignerManifest(opts.key || keyPair.publicKey) : Verifier.createManifest(opts.manifest)
|
|
96
98
|
|
|
97
99
|
header = {
|
|
98
100
|
external: null,
|
|
99
|
-
key: opts.key || (compat ? manifest.
|
|
101
|
+
key: opts.key || (compat ? manifest.signers[0].publicKey : Verifier.manifestHash(manifest)),
|
|
100
102
|
manifest,
|
|
101
103
|
keyPair,
|
|
102
104
|
userData: [],
|
|
@@ -119,7 +121,7 @@ module.exports = class Core {
|
|
|
119
121
|
|
|
120
122
|
if (opts.manifest) {
|
|
121
123
|
// if we provide a manifest and no key, verify that the stored key is the same
|
|
122
|
-
if (!opts.key && !isValidManifest(header.key, createManifest(opts.manifest))) {
|
|
124
|
+
if (!opts.key && !Verifier.isValidManifest(header.key, Verifier.createManifest(opts.manifest))) {
|
|
123
125
|
throw STORAGE_CONFLICT('Manifest does not hash to provided key')
|
|
124
126
|
}
|
|
125
127
|
}
|
|
@@ -129,13 +131,15 @@ module.exports = class Core {
|
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
// if we signalled compat, but already now this core isn't disable it
|
|
132
|
-
if (compat && header.manifest && !isCompat(header.key, header.manifest)) {
|
|
134
|
+
if (compat && header.manifest && !Verifier.isCompat(header.key, header.manifest)) {
|
|
133
135
|
compat = false
|
|
134
|
-
} else if (!compat && header.manifest && isCompat(header.key, header.manifest)) {
|
|
136
|
+
} else if (!compat && header.manifest && Verifier.isCompat(header.key, header.manifest)) {
|
|
135
137
|
compat = true
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
const
|
|
140
|
+
const prologue = header.manifest ? header.manifest.prologue : null
|
|
141
|
+
|
|
142
|
+
const tree = await MerkleTree.open(treeFile, { crypto, prologue, ...header.tree })
|
|
139
143
|
const bitfield = await Bitfield.open(bitfieldFile, tree)
|
|
140
144
|
const blocks = new BlockStore(dataFile, tree)
|
|
141
145
|
|
|
@@ -154,7 +158,7 @@ module.exports = class Core {
|
|
|
154
158
|
while (bitfield.get(header.hints.contiguousLength)) header.hints.contiguousLength++
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
const verifier = header.manifest ?
|
|
161
|
+
const verifier = header.manifest ? new Verifier(header.manifest, { compat: Verifier.isCompat(header.key, header.manifest), crypto, legacy }) : null
|
|
158
162
|
|
|
159
163
|
for (const e of entries) {
|
|
160
164
|
if (e.userData) {
|
|
@@ -190,11 +194,13 @@ module.exports = class Core {
|
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
setManifest (manifest, keyPair) {
|
|
193
|
-
if (!manifest && b4a.equals(keyPair.publicKey, this.header.key)) manifest = defaultSignerManifest(this.header.key)
|
|
197
|
+
if (!manifest && b4a.equals(keyPair.publicKey, this.header.key)) manifest = Verifier.defaultSignerManifest(this.header.key)
|
|
194
198
|
if (!manifest) return
|
|
195
199
|
|
|
196
|
-
const compat = isCompat(this.header.key, manifest)
|
|
197
|
-
const verifier =
|
|
200
|
+
const compat = Verifier.isCompat(this.header.key, manifest)
|
|
201
|
+
const verifier = new Verifier(manifest, { compat, crypto: this.crypto, legacy: this._legacy })
|
|
202
|
+
|
|
203
|
+
if (verifier.prologue) this.tree.setPrologue(verifier.prologue)
|
|
198
204
|
|
|
199
205
|
this.compat = compat
|
|
200
206
|
this.header.manifest = manifest
|
|
@@ -219,7 +225,7 @@ module.exports = class Core {
|
|
|
219
225
|
return false
|
|
220
226
|
}
|
|
221
227
|
|
|
222
|
-
async copyFrom (src, signature, { length = src.tree.length } = {}) {
|
|
228
|
+
async copyFrom (src, signature, { length = src.tree.length, additional = [] } = {}) {
|
|
223
229
|
await this._mutex.lock()
|
|
224
230
|
|
|
225
231
|
try {
|
|
@@ -229,16 +235,19 @@ module.exports = class Core {
|
|
|
229
235
|
throw err
|
|
230
236
|
}
|
|
231
237
|
|
|
238
|
+
const initialLength = this.tree.length
|
|
239
|
+
|
|
232
240
|
try {
|
|
233
241
|
const updates = []
|
|
234
242
|
|
|
235
243
|
let pos = 0
|
|
244
|
+
const copyLength = Math.min(src.tree.length, length)
|
|
236
245
|
|
|
237
|
-
while (pos <
|
|
246
|
+
while (pos < copyLength) {
|
|
238
247
|
const segmentStart = maximumSegmentStart(pos, src.bitfield, this.bitfield)
|
|
239
248
|
if (segmentStart >= length || segmentStart < 0) break
|
|
240
249
|
|
|
241
|
-
const segmentEnd = Math.min(length, minimumSegmentEnd(segmentStart, src.bitfield, this.bitfield))
|
|
250
|
+
const segmentEnd = Math.min(src.tree.length, minimumSegmentEnd(segmentStart, src.bitfield, this.bitfield))
|
|
242
251
|
|
|
243
252
|
const segment = []
|
|
244
253
|
|
|
@@ -262,7 +271,7 @@ module.exports = class Core {
|
|
|
262
271
|
})
|
|
263
272
|
}
|
|
264
273
|
|
|
265
|
-
for (let i = 0; i <
|
|
274
|
+
for (let i = 0; i < copyLength * 2; i++) {
|
|
266
275
|
const node = await src.tree.get(i, false)
|
|
267
276
|
if (node === null) continue
|
|
268
277
|
|
|
@@ -271,22 +280,50 @@ module.exports = class Core {
|
|
|
271
280
|
|
|
272
281
|
await this.tree.flush()
|
|
273
282
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (length <
|
|
281
|
-
|
|
282
|
-
this.tree.roots = [...batch.roots]
|
|
283
|
-
this.tree.length = batch.length
|
|
284
|
-
this.tree.byteLength = batch.byteLength
|
|
283
|
+
let batch = this.tree.batch()
|
|
284
|
+
|
|
285
|
+
// add additional blocks
|
|
286
|
+
if (length > src.tree.length) {
|
|
287
|
+
const missing = length - src.tree.length
|
|
288
|
+
|
|
289
|
+
if (additional.length < missing) {
|
|
290
|
+
throw INVALID_OPERATION('Insufficient additional nodes were passed')
|
|
285
291
|
}
|
|
286
292
|
|
|
293
|
+
const source = src.tree.batch()
|
|
294
|
+
|
|
295
|
+
batch.roots = [...source.roots]
|
|
296
|
+
batch.length = source.length
|
|
297
|
+
batch.byteLength = source.byteLength
|
|
298
|
+
|
|
299
|
+
const blocks = additional.length === missing ? additional : additional.slice(0, missing)
|
|
300
|
+
|
|
301
|
+
await this.blocks.putBatch(source.length, blocks, source.byteLength)
|
|
302
|
+
this.bitfield.setRange(source.length, missing, true)
|
|
303
|
+
|
|
304
|
+
updates.push({
|
|
305
|
+
drop: false,
|
|
306
|
+
start: source.length,
|
|
307
|
+
length: missing
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
for (const block of blocks) await batch.append(block)
|
|
311
|
+
} else {
|
|
312
|
+
const source = length < src.tree.length ? await src.tree.truncate(length) : src.tree.batch()
|
|
313
|
+
|
|
314
|
+
this.tree.roots = [...source.roots]
|
|
315
|
+
this.tree.length = source.length
|
|
316
|
+
this.tree.byteLength = source.byteLength
|
|
317
|
+
|
|
318
|
+
// update batch
|
|
319
|
+
batch = this.tree.batch()
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// verify if upgraded
|
|
323
|
+
if (batch.length > initialLength) {
|
|
287
324
|
try {
|
|
288
|
-
const batch = this.tree.batch()
|
|
289
325
|
batch.signature = signature
|
|
326
|
+
|
|
290
327
|
this._verifyBatchUpgrade(batch, this.header.manifest)
|
|
291
328
|
this.tree.signature = signature
|
|
292
329
|
} catch (err) {
|
|
@@ -294,12 +331,16 @@ module.exports = class Core {
|
|
|
294
331
|
// TODO: how to handle signature failure?
|
|
295
332
|
throw err
|
|
296
333
|
}
|
|
297
|
-
|
|
298
|
-
this.header.tree.length = this.tree.length
|
|
299
|
-
this.header.tree.rootHash = this.tree.hash()
|
|
300
|
-
this.header.tree.signature = this.tree.signature
|
|
301
334
|
}
|
|
302
335
|
|
|
336
|
+
await batch.commit()
|
|
337
|
+
|
|
338
|
+
this.tree.fork = src.tree.fork
|
|
339
|
+
|
|
340
|
+
this.header.tree.length = this.tree.length
|
|
341
|
+
this.header.tree.rootHash = this.tree.hash()
|
|
342
|
+
this.header.tree.signature = this.tree.signature
|
|
343
|
+
|
|
303
344
|
this.header.userData = src.header.userData.slice(0)
|
|
304
345
|
this.header.hints.contiguousLength = Math.min(src.header.hints.contiguousLength, this.header.tree.length)
|
|
305
346
|
|
|
@@ -379,6 +420,10 @@ module.exports = class Core {
|
|
|
379
420
|
}
|
|
380
421
|
|
|
381
422
|
async truncate (length, fork, { signature, keyPair = this.header.keyPair } = {}) {
|
|
423
|
+
if (this.tree.prologue && length < this.tree.prologue.length) {
|
|
424
|
+
throw INVALID_OPERATION('Truncation breaks prologue')
|
|
425
|
+
}
|
|
426
|
+
|
|
382
427
|
this.truncating++
|
|
383
428
|
await this._mutex.lock()
|
|
384
429
|
|
|
@@ -553,6 +598,11 @@ module.exports = class Core {
|
|
|
553
598
|
const batch = this.tree.batch()
|
|
554
599
|
for (const val of values) batch.append(val)
|
|
555
600
|
|
|
601
|
+
// only multisig can have prologue so signature is always present
|
|
602
|
+
if (this.tree.prologue && batch.length < this.tree.prologue.length) {
|
|
603
|
+
throw INVALID_OPERATION('Append is not consistent with prologue')
|
|
604
|
+
}
|
|
605
|
+
|
|
556
606
|
batch.signature = signature || this.verifier.sign(batch, keyPair)
|
|
557
607
|
|
|
558
608
|
const entry = {
|
|
@@ -590,21 +640,21 @@ module.exports = class Core {
|
|
|
590
640
|
|
|
591
641
|
_verifyBatchUpgrade (batch, manifest) {
|
|
592
642
|
if (!this.header.manifest) {
|
|
593
|
-
if (!manifest && this.compat) manifest = defaultSignerManifest(this.header.key)
|
|
643
|
+
if (!manifest && this.compat) manifest = Verifier.defaultSignerManifest(this.header.key)
|
|
594
644
|
|
|
595
|
-
if (!manifest || !(isValidManifest(this.header.key, manifest) || (this.compat && isCompat(this.header.key, manifest)))) {
|
|
645
|
+
if (!manifest || !(Verifier.isValidManifest(this.header.key, manifest) || (this.compat && Verifier.isCompat(this.header.key, manifest)))) {
|
|
596
646
|
throw INVALID_SIGNATURE('Proof contains an invalid manifest') // TODO: proper error type
|
|
597
647
|
}
|
|
598
648
|
}
|
|
599
649
|
|
|
600
|
-
const verifier = this.verifier ||
|
|
650
|
+
const verifier = this.verifier || new Verifier(manifest, { compat: Verifier.isCompat(this.header.key, manifest), crypto: this.crypto, legacy: this._legacy })
|
|
601
651
|
|
|
602
652
|
if (!verifier.verify(batch, batch.signature)) {
|
|
603
653
|
throw INVALID_SIGNATURE('Proof contains an invalid signature')
|
|
604
654
|
}
|
|
605
655
|
|
|
606
656
|
if (!this.header.manifest) {
|
|
607
|
-
this.compat = isCompat(this.header.key, manifest)
|
|
657
|
+
this.compat = Verifier.isCompat(this.header.key, manifest)
|
|
608
658
|
this.header.manifest = manifest
|
|
609
659
|
this.verifier = verifier
|
|
610
660
|
this.onupdate(0b10000, null, null, null)
|
|
@@ -703,7 +753,7 @@ module.exports = class Core {
|
|
|
703
753
|
|
|
704
754
|
// if we got a manifest AND its strictly a non compat one, lets store it
|
|
705
755
|
if (manifest && this.header.manifest === null) {
|
|
706
|
-
if (!isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match')
|
|
756
|
+
if (!Verifier.isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match')
|
|
707
757
|
this.setManifest(manifest, null)
|
|
708
758
|
}
|
|
709
759
|
|
|
@@ -879,10 +929,6 @@ function updateContig (header, upd, bitfield) {
|
|
|
879
929
|
return 0b1000
|
|
880
930
|
}
|
|
881
931
|
|
|
882
|
-
function isValidManifest (key, manifest) {
|
|
883
|
-
return b4a.equals(key, manifestHash(manifest))
|
|
884
|
-
}
|
|
885
|
-
|
|
886
932
|
function addReorgHint (list, tree, batch) {
|
|
887
933
|
if (tree.length === 0 || tree.fork === batch.fork) return
|
|
888
934
|
|
package/lib/merkle-tree.js
CHANGED
|
@@ -360,13 +360,14 @@ class ByteSeeker {
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
module.exports = class MerkleTree {
|
|
363
|
-
constructor (storage, roots, fork, signature) {
|
|
363
|
+
constructor (storage, roots, fork, signature, prologue) {
|
|
364
364
|
this.crypto = crypto
|
|
365
365
|
this.fork = fork
|
|
366
366
|
this.roots = roots
|
|
367
367
|
this.length = roots.length ? totalSpan(roots) / 2 : 0
|
|
368
368
|
this.byteLength = totalSize(roots)
|
|
369
369
|
this.signature = signature
|
|
370
|
+
this.prologue = prologue
|
|
370
371
|
|
|
371
372
|
this.storage = storage
|
|
372
373
|
this.unflushed = new Map()
|
|
@@ -424,6 +425,10 @@ module.exports = class MerkleTree {
|
|
|
424
425
|
return Promise.all(roots)
|
|
425
426
|
}
|
|
426
427
|
|
|
428
|
+
setPrologue ({ hash, length }) {
|
|
429
|
+
this.prologue = { hash, length }
|
|
430
|
+
}
|
|
431
|
+
|
|
427
432
|
async upgradeable (length) {
|
|
428
433
|
const indexes = flat.fullRoots(2 * length)
|
|
429
434
|
const roots = new Array(indexes.length)
|
|
@@ -718,7 +723,7 @@ module.exports = class MerkleTree {
|
|
|
718
723
|
roots.push(await getStoredNode(storage, index, null, true))
|
|
719
724
|
}
|
|
720
725
|
|
|
721
|
-
return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null)
|
|
726
|
+
return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null, opts.prologue || null)
|
|
722
727
|
}
|
|
723
728
|
}
|
|
724
729
|
|
|
@@ -811,6 +816,15 @@ function verifyTree ({ block, hash, seek }, crypto, nodes) {
|
|
|
811
816
|
}
|
|
812
817
|
|
|
813
818
|
function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
|
|
819
|
+
const prologue = batch.tree.prologue
|
|
820
|
+
|
|
821
|
+
if (prologue) {
|
|
822
|
+
const { start, length } = upgrade
|
|
823
|
+
if (start < prologue.length && (start !== 0 || length !== prologue.length)) {
|
|
824
|
+
throw INVALID_PROOF('Upgrade does not satisfy prologue')
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
814
828
|
const q = new NodeQueue(upgrade.nodes, blockRoot)
|
|
815
829
|
|
|
816
830
|
let grow = batch.roots.length > 0
|
|
@@ -840,6 +854,12 @@ function verifyUpgrade ({ fork, upgrade }, blockRoot, batch) {
|
|
|
840
854
|
batch.appendRoot(q.shift(ite.index), ite)
|
|
841
855
|
}
|
|
842
856
|
|
|
857
|
+
if (prologue && batch.length === prologue.length) {
|
|
858
|
+
if (!b4a.equals(prologue.hash, batch.hash())) {
|
|
859
|
+
throw INVALID_PROOF('Invalid hash')
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
843
863
|
const extra = upgrade.additionalNodes
|
|
844
864
|
|
|
845
865
|
ite.seek(batch.roots[batch.roots.length - 1].index)
|
|
@@ -1176,6 +1196,11 @@ async function generateProof (tree, block, hash, seek, upgrade) {
|
|
|
1176
1196
|
// Important that this does not throw inbetween making the promise arrays
|
|
1177
1197
|
// and finalise being called, otherwise there will be lingering promises in the background
|
|
1178
1198
|
|
|
1199
|
+
if (tree.prologue && upgrade) {
|
|
1200
|
+
upgrade.start = upgrade.start < tree.prologue.length ? 0 : upgrade.start
|
|
1201
|
+
upgrade.length = upgrade.start < tree.prologue.length ? tree.prologue.length : upgrade.length
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1179
1204
|
const fork = tree.fork
|
|
1180
1205
|
const signature = tree.signature
|
|
1181
1206
|
const head = 2 * tree.length
|