@forestrie/merklelog 0.3.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 CHANGED
@@ -18,10 +18,11 @@ export { openMassifLeafIndex, MissingIndexError } from "./massifs/leafindex.js";
18
18
  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
- export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, consistentRoots, indexConsistencyProof, type ConsistencyProof, } from "./mmr/algorithms.js";
21
+ export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, consistentRoots, consistentRootsForSizes, indexConsistencyProof, type ConsistencyProof, } from "./mmr/algorithms.js";
22
+ export { ConsistencyShapeError, IncompleteTreeSize, ConsistencyPeakCountMismatch, ConsistencyPathLengthMismatch, ConsistencyPathMalformed, ConsistencyRootMismatch, SizeMustIncrease, SizeNotUint64, } from "./mmr/errors.js";
22
23
  export { createSyncHasher } from "./mmr/algorithms-sync.js";
23
24
  export type { Proof, Peak } from "./mmr/types.js";
24
25
  export type { Hasher } from "./mmr/types.js";
25
- export { inclusionProof, inclusionProofPath, inclusionProofLocalExtend, peakMMRIndexes, peaksBitmap, peakIndexForLeafProof, indexHeight, firstMMRSize, massifIndexFromMMRIndex, peakStackMap, } from "./mmr/proof.js";
26
+ export { inclusionProof, inclusionProofPath, inclusionProofLocalExtend, peakMMRIndexes, peaksBitmap, peakIndexForLeafProof, mmrSizeForLeafCount, indexHeight, firstMMRSize, massifIndexFromMMRIndex, peakStackMap, } from "./mmr/proof.js";
26
27
  export type { NodeGetter, ConsistencyProofLocal } from "./mmr/proof.js";
27
28
  //# sourceMappingURL=index.d.ts.map
@@ -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,qBAAqB,EACrB,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,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,WAAW,EACX,YAAY,EACZ,uBAAuB,EACvB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC"}
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
@@ -20,7 +20,10 @@ export { openMassifLeafIndex, MissingIndexError } from "./massifs/leafindex.js";
20
20
  // Export mmr module
21
21
  export { mmrIndex, massifFirstLeaf, leafMinusSpurSum } from "./mmr/index.js";
22
22
  export { heightIndex, height, mmrIndexFromLeafIndex, leafIndex, mmrPosition, mmrSizeFromHeightIndex, leafCount, leafCountFromHeightIndex, } from "./mmr/math.js";
23
- export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, consistentRoots, indexConsistencyProof, } from "./mmr/algorithms.js";
23
+ export { bagPeaks, calculateRoot, verifyInclusion, verifyConsistency, consistentRoots, consistentRootsForSizes, indexConsistencyProof, } from "./mmr/algorithms.js";
24
+ // Typed proof-shape rejections raised by consistentRootsForSizes (names
25
+ // mirror the Solidity IUnivocityErrors members) — plan-2609-10 s4.3, FOR-568.
26
+ export { ConsistencyShapeError, IncompleteTreeSize, ConsistencyPeakCountMismatch, ConsistencyPathLengthMismatch, ConsistencyPathMalformed, ConsistencyRootMismatch, SizeMustIncrease, SizeNotUint64, } from "./mmr/errors.js";
24
27
  export { createSyncHasher } from "./mmr/algorithms-sync.js";
25
28
  // MMR proof building (pure; store-agnostic) — plan-2607-15 §4 (go: mmr/proof.go, peaks.go)
26
- export { inclusionProof, inclusionProofPath, inclusionProofLocalExtend, peakMMRIndexes, peaksBitmap, peakIndexForLeafProof, indexHeight, firstMMRSize, massifIndexFromMMRIndex, peakStackMap, } from "./mmr/proof.js";
29
+ export { inclusionProof, inclusionProofPath, inclusionProofLocalExtend, peakMMRIndexes, peaksBitmap, peakIndexForLeafProof, mmrSizeForLeafCount, indexHeight, firstMMRSize, massifIndexFromMMRIndex, peakStackMap, } from "./mmr/proof.js";
@@ -79,28 +79,117 @@ export interface ConsistencyProof {
79
79
  * duplicate roots collapse (many old peaks share one new peak). Requires
80
80
  * one path per MMR(A) peak (draft: `len(peaks(ifrom)) == len(accumulatorfrom)`).
81
81
  *
82
+ * This fold does not enforce the proof shape the two sizes imply: it reads
83
+ * whatever path lengths it is given and collapses whatever roots coincide.
84
+ * Use {@link consistentRootsForSizes} for verification — it checks each path
85
+ * against the length MMR(A) -> MMR(B) fixes and requires the paths under one
86
+ * target peak to agree.
87
+ *
82
88
  * @param ifrom - last node index of the complete MMR(A) (`mmrSizeA - 1`)
83
89
  * @param accumulatorFrom - MMR(A) peak values, descending height order
84
90
  * @param paths - inclusion path per peak, proven in MMR(B)
85
91
  */
86
92
  export declare function consistentRoots(hasher: Hasher, ifrom: bigint, accumulatorFrom: Uint8Array[], paths: Uint8Array[][]): Promise<Uint8Array[]>;
93
+ /**
94
+ * Produce the peaks of MMR(sizeTo) that `paths` prove from the peaks of
95
+ * MMR(sizeFrom), requiring the paths to have exactly the shape the two sizes
96
+ * imply (draft-bryce-cose-receipts-mmr-profile, "Verifying the Receipt of
97
+ * consistency", with the draft's SHOULD on path lengths enforced in the same
98
+ * pass). Line-for-line port of the reference `consistent_roots_for_sizes`
99
+ * (algorithms.py) and of Solidity `consistentRootsForSizes`
100
+ * (univocity `src/algorithms/consistentRoots.sol`).
101
+ *
102
+ * Sizes are node counts (the MMR ending at index `i` has `i + 1` nodes).
103
+ * `sizeFrom` MUST be the size of the state the verifier already trusts; taken
104
+ * from the proof instead, the check is void. Only `sizeTo` is required to be
105
+ * a complete MMR size: every trusted size was itself a checked target.
106
+ *
107
+ * For a complete MMR the set bits of `peaksBitmap(size)` are the peak heights,
108
+ * high to low, which is accumulator order. Let `split` be the highest bit on
109
+ * which the two bitmaps differ; as `sizeTo > sizeFrom` the target has it and
110
+ * the origin does not. An origin peak above `split` is also a peak of the
111
+ * target: its path is empty and it is returned unchanged. Every origin peak
112
+ * below `split` is committed by the target peak of height `split`: its path
113
+ * has length `split - h`, and every such path must prove the same root. The
114
+ * target's remaining peaks lie below every origin peak, so no path reaches
115
+ * them; the prover supplies them separately and their count is returned.
116
+ *
117
+ * Because the shape is fixed by the sizes alone, no peak index list and no
118
+ * per-hop bookkeeping beyond the hash itself is needed: the bitmaps are
119
+ * iterated directly.
120
+ *
121
+ * @param hasher - cryptographic hasher instance
122
+ * @param sizeFrom - node count of the trusted origin state (0 for an empty log)
123
+ * @param sizeTo - node count of the target state; must exceed `sizeFrom` and
124
+ * must be a complete MMR size
125
+ * @param accumulatorFrom - peaks of MMR(sizeFrom), descending height order
126
+ * @param paths - one path per origin peak, in the same order
127
+ * @returns `roots`, the peaks of MMR(sizeTo) proven from the origin peaks in
128
+ * descending height order (the unchanged peaks, then the one proven root if
129
+ * any), and `expectedRight`, the number of MMR(sizeTo) peaks the prover must
130
+ * supply as right peaks. `roots` followed by those right peaks is the
131
+ * accumulator of MMR(sizeTo).
132
+ * @throws {SizeNotUint64} if either size is outside `[0, 2^64 - 1]`
133
+ * @throws {SizeMustIncrease} if `sizeTo <= sizeFrom`
134
+ * @throws {IncompleteTreeSize} if `sizeTo` is not a complete MMR size
135
+ * @throws {ConsistencyPeakCountMismatch} if `accumulatorFrom` or `paths` does
136
+ * not have one entry per origin peak
137
+ * @throws {ConsistencyPathMalformed} if `paths` is not a dense array of
138
+ * `Uint8Array[]`
139
+ * @throws {ConsistencyPathLengthMismatch} if a path length differs from the
140
+ * length the two sizes imply
141
+ * @throws {ConsistencyRootMismatch} if two paths under one target peak produce
142
+ * different roots
143
+ */
144
+ export declare function consistentRootsForSizes(hasher: Hasher, sizeFrom: bigint, sizeTo: bigint, accumulatorFrom: Uint8Array[], paths: Uint8Array[][]): Promise<{
145
+ roots: Uint8Array[];
146
+ expectedRight: number;
147
+ }>;
87
148
  /**
88
149
  * Verify MMR(A) is a committed prefix of MMR(B)
89
150
  * (draft-bryce "Verifying the Receipt of consistency";
90
151
  * go-merklelog `VerifyConsistency`).
91
152
  *
153
+ * BOTH sizes are CALLER-SUPPLIED trusted state (ADR-0066 D5.4), never read
154
+ * off the proof: `sizeFrom` is the size of the state `peaksFrom` is the
155
+ * accumulator of, and `sizeTo` the size `peaksTo` is claimed to be the
156
+ * accumulator of. The proof contributes only `paths`. Taking either size
157
+ * from the proof makes the check void: the fold constrains a declared
158
+ * `sizeFrom` by the origin peak COUNT alone, which a whole family of sizes
159
+ * share, so one set of paths and peaks verified at many size pairs — with
160
+ * the draft's own MMR(11) -> MMR(39) material, 24 distinct pairs, of which
161
+ * one is the true relation. `indexConsistencyProof` still returns the
162
+ * {@link ConsistencyProof} wire type with its declared sizes; a caller that
163
+ * relays that type passes `proof.paths` here and supplies the sizes it
164
+ * trusts, comparing the declared sizes with them separately if it wants to
165
+ * report a disagreement.
166
+ *
167
+ * Routed through {@link consistentRootsForSizes}, so the proof must have the
168
+ * shape `sizeFrom -> sizeTo` implies: one path per MMR(A) peak, empty above
169
+ * the split and of length `split - h` below it, with every path below the
170
+ * split proving the same root. The proven roots must then be the leading
171
+ * entries of `peaksTo`, and `peaksTo` must hold exactly those plus the
172
+ * `expectedRight` right peaks no path reaches — so a truncated or padded
173
+ * target accumulator is rejected on its length alone.
174
+ *
92
175
  * Replaces the plan-0027 always-true stub (FOR-368 Phase 1,
93
176
  * plan-2607-29): the previous signature took two inclusion proofs and
94
177
  * returned true unconditionally; no caller existed.
95
178
  *
96
- * @param proof - consistency proof `mmrSizeA -> mmrSizeB`
179
+ * @param sizeFrom - TRUSTED node count of MMR(A)
180
+ * @param sizeTo - TRUSTED node count of MMR(B)
97
181
  * @param peaksFrom - TRUSTED MMR(A) accumulator (e.g. a signed checkpoint
98
182
  * payload), descending height order
183
+ * @param paths - one inclusion path per MMR(A) peak, in the same order
99
184
  * @param peaksTo - MMR(B) accumulator to prove against (e.g. an anchored
100
185
  * on-chain state), descending height order
101
- * @returns ok, with the MMR(B) accumulator on success
186
+ * @returns ok, with the MMR(B) accumulator on success. A value that does not
187
+ * match `peaksTo` returns `{ok: false, accumulator: []}`; a proof that does
188
+ * not have the shape the sizes imply throws one of the typed errors in
189
+ * `./errors.js` (all `ConsistencyShapeError` subclasses), which callers
190
+ * report as a malformed proof rather than a failed comparison.
102
191
  */
103
- export declare function verifyConsistency(hasher: Hasher, proof: ConsistencyProof, peaksFrom: Uint8Array[], peaksTo: Uint8Array[]): Promise<{
192
+ export declare function verifyConsistency(hasher: Hasher, sizeFrom: bigint, sizeTo: bigint, peaksFrom: Uint8Array[], paths: Uint8Array[][], peaksTo: Uint8Array[]): Promise<{
104
193
  ok: boolean;
105
194
  accumulator: Uint8Array[];
106
195
  }>;
@@ -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,EAAkC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAqB7E;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;GAYG;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;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,UAAU,EAAE,EACvB,OAAO,EAAE,UAAU,EAAE,GACpB,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CAwBrD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,gBAAgB,CAMlB"}
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"}
@@ -10,10 +10,18 @@
10
10
  * And the associated IETF draft specification:
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
- import { inclusionProof, peakMMRIndexes } from "./proof.js";
13
+ import { inclusionProof, peakMMRIndexes, bitLength, popcount, peaksBitmap, mmrSizeForLeafCount, } from "./proof.js";
14
+ import { ConsistencyPathLengthMismatch, ConsistencyPathMalformed, ConsistencyPeakCountMismatch, ConsistencyRootMismatch, IncompleteTreeSize, SizeMustIncrease, SizeNotUint64, } from "./errors.js";
14
15
  import { Uint64 } from "../uint64/index.js";
15
16
  import { heightIndex } from "./math.js";
16
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;
17
25
  /**
18
26
  * Encodes a Uint64 as 8 bytes big-endian.
19
27
  *
@@ -140,6 +148,12 @@ export async function verifyInclusion(hasher, leafHash, proof, root) {
140
148
  * duplicate roots collapse (many old peaks share one new peak). Requires
141
149
  * one path per MMR(A) peak (draft: `len(peaks(ifrom)) == len(accumulatorfrom)`).
142
150
  *
151
+ * This fold does not enforce the proof shape the two sizes imply: it reads
152
+ * whatever path lengths it is given and collapses whatever roots coincide.
153
+ * Use {@link consistentRootsForSizes} for verification — it checks each path
154
+ * against the length MMR(A) -> MMR(B) fixes and requires the paths under one
155
+ * target peak to agree.
156
+ *
143
157
  * @param ifrom - last node index of the complete MMR(A) (`mmrSizeA - 1`)
144
158
  * @param accumulatorFrom - MMR(A) peak values, descending height order
145
159
  * @param paths - inclusion path per peak, proven in MMR(B)
@@ -162,40 +176,201 @@ export async function consistentRoots(hasher, ifrom, accumulatorFrom, paths) {
162
176
  }
163
177
  return roots;
164
178
  }
179
+ /**
180
+ * Produce the peaks of MMR(sizeTo) that `paths` prove from the peaks of
181
+ * MMR(sizeFrom), requiring the paths to have exactly the shape the two sizes
182
+ * imply (draft-bryce-cose-receipts-mmr-profile, "Verifying the Receipt of
183
+ * consistency", with the draft's SHOULD on path lengths enforced in the same
184
+ * pass). Line-for-line port of the reference `consistent_roots_for_sizes`
185
+ * (algorithms.py) and of Solidity `consistentRootsForSizes`
186
+ * (univocity `src/algorithms/consistentRoots.sol`).
187
+ *
188
+ * Sizes are node counts (the MMR ending at index `i` has `i + 1` nodes).
189
+ * `sizeFrom` MUST be the size of the state the verifier already trusts; taken
190
+ * from the proof instead, the check is void. Only `sizeTo` is required to be
191
+ * a complete MMR size: every trusted size was itself a checked target.
192
+ *
193
+ * For a complete MMR the set bits of `peaksBitmap(size)` are the peak heights,
194
+ * high to low, which is accumulator order. Let `split` be the highest bit on
195
+ * which the two bitmaps differ; as `sizeTo > sizeFrom` the target has it and
196
+ * the origin does not. An origin peak above `split` is also a peak of the
197
+ * target: its path is empty and it is returned unchanged. Every origin peak
198
+ * below `split` is committed by the target peak of height `split`: its path
199
+ * has length `split - h`, and every such path must prove the same root. The
200
+ * target's remaining peaks lie below every origin peak, so no path reaches
201
+ * them; the prover supplies them separately and their count is returned.
202
+ *
203
+ * Because the shape is fixed by the sizes alone, no peak index list and no
204
+ * per-hop bookkeeping beyond the hash itself is needed: the bitmaps are
205
+ * iterated directly.
206
+ *
207
+ * @param hasher - cryptographic hasher instance
208
+ * @param sizeFrom - node count of the trusted origin state (0 for an empty log)
209
+ * @param sizeTo - node count of the target state; must exceed `sizeFrom` and
210
+ * must be a complete MMR size
211
+ * @param accumulatorFrom - peaks of MMR(sizeFrom), descending height order
212
+ * @param paths - one path per origin peak, in the same order
213
+ * @returns `roots`, the peaks of MMR(sizeTo) proven from the origin peaks in
214
+ * descending height order (the unchanged peaks, then the one proven root if
215
+ * any), and `expectedRight`, the number of MMR(sizeTo) peaks the prover must
216
+ * supply as right peaks. `roots` followed by those right peaks is the
217
+ * accumulator of MMR(sizeTo).
218
+ * @throws {SizeNotUint64} if either size is outside `[0, 2^64 - 1]`
219
+ * @throws {SizeMustIncrease} if `sizeTo <= sizeFrom`
220
+ * @throws {IncompleteTreeSize} if `sizeTo` is not a complete MMR size
221
+ * @throws {ConsistencyPeakCountMismatch} if `accumulatorFrom` or `paths` does
222
+ * not have one entry per origin peak
223
+ * @throws {ConsistencyPathMalformed} if `paths` is not a dense array of
224
+ * `Uint8Array[]`
225
+ * @throws {ConsistencyPathLengthMismatch} if a path length differs from the
226
+ * length the two sizes imply
227
+ * @throws {ConsistencyRootMismatch} if two paths under one target peak produce
228
+ * different roots
229
+ */
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
+ }
239
+ if (sizeTo <= sizeFrom) {
240
+ throw new SizeMustIncrease(sizeFrom, sizeTo);
241
+ }
242
+ const to = peaksBitmap(sizeTo);
243
+ // peaksBitmap rounds an incomplete size down to the largest MMR below it,
244
+ // so `to` describes MMR(sizeTo) only if sizeTo is complete. Without this a
245
+ // target such as 6 anchors an accumulator that is no MMR's, and a verifier
246
+ // later reads its entries at the wrong heights.
247
+ if (mmrSizeForLeafCount(to) !== sizeTo) {
248
+ throw new IncompleteTreeSize(sizeTo);
249
+ }
250
+ const from = peaksBitmap(sizeFrom);
251
+ const n = popcount(from);
252
+ if (accumulatorFrom.length !== n) {
253
+ throw new ConsistencyPeakCountMismatch(n, accumulatorFrom.length);
254
+ }
255
+ if (!Array.isArray(paths)) {
256
+ throw new ConsistencyPathMalformed(`not an array (got ${typeName(paths)})`);
257
+ }
258
+ if (paths.length !== n) {
259
+ throw new ConsistencyPeakCountMismatch(n, paths.length);
260
+ }
261
+ assertDensePaths(paths);
262
+ const nto = popcount(to);
263
+ if (n === 0) {
264
+ return { roots: [], expectedRight: nto };
265
+ }
266
+ const split = bitLength(from ^ to) - 1;
267
+ const roots = [];
268
+ // Nodes preceding the current origin peak's subtree; a peak of height h
269
+ // sits at offset + 2^(h+1) - 2 and its subtree has 2^(h+1) - 1 nodes.
270
+ let offset = 0n;
271
+ let i = 0;
272
+ // Origin peaks above the split are also peaks of the target. The path is
273
+ // not read; requiring it to be empty rejects unused material (a shape
274
+ // check: the result does not depend on it).
275
+ for (let h = bitLength(from) - 1; h > split; h--) {
276
+ if (((from >> BigInt(h)) & 1n) === 0n)
277
+ continue;
278
+ if (paths[i].length !== 0) {
279
+ throw new ConsistencyPathLengthMismatch(i, 0, paths[i].length);
280
+ }
281
+ roots.push(accumulatorFrom[i]);
282
+ offset += (1n << BigInt(h + 1)) - 1n;
283
+ i += 1;
284
+ }
285
+ // Origin peaks below the split are all committed by the target peak of
286
+ // height `split` (bit `split` itself is clear in `from`), so each path must
287
+ // have length split - h and every path must prove the same root. The first
288
+ // `above` peaks were returned unchanged, so i === above at the first peak
289
+ // below the split.
290
+ const above = roots.length;
291
+ let root;
292
+ for (let h = split - 1; h >= 0; h--) {
293
+ if (((from >> BigInt(h)) & 1n) === 0n)
294
+ continue;
295
+ const expected = split - h;
296
+ if (paths[i].length !== expected) {
297
+ throw new ConsistencyPathLengthMismatch(i, expected, paths[i].length);
298
+ }
299
+ const subtree = (1n << BigInt(h + 1)) - 1n;
300
+ const peakMMRIndex = offset + subtree - 1n;
301
+ const proven = await calculateRoot(hasher, accumulatorFrom[i], { path: paths[i], mmrIndex: peakMMRIndex }, peakMMRIndex);
302
+ if (i === above) {
303
+ root = proven;
304
+ }
305
+ else if (!arraysEqual(proven, root)) {
306
+ throw new ConsistencyRootMismatch(i);
307
+ }
308
+ offset += subtree;
309
+ i += 1;
310
+ }
311
+ if (n > above) {
312
+ roots.push(root);
313
+ }
314
+ return { roots, expectedRight: nto - roots.length };
315
+ }
165
316
  /**
166
317
  * Verify MMR(A) is a committed prefix of MMR(B)
167
318
  * (draft-bryce "Verifying the Receipt of consistency";
168
319
  * go-merklelog `VerifyConsistency`).
169
320
  *
321
+ * BOTH sizes are CALLER-SUPPLIED trusted state (ADR-0066 D5.4), never read
322
+ * off the proof: `sizeFrom` is the size of the state `peaksFrom` is the
323
+ * accumulator of, and `sizeTo` the size `peaksTo` is claimed to be the
324
+ * accumulator of. The proof contributes only `paths`. Taking either size
325
+ * from the proof makes the check void: the fold constrains a declared
326
+ * `sizeFrom` by the origin peak COUNT alone, which a whole family of sizes
327
+ * share, so one set of paths and peaks verified at many size pairs — with
328
+ * the draft's own MMR(11) -> MMR(39) material, 24 distinct pairs, of which
329
+ * one is the true relation. `indexConsistencyProof` still returns the
330
+ * {@link ConsistencyProof} wire type with its declared sizes; a caller that
331
+ * relays that type passes `proof.paths` here and supplies the sizes it
332
+ * trusts, comparing the declared sizes with them separately if it wants to
333
+ * report a disagreement.
334
+ *
335
+ * Routed through {@link consistentRootsForSizes}, so the proof must have the
336
+ * shape `sizeFrom -> sizeTo` implies: one path per MMR(A) peak, empty above
337
+ * the split and of length `split - h` below it, with every path below the
338
+ * split proving the same root. The proven roots must then be the leading
339
+ * entries of `peaksTo`, and `peaksTo` must hold exactly those plus the
340
+ * `expectedRight` right peaks no path reaches — so a truncated or padded
341
+ * target accumulator is rejected on its length alone.
342
+ *
170
343
  * Replaces the plan-0027 always-true stub (FOR-368 Phase 1,
171
344
  * plan-2607-29): the previous signature took two inclusion proofs and
172
345
  * returned true unconditionally; no caller existed.
173
346
  *
174
- * @param proof - consistency proof `mmrSizeA -> mmrSizeB`
347
+ * @param sizeFrom - TRUSTED node count of MMR(A)
348
+ * @param sizeTo - TRUSTED node count of MMR(B)
175
349
  * @param peaksFrom - TRUSTED MMR(A) accumulator (e.g. a signed checkpoint
176
350
  * payload), descending height order
351
+ * @param paths - one inclusion path per MMR(A) peak, in the same order
177
352
  * @param peaksTo - MMR(B) accumulator to prove against (e.g. an anchored
178
353
  * on-chain state), descending height order
179
- * @returns ok, with the MMR(B) accumulator on success
354
+ * @returns ok, with the MMR(B) accumulator on success. A value that does not
355
+ * match `peaksTo` returns `{ok: false, accumulator: []}`; a proof that does
356
+ * not have the shape the sizes imply throws one of the typed errors in
357
+ * `./errors.js` (all `ConsistencyShapeError` subclasses), which callers
358
+ * report as a malformed proof rather than a failed comparison.
180
359
  */
181
- export async function verifyConsistency(hasher, proof, peaksFrom, peaksTo) {
182
- const proven = await consistentRoots(hasher, proof.mmrSizeA - 1n, peaksFrom, proof.paths);
183
- // Both lists are in descending height order, so every proven root must
184
- // appear in order within peaksTo (a linear scan; go-merklelog semantics:
185
- // a proven root matches the current peak or exactly the next one down).
186
- let ito = 0;
187
- for (const root of proven) {
188
- if (ito < peaksTo.length && arraysEqual(peaksTo[ito], root)) {
189
- continue;
190
- }
191
- ito += 1;
192
- if (ito >= peaksTo.length || !arraysEqual(peaksTo[ito], root)) {
360
+ export async function verifyConsistency(hasher, sizeFrom, sizeTo, peaksFrom, paths, peaksTo) {
361
+ const { roots, expectedRight } = await consistentRootsForSizes(hasher, sizeFrom, sizeTo, peaksFrom, paths);
362
+ // roots is the leading run of the MMR(B) accumulator and expectedRight
363
+ // counts the peaks below every MMR(A) peak, which no path reaches. Together
364
+ // they fix the length of MMR(B)'s accumulator, so the supplied peaksTo must
365
+ // have that length and must start with the proven roots.
366
+ if (peaksTo.length !== roots.length + expectedRight) {
367
+ return { ok: false, accumulator: [] };
368
+ }
369
+ for (let i = 0; i < roots.length; i++) {
370
+ if (!arraysEqual(peaksTo[i], roots[i])) {
193
371
  return { ok: false, accumulator: [] };
194
372
  }
195
373
  }
196
- // The full MMR(B) accumulator is the proven prefix plus any right-peaks;
197
- // returning peaksTo is safe because consistentRoots enforced one proof
198
- // per MMR(A) peak (see go-merklelog VerifyConsistency).
199
374
  return { ok: true, accumulator: peaksTo };
200
375
  }
201
376
  /**
@@ -211,3 +386,34 @@ export function indexConsistencyProof(get, mmrIndexA, mmrIndexB) {
211
386
  }
212
387
  return { mmrSizeA: mmrIndexA + 1n, mmrSizeB: mmrIndexB + 1n, paths };
213
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
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Typed errors for the size-driven consistency fold
3
+ * (`consistentRootsForSizes`).
4
+ *
5
+ * The names mirror the Solidity `IUnivocityErrors` members raised by the
6
+ * on-chain implementation (`univocity/src/algorithms/consistentRoots.sol`),
7
+ * so a shape rejection reads the same on both sides. Every one of these
8
+ * reports a condition the two sizes fix in advance — the proof does not
9
+ * have the shape MMR(sizeFrom) -> MMR(sizeTo) implies — as distinct from a
10
+ * value mismatch against a trusted accumulator, which callers surface as a
11
+ * plain verification failure.
12
+ *
13
+ * All derive from {@link ConsistencyShapeError} so a caller can map the whole
14
+ * family to "malformed proof" with a single `instanceof`.
15
+ */
16
+ /** Base class for every consistency proof-shape rejection. */
17
+ export declare class ConsistencyShapeError extends Error {
18
+ constructor(message: string);
19
+ }
20
+ /**
21
+ * The target size is not a complete MMR size.
22
+ *
23
+ * `peaksBitmap` rounds an incomplete size down to the largest complete MMR
24
+ * below it, so for an incomplete `sizeTo` the bitmap describes a different
25
+ * tree than the one named. Accepting it would anchor an accumulator that is
26
+ * no MMR's, and a later verifier would read its entries at the wrong heights.
27
+ */
28
+ export declare class IncompleteTreeSize extends ConsistencyShapeError {
29
+ readonly size: bigint;
30
+ constructor(size: bigint);
31
+ }
32
+ /**
33
+ * The accumulator or the proof list does not have one entry per origin peak.
34
+ * The origin peak count is fixed by `sizeFrom` alone.
35
+ */
36
+ export declare class ConsistencyPeakCountMismatch extends ConsistencyShapeError {
37
+ readonly expected: number;
38
+ readonly actual: number;
39
+ constructor(expected: number, actual: number);
40
+ }
41
+ /**
42
+ * A path does not have the length the two sizes imply: 0 for an origin peak
43
+ * above the split, `split - h` for an origin peak of height `h` below it.
44
+ */
45
+ export declare class ConsistencyPathLengthMismatch extends ConsistencyShapeError {
46
+ readonly index: number;
47
+ readonly expected: number;
48
+ readonly actual: number;
49
+ constructor(index: number, expected: number, actual: number);
50
+ }
51
+ /**
52
+ * Two origin peaks below the split proved different roots. Every origin peak
53
+ * below the split is committed by the single target peak of height `split`,
54
+ * so all of their paths must produce the same value.
55
+ */
56
+ export declare class ConsistencyRootMismatch extends ConsistencyShapeError {
57
+ readonly index: number;
58
+ constructor(index: number);
59
+ }
60
+ /**
61
+ * The target size does not exceed the origin size. Consistency is defined for
62
+ * a growing log; equal or decreasing sizes have no proof shape to check.
63
+ */
64
+ export declare class SizeMustIncrease extends ConsistencyShapeError {
65
+ readonly sizeFrom: bigint;
66
+ readonly sizeTo: bigint;
67
+ constructor(sizeFrom: bigint, sizeTo: bigint);
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
+ }
100
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +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;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"}