@forestrie/merklelog 0.4.0 → 0.5.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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/mmr/algorithms.d.ts +3 -0
- package/dist/mmr/algorithms.d.ts.map +1 -1
- package/dist/mmr/algorithms.js +54 -1
- package/dist/mmr/errors.d.ts +31 -0
- package/dist/mmr/errors.d.ts.map +1 -1
- package/dist/mmr/errors.js +41 -0
- package/package.json +1 -1
- package/src/index.ts +2 -0
- package/src/mmr/algorithms.ts +62 -0
- package/src/mmr/errors.ts +43 -0
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type { MassifLeafIndex, LocatedLeaf } from "./massifs/leafindex.js";
|
|
|
19
19
|
export { mmrIndex, massifFirstLeaf, leafMinusSpurSum } from "./mmr/index.js";
|
|
20
20
|
export { heightIndex, height, mmrIndexFromLeafIndex, leafIndex, mmrPosition, mmrSizeFromHeightIndex, leafCount, leafCountFromHeightIndex, } from "./mmr/math.js";
|
|
21
21
|
export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, consistentRoots, consistentRootsForSizes, indexConsistencyProof, type ConsistencyProof, } from "./mmr/algorithms.js";
|
|
22
|
-
export { ConsistencyShapeError, IncompleteTreeSize, ConsistencyPeakCountMismatch, ConsistencyPathLengthMismatch, ConsistencyRootMismatch, SizeMustIncrease, } from "./mmr/errors.js";
|
|
22
|
+
export { ConsistencyShapeError, IncompleteTreeSize, ConsistencyPeakCountMismatch, ConsistencyPathLengthMismatch, ConsistencyPathMalformed, ConsistencyRootMismatch, SizeMustIncrease, SizeNotUint64, } from "./mmr/errors.js";
|
|
23
23
|
export { createSyncHasher } from "./mmr/algorithms-sync.js";
|
|
24
24
|
export type { Proof, Peak } from "./mmr/types.js";
|
|
25
25
|
export type { Hasher } from "./mmr/types.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,SAAS,EACT,KAAK,EACL,KAAK,EACL,OAAO,EACP,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,6BAA6B,EAC7B,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG3E,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACL,WAAW,EACX,MAAM,EACN,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,sBAAsB,EACtB,SAAS,EACT,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,qBAAqB,EACrB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,4BAA4B,EAC5B,6BAA6B,EAC7B,uBAAuB,EACvB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAG3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,SAAS,GACV,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EACL,SAAS,EACT,KAAK,EACL,KAAK,EACL,OAAO,EACP,wBAAwB,EACxB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,6BAA6B,EAC7B,6BAA6B,EAC7B,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG3E,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EACL,WAAW,EACX,MAAM,EACN,qBAAqB,EACrB,SAAS,EACT,WAAW,EACX,sBAAsB,EACtB,SAAS,EACT,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,qBAAqB,EACrB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,4BAA4B,EAC5B,6BAA6B,EAC7B,wBAAwB,EACxB,uBAAuB,EACvB,gBAAgB,EAChB,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EACzB,cAAc,EACd,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ export { heightIndex, height, mmrIndexFromLeafIndex, leafIndex, mmrPosition, mmr
|
|
|
23
23
|
export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, consistentRoots, consistentRootsForSizes, indexConsistencyProof, } from "./mmr/algorithms.js";
|
|
24
24
|
// Typed proof-shape rejections raised by consistentRootsForSizes (names
|
|
25
25
|
// mirror the Solidity IUnivocityErrors members) — plan-2609-10 s4.3, FOR-568.
|
|
26
|
-
export { ConsistencyShapeError, IncompleteTreeSize, ConsistencyPeakCountMismatch, ConsistencyPathLengthMismatch, ConsistencyRootMismatch, SizeMustIncrease, } from "./mmr/errors.js";
|
|
26
|
+
export { ConsistencyShapeError, IncompleteTreeSize, ConsistencyPeakCountMismatch, ConsistencyPathLengthMismatch, ConsistencyPathMalformed, ConsistencyRootMismatch, SizeMustIncrease, SizeNotUint64, } from "./mmr/errors.js";
|
|
27
27
|
export { createSyncHasher } from "./mmr/algorithms-sync.js";
|
|
28
28
|
// MMR proof building (pure; store-agnostic) — plan-2607-15 §4 (go: mmr/proof.go, peaks.go)
|
|
29
29
|
export { inclusionProof, inclusionProofPath, inclusionProofLocalExtend, peakMMRIndexes, peaksBitmap, peakIndexForLeafProof, mmrSizeForLeafCount, indexHeight, firstMMRSize, massifIndexFromMMRIndex, peakStackMap, } from "./mmr/proof.js";
|
package/dist/mmr/algorithms.d.ts
CHANGED
|
@@ -129,10 +129,13 @@ export declare function consistentRoots(hasher: Hasher, ifrom: bigint, accumulat
|
|
|
129
129
|
* any), and `expectedRight`, the number of MMR(sizeTo) peaks the prover must
|
|
130
130
|
* supply as right peaks. `roots` followed by those right peaks is the
|
|
131
131
|
* accumulator of MMR(sizeTo).
|
|
132
|
+
* @throws {SizeNotUint64} if either size is outside `[0, 2^64 - 1]`
|
|
132
133
|
* @throws {SizeMustIncrease} if `sizeTo <= sizeFrom`
|
|
133
134
|
* @throws {IncompleteTreeSize} if `sizeTo` is not a complete MMR size
|
|
134
135
|
* @throws {ConsistencyPeakCountMismatch} if `accumulatorFrom` or `paths` does
|
|
135
136
|
* not have one entry per origin peak
|
|
137
|
+
* @throws {ConsistencyPathMalformed} if `paths` is not a dense array of
|
|
138
|
+
* `Uint8Array[]`
|
|
136
139
|
* @throws {ConsistencyPathLengthMismatch} if a path length differs from the
|
|
137
140
|
* length the two sizes imply
|
|
138
141
|
* @throws {ConsistencyRootMismatch} if two paths under one target peak produce
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"algorithms.d.ts","sourceRoot":"","sources":["../../src/mmr/algorithms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"algorithms.d.ts","sourceRoot":"","sources":["../../src/mmr/algorithms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAsCpB;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EAAE,GAClB,OAAO,CAAC,UAAU,CAAC,CAwBrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,CAAC,CAkCrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,UAAU,EACpB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,UAAU,EAAE,EAC7B,KAAK,EAAE,UAAU,EAAE,EAAE,GACpB,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,UAAU,EAAE,EAC7B,KAAK,EAAE,UAAU,EAAE,EAAE,GACpB,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA2FzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,UAAU,EAAE,EACvB,KAAK,EAAE,UAAU,EAAE,EAAE,EACrB,OAAO,EAAE,UAAU,EAAE,GACpB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAqBrD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAMlB"}
|
package/dist/mmr/algorithms.js
CHANGED
|
@@ -11,10 +11,17 @@
|
|
|
11
11
|
* https://raw.githubusercontent.com/robinbryce/draft-bryce-cose-receipts-mmr-profile/refs/heads/main/draft-bryce-cose-receipts-mmr-profile.md
|
|
12
12
|
*/
|
|
13
13
|
import { inclusionProof, peakMMRIndexes, bitLength, popcount, peaksBitmap, mmrSizeForLeafCount, } from "./proof.js";
|
|
14
|
-
import { ConsistencyPathLengthMismatch, ConsistencyPeakCountMismatch, ConsistencyRootMismatch, IncompleteTreeSize, SizeMustIncrease, } from "./errors.js";
|
|
14
|
+
import { ConsistencyPathLengthMismatch, ConsistencyPathMalformed, ConsistencyPeakCountMismatch, ConsistencyRootMismatch, IncompleteTreeSize, SizeMustIncrease, SizeNotUint64, } from "./errors.js";
|
|
15
15
|
import { Uint64 } from "../uint64/index.js";
|
|
16
16
|
import { heightIndex } from "./math.js";
|
|
17
17
|
import { arraysEqual } from "../utils/arrays.js";
|
|
18
|
+
/**
|
|
19
|
+
* Upper bound of the uint64 domain the MMR profile's sizes live in: Solidity
|
|
20
|
+
* and Go take every size as `uint64`, and a CBOR unsigned integer caps here
|
|
21
|
+
* too. `bigint` carries no such bound, so {@link consistentRootsForSizes}
|
|
22
|
+
* states it.
|
|
23
|
+
*/
|
|
24
|
+
const MAX_UINT64 = (1n << 64n) - 1n;
|
|
18
25
|
/**
|
|
19
26
|
* Encodes a Uint64 as 8 bytes big-endian.
|
|
20
27
|
*
|
|
@@ -208,16 +215,27 @@ export async function consistentRoots(hasher, ifrom, accumulatorFrom, paths) {
|
|
|
208
215
|
* any), and `expectedRight`, the number of MMR(sizeTo) peaks the prover must
|
|
209
216
|
* supply as right peaks. `roots` followed by those right peaks is the
|
|
210
217
|
* accumulator of MMR(sizeTo).
|
|
218
|
+
* @throws {SizeNotUint64} if either size is outside `[0, 2^64 - 1]`
|
|
211
219
|
* @throws {SizeMustIncrease} if `sizeTo <= sizeFrom`
|
|
212
220
|
* @throws {IncompleteTreeSize} if `sizeTo` is not a complete MMR size
|
|
213
221
|
* @throws {ConsistencyPeakCountMismatch} if `accumulatorFrom` or `paths` does
|
|
214
222
|
* not have one entry per origin peak
|
|
223
|
+
* @throws {ConsistencyPathMalformed} if `paths` is not a dense array of
|
|
224
|
+
* `Uint8Array[]`
|
|
215
225
|
* @throws {ConsistencyPathLengthMismatch} if a path length differs from the
|
|
216
226
|
* length the two sizes imply
|
|
217
227
|
* @throws {ConsistencyRootMismatch} if two paths under one target peak produce
|
|
218
228
|
* different roots
|
|
219
229
|
*/
|
|
220
230
|
export async function consistentRootsForSizes(hasher, sizeFrom, sizeTo, accumulatorFrom, paths) {
|
|
231
|
+
// The sizes are uint64 in Solidity and Go; `bigint` carries no such bound,
|
|
232
|
+
// so it is stated here before any arithmetic reads them.
|
|
233
|
+
if (sizeFrom < 0n || sizeFrom > MAX_UINT64) {
|
|
234
|
+
throw new SizeNotUint64("sizeFrom", sizeFrom);
|
|
235
|
+
}
|
|
236
|
+
if (sizeTo < 0n || sizeTo > MAX_UINT64) {
|
|
237
|
+
throw new SizeNotUint64("sizeTo", sizeTo);
|
|
238
|
+
}
|
|
221
239
|
if (sizeTo <= sizeFrom) {
|
|
222
240
|
throw new SizeMustIncrease(sizeFrom, sizeTo);
|
|
223
241
|
}
|
|
@@ -234,9 +252,13 @@ export async function consistentRootsForSizes(hasher, sizeFrom, sizeTo, accumula
|
|
|
234
252
|
if (accumulatorFrom.length !== n) {
|
|
235
253
|
throw new ConsistencyPeakCountMismatch(n, accumulatorFrom.length);
|
|
236
254
|
}
|
|
255
|
+
if (!Array.isArray(paths)) {
|
|
256
|
+
throw new ConsistencyPathMalformed(`not an array (got ${typeName(paths)})`);
|
|
257
|
+
}
|
|
237
258
|
if (paths.length !== n) {
|
|
238
259
|
throw new ConsistencyPeakCountMismatch(n, paths.length);
|
|
239
260
|
}
|
|
261
|
+
assertDensePaths(paths);
|
|
240
262
|
const nto = popcount(to);
|
|
241
263
|
if (n === 0) {
|
|
242
264
|
return { roots: [], expectedRight: nto };
|
|
@@ -364,3 +386,34 @@ export function indexConsistencyProof(get, mmrIndexA, mmrIndexB) {
|
|
|
364
386
|
}
|
|
365
387
|
return { mmrSizeA: mmrIndexA + 1n, mmrSizeB: mmrIndexB + 1n, paths };
|
|
366
388
|
}
|
|
389
|
+
/** Name the value found where a path or a path element was required. */
|
|
390
|
+
function typeName(value) {
|
|
391
|
+
if (value === null)
|
|
392
|
+
return "null";
|
|
393
|
+
if (Array.isArray(value))
|
|
394
|
+
return "an array";
|
|
395
|
+
return typeof value;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Require `paths` to be a DENSE array of `Uint8Array[]`: no holes, and every
|
|
399
|
+
* element an array of `Uint8Array`. A hole and an out-of-type entry both
|
|
400
|
+
* survive the peak COUNT check and would otherwise reach the hashing loop,
|
|
401
|
+
* where they surface as a bare `TypeError` outside the
|
|
402
|
+
* `ConsistencyShapeError` family (review finding C6).
|
|
403
|
+
*/
|
|
404
|
+
function assertDensePaths(paths) {
|
|
405
|
+
for (let i = 0; i < paths.length; i++) {
|
|
406
|
+
if (!(i in paths)) {
|
|
407
|
+
throw new ConsistencyPathMalformed("missing (the array is sparse)", i);
|
|
408
|
+
}
|
|
409
|
+
const path = paths[i];
|
|
410
|
+
if (!Array.isArray(path)) {
|
|
411
|
+
throw new ConsistencyPathMalformed(`not an array (got ${typeName(path)})`, i);
|
|
412
|
+
}
|
|
413
|
+
for (let j = 0; j < path.length; j++) {
|
|
414
|
+
if (!(j in path) || !(path[j] instanceof Uint8Array)) {
|
|
415
|
+
throw new ConsistencyPathMalformed(`element ${j} is not a Uint8Array (got ${typeName(path[j])})`, i);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
package/dist/mmr/errors.d.ts
CHANGED
|
@@ -66,4 +66,35 @@ export declare class SizeMustIncrease extends ConsistencyShapeError {
|
|
|
66
66
|
readonly sizeTo: bigint;
|
|
67
67
|
constructor(sizeFrom: bigint, sizeTo: bigint);
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* A size is outside the uint64 domain the MMR profile's sizes live in.
|
|
71
|
+
*
|
|
72
|
+
* Solidity and Go take these sizes as `uint64`, so the domain is part of
|
|
73
|
+
* their signatures; TypeScript's `bigint` has no bound of its own. Without
|
|
74
|
+
* this check a size at or above 2^64 folds here and nowhere else: the
|
|
75
|
+
* position prefix `hash_pospair64` writes is truncated to eight bytes, so
|
|
76
|
+
* the hashes computed above the bound are not the ones the other two
|
|
77
|
+
* implementations would compute, and a size that large is not one a CBOR
|
|
78
|
+
* unsigned integer can carry or the contract can store either.
|
|
79
|
+
*/
|
|
80
|
+
export declare class SizeNotUint64 extends ConsistencyShapeError {
|
|
81
|
+
readonly which: "sizeFrom" | "sizeTo";
|
|
82
|
+
readonly size: bigint;
|
|
83
|
+
constructor(which: "sizeFrom" | "sizeTo", size: bigint);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `paths` is not a dense array of `Uint8Array[]`.
|
|
87
|
+
*
|
|
88
|
+
* A hole (a sparse array), a `null`/`undefined` entry, a path that is not an
|
|
89
|
+
* array, or a path element that is not a `Uint8Array` all survive the peak
|
|
90
|
+
* COUNT check and then surface as a bare `TypeError` out of the fold, which
|
|
91
|
+
* escapes the {@link ConsistencyShapeError} family callers are told to
|
|
92
|
+
* switch on. The shape of the proof material is a proof-shape condition like
|
|
93
|
+
* any other, so it is reported as one.
|
|
94
|
+
*/
|
|
95
|
+
export declare class ConsistencyPathMalformed extends ConsistencyShapeError {
|
|
96
|
+
readonly detail: string;
|
|
97
|
+
readonly index?: number | undefined;
|
|
98
|
+
constructor(detail: string, index?: number | undefined);
|
|
99
|
+
}
|
|
69
100
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/mmr/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/mmr/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,8DAA8D;AAC9D,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,qBAAqB;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIlC;AAED;;;GAGG;AACH,qBAAa,4BAA6B,SAAQ,qBAAqB;IAEnE,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAK1B;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,qBAAqB;IAEpE,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAFd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAK1B;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,qBAAqB;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CAInC;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,qBAAqB;IAEvD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAK1B"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/mmr/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,8DAA8D;AAC9D,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,qBAAqB;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIlC;AAED;;;GAGG;AACH,qBAAa,4BAA6B,SAAQ,qBAAqB;IAEnE,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAK1B;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,qBAAqB;IAEpE,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAFd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAK1B;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,qBAAqB;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CAInC;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,qBAAqB;IAEvD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAK1B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,aAAc,SAAQ,qBAAqB;IAEpD,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBADZ,KAAK,EAAE,UAAU,GAAG,QAAQ,EAC5B,IAAI,EAAE,MAAM;CAKxB;AAED;;;;;;;;;GASG;AACH,qBAAa,wBAAyB,SAAQ,qBAAqB;IAE/D,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM;gBADd,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,YAAA;CAO1B"}
|
package/dist/mmr/errors.js
CHANGED
|
@@ -93,3 +93,44 @@ export class SizeMustIncrease extends ConsistencyShapeError {
|
|
|
93
93
|
this.name = "SizeMustIncrease";
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* A size is outside the uint64 domain the MMR profile's sizes live in.
|
|
98
|
+
*
|
|
99
|
+
* Solidity and Go take these sizes as `uint64`, so the domain is part of
|
|
100
|
+
* their signatures; TypeScript's `bigint` has no bound of its own. Without
|
|
101
|
+
* this check a size at or above 2^64 folds here and nowhere else: the
|
|
102
|
+
* position prefix `hash_pospair64` writes is truncated to eight bytes, so
|
|
103
|
+
* the hashes computed above the bound are not the ones the other two
|
|
104
|
+
* implementations would compute, and a size that large is not one a CBOR
|
|
105
|
+
* unsigned integer can carry or the contract can store either.
|
|
106
|
+
*/
|
|
107
|
+
export class SizeNotUint64 extends ConsistencyShapeError {
|
|
108
|
+
which;
|
|
109
|
+
size;
|
|
110
|
+
constructor(which, size) {
|
|
111
|
+
super(`${which} (${size}) is outside the uint64 range [0, 2^64 - 1]`);
|
|
112
|
+
this.which = which;
|
|
113
|
+
this.size = size;
|
|
114
|
+
this.name = "SizeNotUint64";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* `paths` is not a dense array of `Uint8Array[]`.
|
|
119
|
+
*
|
|
120
|
+
* A hole (a sparse array), a `null`/`undefined` entry, a path that is not an
|
|
121
|
+
* array, or a path element that is not a `Uint8Array` all survive the peak
|
|
122
|
+
* COUNT check and then surface as a bare `TypeError` out of the fold, which
|
|
123
|
+
* escapes the {@link ConsistencyShapeError} family callers are told to
|
|
124
|
+
* switch on. The shape of the proof material is a proof-shape condition like
|
|
125
|
+
* any other, so it is reported as one.
|
|
126
|
+
*/
|
|
127
|
+
export class ConsistencyPathMalformed extends ConsistencyShapeError {
|
|
128
|
+
detail;
|
|
129
|
+
index;
|
|
130
|
+
constructor(detail, index) {
|
|
131
|
+
super(index === undefined ? `paths: ${detail}` : `path ${index}: ${detail}`);
|
|
132
|
+
this.detail = detail;
|
|
133
|
+
this.index = index;
|
|
134
|
+
this.name = "ConsistencyPathMalformed";
|
|
135
|
+
}
|
|
136
|
+
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -82,8 +82,10 @@ export {
|
|
|
82
82
|
IncompleteTreeSize,
|
|
83
83
|
ConsistencyPeakCountMismatch,
|
|
84
84
|
ConsistencyPathLengthMismatch,
|
|
85
|
+
ConsistencyPathMalformed,
|
|
85
86
|
ConsistencyRootMismatch,
|
|
86
87
|
SizeMustIncrease,
|
|
88
|
+
SizeNotUint64,
|
|
87
89
|
} from "./mmr/errors.js";
|
|
88
90
|
export { createSyncHasher } from "./mmr/algorithms-sync.js";
|
|
89
91
|
export type { Proof, Peak } from "./mmr/types.js";
|
package/src/mmr/algorithms.ts
CHANGED
|
@@ -23,15 +23,25 @@ import {
|
|
|
23
23
|
} from "./proof.js";
|
|
24
24
|
import {
|
|
25
25
|
ConsistencyPathLengthMismatch,
|
|
26
|
+
ConsistencyPathMalformed,
|
|
26
27
|
ConsistencyPeakCountMismatch,
|
|
27
28
|
ConsistencyRootMismatch,
|
|
28
29
|
IncompleteTreeSize,
|
|
29
30
|
SizeMustIncrease,
|
|
31
|
+
SizeNotUint64,
|
|
30
32
|
} from "./errors.js";
|
|
31
33
|
import { Uint64 } from "../uint64/index.js";
|
|
32
34
|
import { heightIndex } from "./math.js";
|
|
33
35
|
import { arraysEqual } from "../utils/arrays.js";
|
|
34
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Upper bound of the uint64 domain the MMR profile's sizes live in: Solidity
|
|
39
|
+
* and Go take every size as `uint64`, and a CBOR unsigned integer caps here
|
|
40
|
+
* too. `bigint` carries no such bound, so {@link consistentRootsForSizes}
|
|
41
|
+
* states it.
|
|
42
|
+
*/
|
|
43
|
+
const MAX_UINT64 = (1n << 64n) - 1n;
|
|
44
|
+
|
|
35
45
|
/**
|
|
36
46
|
* Encodes a Uint64 as 8 bytes big-endian.
|
|
37
47
|
*
|
|
@@ -282,10 +292,13 @@ export async function consistentRoots(
|
|
|
282
292
|
* any), and `expectedRight`, the number of MMR(sizeTo) peaks the prover must
|
|
283
293
|
* supply as right peaks. `roots` followed by those right peaks is the
|
|
284
294
|
* accumulator of MMR(sizeTo).
|
|
295
|
+
* @throws {SizeNotUint64} if either size is outside `[0, 2^64 - 1]`
|
|
285
296
|
* @throws {SizeMustIncrease} if `sizeTo <= sizeFrom`
|
|
286
297
|
* @throws {IncompleteTreeSize} if `sizeTo` is not a complete MMR size
|
|
287
298
|
* @throws {ConsistencyPeakCountMismatch} if `accumulatorFrom` or `paths` does
|
|
288
299
|
* not have one entry per origin peak
|
|
300
|
+
* @throws {ConsistencyPathMalformed} if `paths` is not a dense array of
|
|
301
|
+
* `Uint8Array[]`
|
|
289
302
|
* @throws {ConsistencyPathLengthMismatch} if a path length differs from the
|
|
290
303
|
* length the two sizes imply
|
|
291
304
|
* @throws {ConsistencyRootMismatch} if two paths under one target peak produce
|
|
@@ -298,6 +311,14 @@ export async function consistentRootsForSizes(
|
|
|
298
311
|
accumulatorFrom: Uint8Array[],
|
|
299
312
|
paths: Uint8Array[][],
|
|
300
313
|
): Promise<{ roots: Uint8Array[]; expectedRight: number }> {
|
|
314
|
+
// The sizes are uint64 in Solidity and Go; `bigint` carries no such bound,
|
|
315
|
+
// so it is stated here before any arithmetic reads them.
|
|
316
|
+
if (sizeFrom < 0n || sizeFrom > MAX_UINT64) {
|
|
317
|
+
throw new SizeNotUint64("sizeFrom", sizeFrom);
|
|
318
|
+
}
|
|
319
|
+
if (sizeTo < 0n || sizeTo > MAX_UINT64) {
|
|
320
|
+
throw new SizeNotUint64("sizeTo", sizeTo);
|
|
321
|
+
}
|
|
301
322
|
if (sizeTo <= sizeFrom) {
|
|
302
323
|
throw new SizeMustIncrease(sizeFrom, sizeTo);
|
|
303
324
|
}
|
|
@@ -314,9 +335,13 @@ export async function consistentRootsForSizes(
|
|
|
314
335
|
if (accumulatorFrom.length !== n) {
|
|
315
336
|
throw new ConsistencyPeakCountMismatch(n, accumulatorFrom.length);
|
|
316
337
|
}
|
|
338
|
+
if (!Array.isArray(paths)) {
|
|
339
|
+
throw new ConsistencyPathMalformed(`not an array (got ${typeName(paths)})`);
|
|
340
|
+
}
|
|
317
341
|
if (paths.length !== n) {
|
|
318
342
|
throw new ConsistencyPeakCountMismatch(n, paths.length);
|
|
319
343
|
}
|
|
344
|
+
assertDensePaths(paths);
|
|
320
345
|
const nto = popcount(to);
|
|
321
346
|
if (n === 0) {
|
|
322
347
|
return { roots: [], expectedRight: nto };
|
|
@@ -469,3 +494,40 @@ export function indexConsistencyProof(
|
|
|
469
494
|
}
|
|
470
495
|
return { mmrSizeA: mmrIndexA + 1n, mmrSizeB: mmrIndexB + 1n, paths };
|
|
471
496
|
}
|
|
497
|
+
|
|
498
|
+
/** Name the value found where a path or a path element was required. */
|
|
499
|
+
function typeName(value: unknown): string {
|
|
500
|
+
if (value === null) return "null";
|
|
501
|
+
if (Array.isArray(value)) return "an array";
|
|
502
|
+
return typeof value;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Require `paths` to be a DENSE array of `Uint8Array[]`: no holes, and every
|
|
507
|
+
* element an array of `Uint8Array`. A hole and an out-of-type entry both
|
|
508
|
+
* survive the peak COUNT check and would otherwise reach the hashing loop,
|
|
509
|
+
* where they surface as a bare `TypeError` outside the
|
|
510
|
+
* `ConsistencyShapeError` family (review finding C6).
|
|
511
|
+
*/
|
|
512
|
+
function assertDensePaths(paths: Uint8Array[][]): void {
|
|
513
|
+
for (let i = 0; i < paths.length; i++) {
|
|
514
|
+
if (!(i in paths)) {
|
|
515
|
+
throw new ConsistencyPathMalformed("missing (the array is sparse)", i);
|
|
516
|
+
}
|
|
517
|
+
const path = paths[i] as unknown;
|
|
518
|
+
if (!Array.isArray(path)) {
|
|
519
|
+
throw new ConsistencyPathMalformed(
|
|
520
|
+
`not an array (got ${typeName(path)})`,
|
|
521
|
+
i,
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
for (let j = 0; j < path.length; j++) {
|
|
525
|
+
if (!(j in path) || !((path[j] as unknown) instanceof Uint8Array)) {
|
|
526
|
+
throw new ConsistencyPathMalformed(
|
|
527
|
+
`element ${j} is not a Uint8Array (got ${typeName(path[j])})`,
|
|
528
|
+
i,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
package/src/mmr/errors.ts
CHANGED
|
@@ -91,3 +91,46 @@ export class SizeMustIncrease extends ConsistencyShapeError {
|
|
|
91
91
|
this.name = "SizeMustIncrease";
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A size is outside the uint64 domain the MMR profile's sizes live in.
|
|
97
|
+
*
|
|
98
|
+
* Solidity and Go take these sizes as `uint64`, so the domain is part of
|
|
99
|
+
* their signatures; TypeScript's `bigint` has no bound of its own. Without
|
|
100
|
+
* this check a size at or above 2^64 folds here and nowhere else: the
|
|
101
|
+
* position prefix `hash_pospair64` writes is truncated to eight bytes, so
|
|
102
|
+
* the hashes computed above the bound are not the ones the other two
|
|
103
|
+
* implementations would compute, and a size that large is not one a CBOR
|
|
104
|
+
* unsigned integer can carry or the contract can store either.
|
|
105
|
+
*/
|
|
106
|
+
export class SizeNotUint64 extends ConsistencyShapeError {
|
|
107
|
+
constructor(
|
|
108
|
+
readonly which: "sizeFrom" | "sizeTo",
|
|
109
|
+
readonly size: bigint,
|
|
110
|
+
) {
|
|
111
|
+
super(`${which} (${size}) is outside the uint64 range [0, 2^64 - 1]`);
|
|
112
|
+
this.name = "SizeNotUint64";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* `paths` is not a dense array of `Uint8Array[]`.
|
|
118
|
+
*
|
|
119
|
+
* A hole (a sparse array), a `null`/`undefined` entry, a path that is not an
|
|
120
|
+
* array, or a path element that is not a `Uint8Array` all survive the peak
|
|
121
|
+
* COUNT check and then surface as a bare `TypeError` out of the fold, which
|
|
122
|
+
* escapes the {@link ConsistencyShapeError} family callers are told to
|
|
123
|
+
* switch on. The shape of the proof material is a proof-shape condition like
|
|
124
|
+
* any other, so it is reported as one.
|
|
125
|
+
*/
|
|
126
|
+
export class ConsistencyPathMalformed extends ConsistencyShapeError {
|
|
127
|
+
constructor(
|
|
128
|
+
readonly detail: string,
|
|
129
|
+
readonly index?: number,
|
|
130
|
+
) {
|
|
131
|
+
super(
|
|
132
|
+
index === undefined ? `paths: ${detail}` : `path ${index}: ${detail}`,
|
|
133
|
+
);
|
|
134
|
+
this.name = "ConsistencyPathMalformed";
|
|
135
|
+
}
|
|
136
|
+
}
|