hypercore 10.22.3 → 10.23.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 +69 -43
- package/lib/batch.js +14 -15
- package/lib/caps.js +8 -14
- package/lib/core.js +95 -68
- package/lib/manifest.js +218 -0
- package/lib/merkle-tree.js +6 -6
- package/lib/messages.js +91 -5
- package/lib/multisig.js +123 -0
- package/lib/replicator.js +47 -21
- package/package.json +1 -1
package/lib/core.js
CHANGED
|
@@ -7,25 +7,24 @@ 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 } = require('hypercore-errors')
|
|
11
|
-
const c = require('compact-encoding')
|
|
10
|
+
const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_SIGNATURE, INVALID_CHECKSUM } = require('hypercore-errors')
|
|
12
11
|
const m = require('./messages')
|
|
13
|
-
const
|
|
12
|
+
const { manifestHash, createVerifier, createManifest, defaultSignerManifest, isCompat } = require('./manifest')
|
|
14
13
|
|
|
15
14
|
module.exports = class Core {
|
|
16
|
-
constructor (header, crypto, oplog, bigHeader, tree, blocks, bitfield,
|
|
15
|
+
constructor (header, compat, crypto, oplog, bigHeader, tree, blocks, bitfield, verifier, legacy, onupdate, onconflict) {
|
|
17
16
|
this.onupdate = onupdate
|
|
18
17
|
this.onconflict = onconflict
|
|
19
18
|
this.preupdate = null
|
|
20
19
|
this.header = header
|
|
21
|
-
this.compat =
|
|
20
|
+
this.compat = compat
|
|
22
21
|
this.crypto = crypto
|
|
23
22
|
this.oplog = oplog
|
|
24
23
|
this.bigHeader = bigHeader
|
|
25
24
|
this.tree = tree
|
|
26
25
|
this.blocks = blocks
|
|
27
26
|
this.bitfield = bitfield
|
|
28
|
-
this.
|
|
27
|
+
this.verifier = verifier
|
|
29
28
|
this.truncating = 0
|
|
30
29
|
this.updating = false
|
|
31
30
|
this.closed = false
|
|
@@ -54,26 +53,14 @@ module.exports = class Core {
|
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
static createAuth (crypto, header) {
|
|
58
|
-
const manifest = header.manifest || defaultSignerManifest(header.keyPair.publicKey)
|
|
59
|
-
const secretKey = header.keyPair && header.keyPair.secretKey
|
|
60
|
-
const publicKey = manifest.signer.publicKey
|
|
61
|
-
const sign = signable => crypto.sign(signable, secretKey)
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
sign: secretKey ? sign : null,
|
|
65
|
-
verify (signable, signature) {
|
|
66
|
-
return crypto.verify(signable, signature, publicKey)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
56
|
static async resume (oplogFile, treeFile, bitfieldFile, dataFile, headerFile, opts) {
|
|
72
57
|
let overwrite = opts.overwrite === true
|
|
73
58
|
|
|
74
59
|
const force = opts.force === true
|
|
75
60
|
const createIfMissing = opts.createIfMissing !== false
|
|
76
61
|
const crypto = opts.crypto || hypercoreCrypto
|
|
62
|
+
// kill this flag soon
|
|
63
|
+
const legacy = !!opts.legacy
|
|
77
64
|
|
|
78
65
|
const oplog = new Oplog(oplogFile, {
|
|
79
66
|
headerEncoding: m.oplog.header,
|
|
@@ -81,6 +68,9 @@ module.exports = class Core {
|
|
|
81
68
|
readonly: opts.readonly
|
|
82
69
|
})
|
|
83
70
|
|
|
71
|
+
// default to true for now if no manifest is provided
|
|
72
|
+
let compat = opts.compat === true || (opts.compat !== false && !opts.manifest)
|
|
73
|
+
|
|
84
74
|
let { header, entries } = await oplog.open()
|
|
85
75
|
|
|
86
76
|
if (force && opts.key && header && !b4a.equals(header.key, opts.key)) {
|
|
@@ -94,18 +84,19 @@ module.exports = class Core {
|
|
|
94
84
|
throw STORAGE_EMPTY('No Hypercore is stored here')
|
|
95
85
|
}
|
|
96
86
|
|
|
97
|
-
if (
|
|
87
|
+
if (compat) {
|
|
98
88
|
if (opts.key && opts.keyPair && !b4a.equals(opts.key, opts.keyPair.publicKey)) {
|
|
99
|
-
throw BAD_ARGUMENT('Key must match publicKey when in compat mode
|
|
89
|
+
throw BAD_ARGUMENT('Key must match publicKey when in compat mode')
|
|
100
90
|
}
|
|
101
91
|
}
|
|
102
92
|
|
|
103
93
|
const keyPair = opts.keyPair || (opts.key ? null : crypto.keyPair())
|
|
104
|
-
const
|
|
94
|
+
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)
|
|
105
96
|
|
|
106
97
|
header = {
|
|
107
98
|
external: null,
|
|
108
|
-
key: opts.key || (
|
|
99
|
+
key: opts.key || (compat ? manifest.signer.publicKey : manifestHash(manifest)),
|
|
109
100
|
manifest,
|
|
110
101
|
keyPair,
|
|
111
102
|
userData: [],
|
|
@@ -126,10 +117,22 @@ module.exports = class Core {
|
|
|
126
117
|
header = await bigHeader.load(header.external)
|
|
127
118
|
}
|
|
128
119
|
|
|
120
|
+
if (opts.manifest) {
|
|
121
|
+
// 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))) {
|
|
123
|
+
throw STORAGE_CONFLICT('Manifest does not hash to provided key')
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
129
127
|
if (opts.key && !b4a.equals(header.key, opts.key)) {
|
|
130
128
|
throw STORAGE_CONFLICT('Another Hypercore is stored here')
|
|
131
129
|
}
|
|
132
130
|
|
|
131
|
+
// if we signalled compat, but already now this core isn't disable it
|
|
132
|
+
if (compat && header.manifest && !isCompat(header.key, header.manifest)) {
|
|
133
|
+
compat = false
|
|
134
|
+
}
|
|
135
|
+
|
|
133
136
|
const tree = await MerkleTree.open(treeFile, { crypto, ...header.tree })
|
|
134
137
|
const bitfield = await Bitfield.open(bitfieldFile, tree)
|
|
135
138
|
const blocks = new BlockStore(dataFile, tree)
|
|
@@ -149,7 +152,7 @@ module.exports = class Core {
|
|
|
149
152
|
while (bitfield.get(header.hints.contiguousLength)) header.hints.contiguousLength++
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
const
|
|
155
|
+
const verifier = header.manifest ? createVerifier(header.manifest, { compat: isCompat(header.key, header.manifest), crypto, legacy }) : null
|
|
153
156
|
|
|
154
157
|
for (const e of entries) {
|
|
155
158
|
if (e.userData) {
|
|
@@ -181,7 +184,22 @@ module.exports = class Core {
|
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
return new this(header, crypto, oplog, bigHeader, tree, blocks, bitfield,
|
|
187
|
+
return new this(header, compat, crypto, oplog, bigHeader, tree, blocks, bitfield, verifier, legacy, opts.onupdate || noop, opts.onconflict || noop)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
setManifest (manifest, keyPair) {
|
|
191
|
+
if (!manifest && b4a.equals(keyPair.publicKey, this.header.key)) manifest = defaultSignerManifest(this.header.key)
|
|
192
|
+
if (!manifest) return
|
|
193
|
+
|
|
194
|
+
const compat = isCompat(this.header.key, manifest)
|
|
195
|
+
const verifier = createVerifier(manifest, { compat, crypto: this.crypto, legacy: this._legacy })
|
|
196
|
+
|
|
197
|
+
this.compat = compat
|
|
198
|
+
this.header.manifest = manifest
|
|
199
|
+
this.verifier = verifier
|
|
200
|
+
this._manifestFlushed = false
|
|
201
|
+
|
|
202
|
+
this.onupdate(0b10000, null, null, null)
|
|
185
203
|
}
|
|
186
204
|
|
|
187
205
|
_shouldFlush () {
|
|
@@ -199,7 +217,7 @@ module.exports = class Core {
|
|
|
199
217
|
return false
|
|
200
218
|
}
|
|
201
219
|
|
|
202
|
-
async copyFrom (src, signature
|
|
220
|
+
async copyFrom (src, signature) {
|
|
203
221
|
this._mutex.lock()
|
|
204
222
|
|
|
205
223
|
try {
|
|
@@ -242,14 +260,16 @@ module.exports = class Core {
|
|
|
242
260
|
this.tree.roots = [...src.tree.roots]
|
|
243
261
|
this.tree.length = src.tree.length
|
|
244
262
|
this.tree.byteLength = src.tree.byteLength
|
|
245
|
-
this.tree.signature = null // must provide signature
|
|
246
|
-
|
|
247
|
-
this.tree.signature = signature || auth.sign(this.tree.signable())
|
|
248
263
|
|
|
249
|
-
|
|
250
|
-
|
|
264
|
+
try {
|
|
265
|
+
const batch = this.tree.batch()
|
|
266
|
+
batch.signature = signature
|
|
267
|
+
this._verifyBatchUpgrade(batch, this.header.manifest)
|
|
268
|
+
this.tree.signature = signature
|
|
269
|
+
} catch (err) {
|
|
251
270
|
this.tree.signature = null
|
|
252
|
-
|
|
271
|
+
// TODO: how to handle signature failure?
|
|
272
|
+
throw err
|
|
253
273
|
}
|
|
254
274
|
|
|
255
275
|
this.header.tree.length = this.tree.length
|
|
@@ -263,6 +283,17 @@ module.exports = class Core {
|
|
|
263
283
|
}
|
|
264
284
|
}
|
|
265
285
|
|
|
286
|
+
async flush () {
|
|
287
|
+
await this._mutex.lock()
|
|
288
|
+
try {
|
|
289
|
+
this._manifestFlushed = true
|
|
290
|
+
this._autoFlush = 4
|
|
291
|
+
await this._flushOplog()
|
|
292
|
+
} finally {
|
|
293
|
+
this._mutex.unlock()
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
266
297
|
async _flushOplog () {
|
|
267
298
|
// TODO: the apis using this, actually do not need to wait for the bitfields, tree etc to flush
|
|
268
299
|
// as their mutations are already stored in the oplog. We could potentially just run this in the
|
|
@@ -316,13 +347,16 @@ module.exports = class Core {
|
|
|
316
347
|
}
|
|
317
348
|
}
|
|
318
349
|
|
|
319
|
-
async truncate (length, fork,
|
|
350
|
+
async truncate (length, fork, { signature, keyPair = this.header.keyPair } = {}) {
|
|
320
351
|
this.truncating++
|
|
321
352
|
await this._mutex.lock()
|
|
322
353
|
|
|
354
|
+
// upsert compat manifest
|
|
355
|
+
if (this.verifier === null && keyPair) this.setManifest(null, keyPair)
|
|
356
|
+
|
|
323
357
|
try {
|
|
324
358
|
const batch = await this.tree.truncate(length, fork)
|
|
325
|
-
batch.signature =
|
|
359
|
+
batch.signature = signature || this.verifier.sign(batch, keyPair)
|
|
326
360
|
await this._truncate(batch, null)
|
|
327
361
|
} finally {
|
|
328
362
|
this.truncating--
|
|
@@ -398,11 +432,14 @@ module.exports = class Core {
|
|
|
398
432
|
})
|
|
399
433
|
}
|
|
400
434
|
|
|
401
|
-
async append (values,
|
|
435
|
+
async append (values, { signature, keyPair = this.header.keyPair, preappend } = {}) {
|
|
402
436
|
await this._mutex.lock()
|
|
403
437
|
|
|
438
|
+
// upsert compat manifest
|
|
439
|
+
if (this.verifier === null && keyPair) this.setManifest(null, keyPair)
|
|
440
|
+
|
|
404
441
|
try {
|
|
405
|
-
if (
|
|
442
|
+
if (preappend) await preappend(values)
|
|
406
443
|
|
|
407
444
|
if (!values.length) {
|
|
408
445
|
return { length: this.tree.length, byteLength: this.tree.byteLength }
|
|
@@ -411,8 +448,7 @@ module.exports = class Core {
|
|
|
411
448
|
const batch = this.tree.batch()
|
|
412
449
|
for (const val of values) batch.append(val)
|
|
413
450
|
|
|
414
|
-
|
|
415
|
-
batch.signature = await auth.sign(this._legacy ? batch.signableLegacy(hash) : batch.signable(hash), batch)
|
|
451
|
+
batch.signature = signature || this.verifier.sign(batch, keyPair)
|
|
416
452
|
|
|
417
453
|
const entry = {
|
|
418
454
|
userData: null,
|
|
@@ -433,7 +469,7 @@ module.exports = class Core {
|
|
|
433
469
|
batch.commit()
|
|
434
470
|
|
|
435
471
|
this.header.tree.length = batch.length
|
|
436
|
-
this.header.tree.rootHash = hash
|
|
472
|
+
this.header.tree.rootHash = batch.hash()
|
|
437
473
|
this.header.tree.signature = batch.signature
|
|
438
474
|
|
|
439
475
|
const status = 0b0001 | updateContig(this.header, entry.bitfield, this.bitfield)
|
|
@@ -448,27 +484,25 @@ module.exports = class Core {
|
|
|
448
484
|
}
|
|
449
485
|
|
|
450
486
|
_verifyBatchUpgrade (batch, manifest) {
|
|
451
|
-
const hash = batch.hash()
|
|
452
|
-
const signable = this._legacy ? batch.signableLegacy(hash) : batch.signable(hash)
|
|
453
|
-
|
|
454
487
|
if (!this.header.manifest) {
|
|
455
|
-
if (!manifest
|
|
456
|
-
|
|
457
|
-
|
|
488
|
+
if (!manifest && this.compat) manifest = defaultSignerManifest(this.header.key)
|
|
489
|
+
|
|
490
|
+
if (!manifest || !(isValidManifest(this.header.key, manifest) || (this.compat && isCompat(this.header.key, manifest)))) {
|
|
458
491
|
throw INVALID_SIGNATURE('Proof contains an invalid manifest') // TODO: proper error type
|
|
459
492
|
}
|
|
460
493
|
}
|
|
461
494
|
|
|
462
|
-
const
|
|
495
|
+
const verifier = this.verifier || createVerifier(manifest, { compat: isCompat(this.header.key, manifest), crypto: this.crypto, legacy: this._legacy })
|
|
463
496
|
|
|
464
|
-
if (!batch.signature || !
|
|
497
|
+
if (!batch.signature || !verifier.verify(batch, batch.signature)) {
|
|
465
498
|
throw INVALID_SIGNATURE('Proof contains an invalid signature')
|
|
466
499
|
}
|
|
467
500
|
|
|
468
|
-
this.defaultAuth = auth
|
|
469
501
|
if (!this.header.manifest) {
|
|
470
|
-
this.compat =
|
|
502
|
+
this.compat = isCompat(this.header.key, manifest)
|
|
471
503
|
this.header.manifest = manifest
|
|
504
|
+
this.verifier = verifier
|
|
505
|
+
this.onupdate(0b10000, null, null, null)
|
|
472
506
|
}
|
|
473
507
|
}
|
|
474
508
|
|
|
@@ -548,7 +582,7 @@ module.exports = class Core {
|
|
|
548
582
|
await this.oplog.append(entries, false)
|
|
549
583
|
|
|
550
584
|
for (let i = 0; i < verifies.length; i++) {
|
|
551
|
-
const { batch, bitfield, value, from } = verifies[i]
|
|
585
|
+
const { batch, bitfield, value, manifest, from } = verifies[i]
|
|
552
586
|
|
|
553
587
|
if (!batch.commitable()) {
|
|
554
588
|
verifies[i] = null // signal that we cannot commit this one
|
|
@@ -562,6 +596,12 @@ module.exports = class Core {
|
|
|
562
596
|
status = updateContig(this.header, bitfield, this.bitfield)
|
|
563
597
|
}
|
|
564
598
|
|
|
599
|
+
// if we got a manifest AND its strictly a non compat one, lets store it
|
|
600
|
+
if (manifest && this.header.manifest === null) {
|
|
601
|
+
if (!isValidManifest(this.header.key, manifest)) throw INVALID_CHECKSUM('Manifest hash does not match')
|
|
602
|
+
this.setManifest(manifest, null)
|
|
603
|
+
}
|
|
604
|
+
|
|
565
605
|
batch.commit()
|
|
566
606
|
|
|
567
607
|
this.onupdate(status, bitfield, value, from)
|
|
@@ -730,6 +770,10 @@ function updateContig (header, upd, bitfield) {
|
|
|
730
770
|
return 0b1000
|
|
731
771
|
}
|
|
732
772
|
|
|
773
|
+
function isValidManifest (key, manifest) {
|
|
774
|
+
return b4a.equals(key, manifestHash(manifest))
|
|
775
|
+
}
|
|
776
|
+
|
|
733
777
|
function addReorgHint (list, tree, batch) {
|
|
734
778
|
if (tree.length === 0 || tree.fork === batch.fork) return
|
|
735
779
|
|
|
@@ -788,21 +832,4 @@ async function flushHeader (oplog, bigHeader, header) {
|
|
|
788
832
|
}
|
|
789
833
|
}
|
|
790
834
|
|
|
791
|
-
function defaultSignerManifest (publicKey) {
|
|
792
|
-
return {
|
|
793
|
-
hash: 'blake2b',
|
|
794
|
-
static: null,
|
|
795
|
-
signer: {
|
|
796
|
-
signature: 'ed25519',
|
|
797
|
-
namespace: caps.DEFAULT_NAMESPACE,
|
|
798
|
-
publicKey
|
|
799
|
-
},
|
|
800
|
-
multipleSigners: null
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function hashManifest (manifest) {
|
|
805
|
-
return caps.manifestHash(c.encode(m.manifest, manifest))
|
|
806
|
-
}
|
|
807
|
-
|
|
808
835
|
function noop () {}
|
package/lib/manifest.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const defaultCrypto = require('hypercore-crypto')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
const c = require('compact-encoding')
|
|
4
|
+
const { BAD_ARGUMENT } = require('hypercore-errors')
|
|
5
|
+
|
|
6
|
+
const m = require('./messages')
|
|
7
|
+
const multisig = require('./multisig')
|
|
8
|
+
const caps = require('./caps')
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
manifestHash,
|
|
12
|
+
isCompat,
|
|
13
|
+
defaultSignerManifest,
|
|
14
|
+
createManifest,
|
|
15
|
+
createVerifier
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class StaticVerifier {
|
|
19
|
+
constructor (treeHash) {
|
|
20
|
+
this.treeHash = treeHash
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
sign () {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
verify (batch, signature) {
|
|
28
|
+
return b4a.equals(batch.hash(), this.treeHash)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class CompatVerifier {
|
|
33
|
+
constructor (crypto, signer, legacy) {
|
|
34
|
+
validateSigner(signer)
|
|
35
|
+
|
|
36
|
+
this.legacy = legacy
|
|
37
|
+
this.crypto = crypto
|
|
38
|
+
this.publicKey = signer.publicKey
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
sign (batch, keyPair) {
|
|
42
|
+
if (!keyPair || !keyPair.secretKey) throw BAD_ARGUMENT('No signer was passed')
|
|
43
|
+
return this.crypto.sign(batch.signableCompat(this.legacy), keyPair.secretKey)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
verify (batch, signature) {
|
|
47
|
+
return this.crypto.verify(batch.signableCompat(this.legacy), signature, this.publicKey)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class SingleVerifier {
|
|
52
|
+
constructor (crypto, signer) {
|
|
53
|
+
validateSigner(signer)
|
|
54
|
+
|
|
55
|
+
this.crypto = crypto
|
|
56
|
+
this.publicKey = signer.publicKey
|
|
57
|
+
this.namespace = signer.namespace
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
sign (batch, keyPair) {
|
|
61
|
+
if (!keyPair || !keyPair.secretKey) throw BAD_ARGUMENT('No signer was passed')
|
|
62
|
+
return this.crypto.sign(batch.signable(this.namespace), keyPair.secretKey)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
verify (batch, signature) {
|
|
66
|
+
return this.crypto.verify(batch.signable(this.namespace), signature, this.publicKey)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class MultiVerifier {
|
|
71
|
+
constructor (crypto, multipleSigners) {
|
|
72
|
+
this.signers = multipleSigners.signers
|
|
73
|
+
this.quorum = multipleSigners.quorum
|
|
74
|
+
this.allowPatched = multipleSigners.allowPatched
|
|
75
|
+
this.verifiers = this.signers.map(s => new SingleVerifier(crypto, s))
|
|
76
|
+
|
|
77
|
+
if (this.verifiers.length < this.quorum || (this.quorum === 0)) throw BAD_ARGUMENT('Invalid quorum')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
sign () {
|
|
81
|
+
throw BAD_ARGUMENT('Multi signature must be provided')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
verify (batch, signature) {
|
|
85
|
+
const inputs = multisig.inflate(signature)
|
|
86
|
+
|
|
87
|
+
if (inputs.length < this.quorum) return false
|
|
88
|
+
|
|
89
|
+
const tried = new Uint8Array(this.verifiers.length)
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < this.quorum; i++) {
|
|
92
|
+
const inp = inputs[i]
|
|
93
|
+
|
|
94
|
+
let tree = batch
|
|
95
|
+
|
|
96
|
+
if (inp.patch) {
|
|
97
|
+
if (!this.allowPatched) return false
|
|
98
|
+
|
|
99
|
+
tree = batch.clone()
|
|
100
|
+
const proof = { fork: tree.fork, block: null, hash: null, seek: null, upgrade: inp.patch, manifest: null }
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
if (!tree.verifyUpgrade(proof)) return false
|
|
104
|
+
} catch {
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (inp.signer >= this.verifiers.length || tried[inp.signer]) return false
|
|
110
|
+
tried[inp.signer] = 1
|
|
111
|
+
|
|
112
|
+
if (!this.verifiers[inp.signer].verify(tree, inp.signature)) return false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function createVerifier (manifest, { compat = false, crypto = defaultCrypto, legacy = false } = {}) {
|
|
120
|
+
if (compat && manifest.signer) {
|
|
121
|
+
return new CompatVerifier(crypto, manifest.signer, legacy)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (manifest.static) {
|
|
125
|
+
return new StaticVerifier(manifest.static)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (manifest.signer) {
|
|
129
|
+
return new SingleVerifier(crypto, manifest.signer)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (manifest.multipleSigners) {
|
|
133
|
+
return new MultiVerifier(crypto, manifest.multipleSigners)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
throw BAD_ARGUMENT('No signer was provided')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function createManifest (inp) {
|
|
140
|
+
if (!inp) return null
|
|
141
|
+
|
|
142
|
+
const manifest = {
|
|
143
|
+
hash: 'blake2b',
|
|
144
|
+
static: null,
|
|
145
|
+
signer: null,
|
|
146
|
+
multipleSigners: null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (inp.hash && inp.hash !== 'blake2b') throw BAD_ARGUMENT('Only Blake2b hashes are supported')
|
|
150
|
+
|
|
151
|
+
if (inp.static) {
|
|
152
|
+
if (!(b4a.isBuffer(inp.static) && inp.static.byteLength === 32)) throw BAD_ARGUMENT('Invalid static manifest')
|
|
153
|
+
manifest.static = inp.static
|
|
154
|
+
return manifest
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (inp.signer) {
|
|
158
|
+
manifest.signer = parseSigner(inp.signer)
|
|
159
|
+
return manifest
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (inp.multipleSigners) {
|
|
163
|
+
manifest.multipleSigners = parseMultipleSigners(inp.multipleSigners)
|
|
164
|
+
return manifest
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
throw BAD_ARGUMENT('No signer was provided')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function parseMultipleSigners (m) {
|
|
171
|
+
if (m.signers.length < m.quorum || !(m.quorum > 0)) throw BAD_ARGUMENT('Invalid quorum')
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
allowPatched: !!m.allowPatched,
|
|
175
|
+
quorum: m.quorum,
|
|
176
|
+
signers: m.signers.map(parseSigner)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function parseSigner (signer) {
|
|
181
|
+
validateSigner(signer)
|
|
182
|
+
return {
|
|
183
|
+
signature: 'ed25519',
|
|
184
|
+
namespace: signer.namespace || caps.DEFAULT_NAMESPACE,
|
|
185
|
+
publicKey: signer.publicKey
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function validateSigner (signer) {
|
|
190
|
+
if (!signer || !signer.publicKey) throw BAD_ARGUMENT('Signer missing public key')
|
|
191
|
+
if (signer.signature !== 'ed25519') throw BAD_ARGUMENT('Only Ed25519 signatures are supported')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function defaultSignerManifest (publicKey) {
|
|
195
|
+
return {
|
|
196
|
+
hash: 'blake2b',
|
|
197
|
+
static: null,
|
|
198
|
+
signer: {
|
|
199
|
+
signature: 'ed25519',
|
|
200
|
+
namespace: caps.DEFAULT_NAMESPACE,
|
|
201
|
+
publicKey
|
|
202
|
+
},
|
|
203
|
+
multipleSigners: null
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function manifestHash (manifest) {
|
|
208
|
+
const state = { start: 0, end: 32, buffer: null }
|
|
209
|
+
m.manifest.preencode(state, manifest)
|
|
210
|
+
state.buffer = b4a.allocUnsafe(state.end)
|
|
211
|
+
c.raw.encode(state, caps.MANIFEST)
|
|
212
|
+
m.manifest.encode(state, manifest)
|
|
213
|
+
return defaultCrypto.hash(state.buffer)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function isCompat (key, manifest) {
|
|
217
|
+
return !!(manifest && manifest.signer && b4a.equals(key, manifest.signer.publicKey))
|
|
218
|
+
}
|
package/lib/merkle-tree.js
CHANGED
|
@@ -79,12 +79,12 @@ class MerkleTreeBatch {
|
|
|
79
79
|
return this.hashCached
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
signable (
|
|
83
|
-
return caps.treeSignable(hash, this.length, this.fork)
|
|
82
|
+
signable (namespace) {
|
|
83
|
+
return caps.treeSignable(namespace, this.hash(), this.length, this.fork)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
return caps.
|
|
86
|
+
signableCompat (noHeader) {
|
|
87
|
+
return caps.treeSignableCompat(this.hash(), this.length, this.fork, noHeader)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
get (index) {
|
|
@@ -425,8 +425,8 @@ module.exports = class MerkleTree {
|
|
|
425
425
|
return this.crypto.tree(this.roots)
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
signable (
|
|
429
|
-
return caps.treeSignable(hash, this.length, this.fork)
|
|
428
|
+
signable (namespace) {
|
|
429
|
+
return caps.treeSignable(namespace, this.hash(), this.length, this.fork)
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
getRoots (length) {
|