@smartledger/bsv 8.2.0 → 8.3.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.
@@ -0,0 +1,334 @@
1
+ 'use strict'
2
+
3
+ var Transaction = require('../transaction')
4
+ var Hash = require('../crypto/hash')
5
+ var SPV = require('../spv')
6
+ var Encoding = require('./encoding')
7
+ var NotaryScript = require('./script')
8
+ var Certificate = require('./certificate')
9
+ var Suites = require('./suites')
10
+ var Merkle = require('./merkle')
11
+
12
+ /**
13
+ * BRC-220 NotaryHash.
14
+ *
15
+ * A certificate is valid if and only if ALL THREE of the spec's checks hold:
16
+ *
17
+ * 1. Signature — verify(algorithm, payloadHash, signature, publicKey) (offline)
18
+ * 2. Proof integrity — recomputed proofHash equals certificate.proofHash (offline)
19
+ * 3. Anchor — SPV against a block header, or a direct chain lookup
20
+ *
21
+ * `verify()` runs all three and reports each separately, because "invalid" without
22
+ * saying which check failed is unactionable — a bad signature and an unmined transaction
23
+ * are different problems with different fixes.
24
+ *
25
+ * NOTE this library never fetches a block header. The spec is explicit that the verifier
26
+ * "trusts only a block header, obtained from any source it chooses", and choosing that
27
+ * source is the caller's decision, not ours: a single provider is a single point of
28
+ * trust, and quietly picking one on the caller's behalf would hide exactly the trust
29
+ * assumption the protocol exists to remove. The caller supplies the header.
30
+ */
31
+
32
+ var NotaryHash = {}
33
+
34
+ NotaryHash.Encoding = Encoding
35
+ NotaryHash.Script = NotaryScript
36
+ NotaryHash.Certificate = Certificate
37
+ NotaryHash.Suites = Suites
38
+ NotaryHash.Merkle = Merkle
39
+ NotaryHash.MODE = NotaryScript.MODE
40
+
41
+ /** Register a signature suite. See lib/notaryhash/suites.js for why PQ is not built in. */
42
+ NotaryHash.registerSuite = function (algorithm, suite) {
43
+ return Suites.register(algorithm, suite)
44
+ }
45
+
46
+ /**
47
+ * Check 1 — the signature, offline.
48
+ *
49
+ * @param {Object} certificate
50
+ * @returns {Boolean}
51
+ */
52
+ NotaryHash.verifySignature = function (certificate) {
53
+ try {
54
+ if (!certificate || typeof certificate !== 'object') return false
55
+ return Suites.verify(
56
+ certificate.algorithm,
57
+ Buffer.from(certificate.payloadHash, 'hex'),
58
+ Buffer.from(certificate.signature, 'hex'),
59
+ Buffer.from(certificate.publicKey, 'hex')
60
+ )
61
+ } catch (e) {
62
+ return false
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Compute a txid from a raw transaction, the way the spec states it:
68
+ * `txid = reverse(SHA256(SHA256(rawTx)))`.
69
+ *
70
+ * This is what makes provider-supplied data self-checking — a raw transaction is only
71
+ * accepted if it hashes to the txid already held, so a provider cannot substitute
72
+ * different bytes.
73
+ *
74
+ * @param {Buffer|String} rawTx
75
+ * @returns {String} txid, big-endian hex as displayed
76
+ */
77
+ NotaryHash.txidFromRawTx = function (rawTx) {
78
+ var buf = Buffer.isBuffer(rawTx) ? rawTx : Buffer.from(rawTx, 'hex')
79
+ return Buffer.from(Hash.sha256sha256(buf)).reverse().toString('hex')
80
+ }
81
+
82
+ /**
83
+ * Find and parse the NotaryHash record in a raw transaction.
84
+ *
85
+ * @param {Buffer|String} rawTx
86
+ * @returns {Object|null} the parsed record, or null if there is none
87
+ */
88
+ NotaryHash.recordFromRawTx = function (rawTx) {
89
+ try {
90
+ var tx = new Transaction(Buffer.isBuffer(rawTx) ? rawTx.toString('hex') : rawTx)
91
+ for (var i = 0; i < tx.outputs.length; i++) {
92
+ var script = tx.outputs[i].script
93
+ if (NotaryScript.isNotaryHash(script)) {
94
+ return NotaryScript.parse(script)
95
+ }
96
+ }
97
+ return null
98
+ } catch (e) {
99
+ return null
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Does an on-chain record agree with the certificate that claims it?
105
+ *
106
+ * Compares only the fields the record actually carries. In hybrid mode the chain holds
107
+ * SHA-256 of the key and signature, so those are compared as digests of the
108
+ * certificate's full blobs — which is the whole point of the mode, and the step that
109
+ * would otherwise let a hybrid certificate reference a record for a different key.
110
+ *
111
+ * @param {Object} record - from NotaryScript.parse
112
+ * @param {Object} certificate
113
+ * @returns {Boolean}
114
+ */
115
+ NotaryHash.recordMatchesCertificate = function (record, certificate) {
116
+ try {
117
+ if (!record || !certificate) return false
118
+ if (record.mode !== certificate.mode) return false
119
+
120
+ if (record.mode === NotaryScript.MODE.BATCH) {
121
+ if (!certificate.merkle) return false
122
+ if (record.merkleRoot.toString('hex') !== String(certificate.merkle.root).toLowerCase()) {
123
+ return false
124
+ }
125
+ // leafCount is compared because the inclusion proof CANNOT be relied on to catch a
126
+ // wrong one. RFC 6962 derives the tree shape from the count, but for most indices
127
+ // the fold is identical across neighbouring counts — measured on an 8-leaf tree,
128
+ // only indices 6 and 7 fold differently when the count is claimed as 7. The
129
+ // on-chain u32be is the authoritative value, so it is checked here rather than
130
+ // assumed to be implied.
131
+ return record.leafCount === certificate.merkle.leafCount
132
+ }
133
+
134
+ if (record.algorithm !== certificate.algorithm) return false
135
+ if (record.hashAlgorithm !== certificate.hashAlgorithm) return false
136
+ if (record.payloadHash.toString('hex') !== String(certificate.payloadHash).toLowerCase()) return false
137
+ if (record.proofHash.toString('hex') !== String(certificate.proofHash).toLowerCase()) return false
138
+
139
+ var certPub = Buffer.from(certificate.publicKey, 'hex')
140
+ var certSig = Buffer.from(certificate.signature, 'hex')
141
+
142
+ if (record.mode === NotaryScript.MODE.HYBRID) {
143
+ return record.publicKeyHash.equals(Hash.sha256(certPub)) &&
144
+ record.signatureHash.equals(Hash.sha256(certSig))
145
+ }
146
+
147
+ return record.publicKey.equals(certPub) && record.signature.equals(certSig)
148
+ } catch (e) {
149
+ return false
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Check 3 — the anchor, via SPV.
155
+ *
156
+ * The caller supplies the block HEADER, obtained however it chose. Not a bare Merkle
157
+ * root: a header carries the proof of work, so `lib/spv` can confirm the root belongs to
158
+ * a block that cost something to produce rather than to a root someone asserted. Without
159
+ * a header this returns false — a certificate whose anchor has not been checked against
160
+ * one has not satisfied check 3, and reporting otherwise would restore the exact trust
161
+ * the spec removes.
162
+ *
163
+ * @param {Object} certificate - must carry an `spv` envelope
164
+ * @param {Object} opts
165
+ * @param {String|Buffer|BlockHeader} opts.header - independently obtained
166
+ * @param {Boolean} [opts.requirePow=true] - pass false only for test fixtures
167
+ * @returns {Object} { valid, errors }
168
+ */
169
+ NotaryHash.verifyAnchorSPV = function (certificate, opts) {
170
+ var errors = []
171
+ opts = opts || {}
172
+
173
+ try {
174
+ var spv = certificate && certificate.spv
175
+ if (!spv) {
176
+ return { valid: false, errors: ['certificate has no SPV envelope'] }
177
+ }
178
+ if (!opts.header) {
179
+ return {
180
+ valid: false,
181
+ errors: ['a block header is required: the verifier must obtain one itself ' +
182
+ 'rather than trust the certificate or its issuer for the anchor']
183
+ }
184
+ }
185
+
186
+ var txid = NotaryHash.txidFromRawTx(spv.rawTx)
187
+ if (txid !== String(certificate.anchor.txid).toLowerCase()) {
188
+ errors.push('rawTx does not hash to anchor.txid')
189
+ }
190
+
191
+ var record = NotaryHash.recordFromRawTx(spv.rawTx)
192
+ if (!record) {
193
+ errors.push('no NotaryHash record found in rawTx')
194
+ } else if (!NotaryHash.recordMatchesCertificate(record, certificate)) {
195
+ errors.push('on-chain record does not match the certificate')
196
+ }
197
+
198
+ var proof = spv.merkleProof || {}
199
+ var inclusion = SPV.verifyTxInclusion({
200
+ header: opts.header,
201
+ txid: txid,
202
+ index: proof.index,
203
+ nodes: proof.nodes,
204
+ requirePow: opts.requirePow !== false
205
+ })
206
+ if (inclusion.valid !== true) {
207
+ errors.push(inclusion.rootMatches === false
208
+ ? 'merkle proof does not fold to the header\'s root'
209
+ : 'block header failed proof-of-work validation')
210
+ }
211
+
212
+ return { valid: errors.length === 0, errors: errors }
213
+ } catch (e) {
214
+ return { valid: false, errors: ['anchor verification error: ' + e.message] }
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Batch inclusion — the extra step a batched certificate needs.
220
+ *
221
+ * The on-chain record for a batch carries only a root and a leaf count, so the
222
+ * certificate's own `merkle` proof is what ties it to that root. Folding is RFC 6962,
223
+ * NOT the Bitcoin tree in lib/spv — see lib/notaryhash/merkle.js for why the difference
224
+ * matters and why reusing the other one would be silently wrong.
225
+ *
226
+ * ASSUMPTION, flagged rather than buried: the leaf data is the certificate's proofHash.
227
+ * The spec says a batch "anchors many proofs" under one root and that the certificate's
228
+ * merkle proof must fold to it, but does not state what a leaf contains. proofHash is the
229
+ * only per-proof 32-byte value that identifies the proof, so it is the reading taken
230
+ * here — and it needs confirming against the reference implementation before this is
231
+ * relied on. See docs/BRC220_PLAN.md.
232
+ *
233
+ * @param {Object} certificate
234
+ * @returns {Object} { valid, errors }
235
+ */
236
+ NotaryHash.verifyBatchInclusion = function (certificate) {
237
+ try {
238
+ if (!certificate || certificate.mode !== NotaryScript.MODE.BATCH) {
239
+ return { valid: false, errors: ['certificate is not in batch mode'] }
240
+ }
241
+ var m = certificate.merkle
242
+ if (!m) return { valid: false, errors: ['batch certificate has no merkle proof'] }
243
+
244
+ var leafData = Buffer.from(certificate.proofHash, 'hex')
245
+ var path = (m.path || []).map(function (node) {
246
+ return Buffer.isBuffer(node) ? node : Buffer.from(node, 'hex')
247
+ })
248
+ var root = Buffer.isBuffer(m.root) ? m.root : Buffer.from(String(m.root), 'hex')
249
+
250
+ var included = Merkle.verifyInclusion(leafData, m.leafIndex, m.leafCount, path, root)
251
+ return included
252
+ ? { valid: true, errors: [] }
253
+ : { valid: false, errors: ['merkle inclusion proof does not fold to the batch root'] }
254
+ } catch (e) {
255
+ return { valid: false, errors: ['batch inclusion error: ' + e.message] }
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Verify a certificate: all three checks.
261
+ *
262
+ * Returns a REPORT, not a boolean — `valid` is the verdict and the per-check fields say
263
+ * why. Callers must read `.valid`; the object itself is always truthy, and this module
264
+ * deliberately does not hand back something that could be mistaken for a pass. That
265
+ * distinction has bitten this codebase repeatedly, so `isValid()` below exists for the
266
+ * `if (...)` case.
267
+ *
268
+ * @param {Object} certificate
269
+ * @param {Object} [opts]
270
+ * @param {String|Buffer} [opts.header] - an independently obtained block header
271
+ * @param {Boolean} [opts.skipAnchor] - check 1 and 2 only; the result is NOT a valid
272
+ * certificate, and `valid` will be false. For offline triage.
273
+ * @returns {Object} { valid, signature, proofIntegrity, anchor, shape, errors }
274
+ */
275
+ NotaryHash.verify = function (certificate, opts) {
276
+ opts = opts || {}
277
+
278
+ var report = {
279
+ valid: false,
280
+ shape: [],
281
+ signature: false,
282
+ proofIntegrity: false,
283
+ anchor: false,
284
+ errors: []
285
+ }
286
+
287
+ report.shape = Certificate.validateShape(certificate)
288
+ if (report.shape.length) {
289
+ report.errors = report.shape.slice()
290
+ return report
291
+ }
292
+
293
+ report.signature = NotaryHash.verifySignature(certificate)
294
+ if (!report.signature) report.errors.push('signature does not verify')
295
+
296
+ report.proofIntegrity = Certificate.proofHashMatches(certificate)
297
+ if (!report.proofIntegrity) report.errors.push('proofHash does not match the certificate fields')
298
+
299
+ if (opts.skipAnchor) {
300
+ report.errors.push('anchor not checked (skipAnchor): this certificate is NOT verified')
301
+ return report
302
+ }
303
+
304
+ var anchor = NotaryHash.verifyAnchorSPV(certificate, opts)
305
+ report.anchor = anchor.valid
306
+ anchor.errors.forEach(function (e) { report.errors.push(e) })
307
+
308
+ // A batched certificate has a fourth thing to prove: that this proof is actually one
309
+ // of the ones the on-chain root commits to. Without it, any certificate could point at
310
+ // any batch anchor and the anchor check alone would not notice.
311
+ report.batchInclusion = true
312
+ if (certificate.mode === NotaryScript.MODE.BATCH) {
313
+ var batch = NotaryHash.verifyBatchInclusion(certificate)
314
+ report.batchInclusion = batch.valid
315
+ batch.errors.forEach(function (e) { report.errors.push(e) })
316
+ }
317
+
318
+ report.valid = report.signature && report.proofIntegrity && report.anchor &&
319
+ report.batchInclusion
320
+ return report
321
+ }
322
+
323
+ /**
324
+ * Strict boolean verdict, for `if (...)`.
325
+ *
326
+ * @param {Object} certificate
327
+ * @param {Object} [opts]
328
+ * @returns {Boolean}
329
+ */
330
+ NotaryHash.isValid = function (certificate, opts) {
331
+ return NotaryHash.verify(certificate, opts).valid === true
332
+ }
333
+
334
+ module.exports = NotaryHash
@@ -0,0 +1,209 @@
1
+ 'use strict'
2
+
3
+ var Hash = require('../crypto/hash')
4
+ var $ = require('../util/preconditions')
5
+
6
+ /**
7
+ * RFC 6962 Merkle trees, for BRC-220 batch mode.
8
+ *
9
+ * THIS IS NOT THE BITCOIN MERKLE TREE, and it is not the one in lib/gdaf/zk-prover.js
10
+ * either. Three trees now live in this repository and all three differ:
11
+ *
12
+ * lib/spv/merkleproof.js leaf = txid node = sha256d(l‖r) odd leaf DUPLICATED
13
+ * lib/gdaf/zk-prover.js leaf = salted hash node = sha256(l‖r) odd leaf duplicated
14
+ * here (RFC 6962) leaf = sha256(0x00‖d) node = sha256(0x01‖l‖r) NEVER duplicated
15
+ *
16
+ * The domain separation and the no-duplication rule are what make RFC 6962 resistant to
17
+ * the second-preimage attack Bitcoin's tree is famously open to: without the 0x00/0x01
18
+ * prefixes, an internal node can be reinterpreted as a leaf.
19
+ *
20
+ * Reusing either of the other two here would produce a root that no other BRC-220
21
+ * implementation computes, and the failure would surface only when somebody else tried
22
+ * to verify a batch certificate. That is why this file exists rather than an import.
23
+ *
24
+ * One more trap: RFC 6962 splits at the LARGEST POWER OF TWO STRICTLY LESS THAN n, not at
25
+ * the midpoint. For n = 5 that is 4/1, where a midpoint split gives 2/3 and a different
26
+ * root. Every non-power-of-two tree depends on getting this right.
27
+ */
28
+
29
+ var Merkle = {}
30
+
31
+ /** Prefix bytes, per RFC 6962 §2.1. */
32
+ Merkle.LEAF_PREFIX = 0x00
33
+ Merkle.NODE_PREFIX = 0x01
34
+
35
+ /**
36
+ * Hash a leaf: `SHA-256(0x00 || d)`.
37
+ *
38
+ * @param {Buffer} d
39
+ * @returns {Buffer} 32 bytes
40
+ */
41
+ Merkle.hashLeaf = function (d) {
42
+ $.checkArgument(Buffer.isBuffer(d), 'leaf data must be a Buffer')
43
+ return Hash.sha256(Buffer.concat([Buffer.from([Merkle.LEAF_PREFIX]), d]))
44
+ }
45
+
46
+ /**
47
+ * Hash an internal node: `SHA-256(0x01 || left || right)`.
48
+ *
49
+ * @param {Buffer} left
50
+ * @param {Buffer} right
51
+ * @returns {Buffer} 32 bytes
52
+ */
53
+ Merkle.hashNode = function (left, right) {
54
+ return Hash.sha256(Buffer.concat([Buffer.from([Merkle.NODE_PREFIX]), left, right]))
55
+ }
56
+
57
+ /**
58
+ * The largest power of two strictly less than n.
59
+ *
60
+ * RFC 6962 splits here rather than at the midpoint, which is what makes its trees
61
+ * left-complete. For n = 5 this is 4, so the split is 4/1 — a midpoint split would give
62
+ * 2/3 and a root no conformant implementation would agree with.
63
+ *
64
+ * @param {Number} n - at least 2
65
+ * @returns {Number}
66
+ */
67
+ Merkle.largestPowerOfTwoBelow = function (n) {
68
+ $.checkArgument(Number.isInteger(n) && n >= 2, 'n must be an integer >= 2')
69
+ var k = 1
70
+ while (k * 2 < n) k *= 2
71
+ return k
72
+ }
73
+
74
+ /**
75
+ * The Merkle Tree Hash of a list of leaf data, per RFC 6962 §2.1.
76
+ *
77
+ * MTH({}) = SHA-256() — the hash of the empty string
78
+ * MTH({d}) = SHA-256(0x00 || d)
79
+ * MTH(D[n]) = SHA-256(0x01 || MTH(D[0:k]) || MTH(D[k:n]))
80
+ *
81
+ * @param {Array<Buffer>} leaves - the leaf DATA, not pre-hashed
82
+ * @returns {Buffer} 32 bytes
83
+ */
84
+ Merkle.root = function (leaves) {
85
+ $.checkArgument(Array.isArray(leaves), 'leaves must be an array')
86
+
87
+ // The empty tree is the hash of the empty string, not a zero buffer. Stated explicitly
88
+ // because "no leaves" is easy to answer with 32 zero bytes and be wrong.
89
+ if (leaves.length === 0) return Hash.sha256(Buffer.alloc(0))
90
+ if (leaves.length === 1) return Merkle.hashLeaf(leaves[0])
91
+
92
+ var k = Merkle.largestPowerOfTwoBelow(leaves.length)
93
+ return Merkle.hashNode(
94
+ Merkle.root(leaves.slice(0, k)),
95
+ Merkle.root(leaves.slice(k))
96
+ )
97
+ }
98
+
99
+ /**
100
+ * The audit path for the leaf at `index`, per RFC 6962 §2.1.1.
101
+ *
102
+ * Returns sibling hashes ordered from the leaf upward. Unlike the Bitcoin proofs in
103
+ * lib/spv, there is no direction flag: RFC 6962 recovers left/right from the index and
104
+ * the tree size during folding, so the path is bare hashes and `verify` needs both
105
+ * numbers.
106
+ *
107
+ * @param {Array<Buffer>} leaves
108
+ * @param {Number} index
109
+ * @returns {Array<Buffer>}
110
+ */
111
+ Merkle.path = function (leaves, index) {
112
+ $.checkArgument(Array.isArray(leaves) && leaves.length > 0, 'leaves must be a non-empty array')
113
+ $.checkArgument(Number.isInteger(index) && index >= 0 && index < leaves.length,
114
+ 'index must be within the leaves')
115
+
116
+ if (leaves.length === 1) return []
117
+
118
+ var k = Merkle.largestPowerOfTwoBelow(leaves.length)
119
+
120
+ if (index < k) {
121
+ return Merkle.path(leaves.slice(0, k), index)
122
+ .concat([Merkle.root(leaves.slice(k))])
123
+ }
124
+ return Merkle.path(leaves.slice(k), index - k)
125
+ .concat([Merkle.root(leaves.slice(0, k))])
126
+ }
127
+
128
+ /**
129
+ * Fold an audit path back to a root, per RFC 6962 §2.1.2.
130
+ *
131
+ * `leafCount` is required and is not decoration: the tree's shape at each level comes
132
+ * from it, and RFC 6962 folding cannot be done from the path alone. Bitcoin proofs carry
133
+ * a direction bit per node instead, which is why lib/spv's shape does not transfer here.
134
+ *
135
+ * @param {Buffer} leafData - the leaf DATA, hashed here
136
+ * @param {Number} index
137
+ * @param {Number} leafCount
138
+ * @param {Array<Buffer>} path
139
+ * @returns {Buffer} the computed root
140
+ */
141
+ Merkle.foldPath = function (leafData, index, leafCount, path) {
142
+ $.checkArgument(Buffer.isBuffer(leafData), 'leafData must be a Buffer')
143
+ $.checkArgument(Number.isInteger(index) && index >= 0, 'index must be a non-negative integer')
144
+ $.checkArgument(Number.isInteger(leafCount) && leafCount > 0, 'leafCount must be a positive integer')
145
+ $.checkArgument(index < leafCount, 'index must be less than leafCount')
146
+ $.checkArgument(Array.isArray(path), 'path must be an array')
147
+
148
+ // RFC 6962 §2.1.2, transcribed rather than reinvented. The first attempt at this
149
+ // descended from the root by repeatedly splitting at the largest power of two, which
150
+ // is how root() builds the tree — but the audit path is ordered LEAF-UPWARD, so
151
+ // top-down consumption pairs each sibling at the wrong level. It round-tripped for
152
+ // powers of two and failed for 21 of the 45 leaves in trees of size 1..9.
153
+ //
154
+ // The RFC tracks two counters instead: fn, the node's index within its level, and sn,
155
+ // the index of the last node in that level. A node folds as a RIGHT child when fn is
156
+ // odd or when it is the last node in the level — which is what encodes "the rightmost
157
+ // leaf is never duplicated" without a direction flag in the path.
158
+ var r = Merkle.hashLeaf(leafData)
159
+ var fn = index
160
+ var sn = leafCount - 1
161
+
162
+ for (var i = 0; i < path.length; i++) {
163
+ if (sn === 0) {
164
+ throw new Error('audit path is too long for a tree of ' + leafCount + ' leaves')
165
+ }
166
+ if ((fn & 1) === 1 || fn === sn) {
167
+ r = Merkle.hashNode(path[i], r)
168
+ while ((fn & 1) === 0 && fn !== 0) {
169
+ fn >>= 1
170
+ sn >>= 1
171
+ }
172
+ } else {
173
+ r = Merkle.hashNode(r, path[i])
174
+ }
175
+ fn >>= 1
176
+ sn >>= 1
177
+ }
178
+
179
+ if (sn !== 0) {
180
+ throw new Error('audit path is too short for a tree of ' + leafCount + ' leaves')
181
+ }
182
+
183
+ return r
184
+ }
185
+
186
+ /**
187
+ * Does a leaf belong to the tree with this root?
188
+ *
189
+ * Strict boolean, and false on any malformed input rather than throwing — a caller
190
+ * writing `if (verifyInclusion(...))` must not receive a truthy object, which is the
191
+ * defect class this codebase has fixed most often.
192
+ *
193
+ * @param {Buffer} leafData
194
+ * @param {Number} index
195
+ * @param {Number} leafCount
196
+ * @param {Array<Buffer>} path
197
+ * @param {Buffer} expectedRoot
198
+ * @returns {Boolean}
199
+ */
200
+ Merkle.verifyInclusion = function (leafData, index, leafCount, path, expectedRoot) {
201
+ try {
202
+ if (!Buffer.isBuffer(expectedRoot)) return false
203
+ return Merkle.foldPath(leafData, index, leafCount, path).equals(expectedRoot)
204
+ } catch (e) {
205
+ return false
206
+ }
207
+ }
208
+
209
+ module.exports = Merkle