@forestrie/receipt-verify 2.1.0 → 3.0.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/build-receipt-offline.js +14 -10
- package/dist/checkpoint-chain.d.ts +130 -48
- package/dist/checkpoint-chain.d.ts.map +1 -1
- package/dist/checkpoint-chain.js +212 -60
- package/dist/decode-checkpoint-consistency-proof.d.ts +47 -15
- package/dist/decode-checkpoint-consistency-proof.d.ts.map +1 -1
- package/dist/decode-checkpoint-consistency-proof.js +105 -33
- package/dist/freshen-receipt.d.ts +12 -6
- package/dist/freshen-receipt.d.ts.map +1 -1
- package/dist/freshen-receipt.js +34 -14
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/src/build-receipt-offline.ts +14 -10
- package/src/checkpoint-chain.ts +254 -79
- package/src/decode-checkpoint-consistency-proof.ts +118 -34
- package/src/freshen-receipt.ts +43 -16
- package/src/index.ts +6 -0
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { COSE_LABEL_PEAK_RECEIPTS, COSE_LABEL_VDP, decodeCborDeterministic, encodeCborDeterministic, readProtectedTreeSize2, } from "@forestrie/encoding";
|
|
18
18
|
import { calculateRoot, inclusionProof, massifIndexFromMMRIndex, openMassifNodeStore, peakIndexForLeafProof, } from "@forestrie/merklelog";
|
|
19
19
|
import { requireCoseSign1, toHeaderMap, unwrapCoseSign1Tag, } from "./parse-receipt.js";
|
|
20
|
-
import {
|
|
20
|
+
import { decodeConsistencyProofsFromUnprotected } from "./decode-checkpoint-consistency-proof.js";
|
|
21
21
|
import { SubtleHasher } from "./subtle-hasher.js";
|
|
22
22
|
/** Not in scope for the shared cose-labels module (FOR-568 §4.1). */
|
|
23
23
|
const DELEGATION_CERT_LABEL = 1000;
|
|
@@ -158,20 +158,24 @@ function cborBytes(value) {
|
|
|
158
158
|
* (ADR-0066 D1 as amended, label -65933) from the PROTECTED header — not the
|
|
159
159
|
* unprotected consistency proof's declared value, which a checkpoint without
|
|
160
160
|
* a signing key could freely restate (ADR-0066's "keyless first checkpoint"
|
|
161
|
-
* case).
|
|
162
|
-
* with no proof is not sealed at all)
|
|
163
|
-
*
|
|
164
|
-
*
|
|
161
|
+
* case). At least one consistency proof must still be present (an unsigned
|
|
162
|
+
* checkpoint with no proof is not sealed at all) — one or more, since a
|
|
163
|
+
* checkpoint may relay a chain of sealed steps under one signature
|
|
164
|
+
* (ADR-0066 D2). Their declared sizes are not otherwise used here;
|
|
165
|
+
* {@link checkpointConsistencyProof} in `checkpoint-chain.ts` is the
|
|
166
|
+
* validating decode that requires the signed size and the last relayed
|
|
167
|
+
* proof's to agree.
|
|
165
168
|
*
|
|
166
|
-
* Lenient by design: an absent proof,
|
|
167
|
-
* malformed signed tree-size-2
|
|
168
|
-
*
|
|
169
|
-
*
|
|
169
|
+
* Lenient by design: an absent proof, an empty or malformed
|
|
170
|
+
* consistency-proofs array, or an absent or malformed signed tree-size-2
|
|
171
|
+
* all yield `null` rather than a throw, so a caller of
|
|
172
|
+
* {@link parseCheckpoint} sees exactly the same "not verifiable" outcome
|
|
173
|
+
* either way.
|
|
170
174
|
*/
|
|
171
175
|
function sealedSizeFromCheckpoint(coseSign1, unprotected) {
|
|
172
176
|
let declared;
|
|
173
177
|
try {
|
|
174
|
-
declared =
|
|
178
|
+
declared = decodeConsistencyProofsFromUnprotected(unprotected);
|
|
175
179
|
}
|
|
176
180
|
catch {
|
|
177
181
|
return null;
|
|
@@ -1,19 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
1
|
+
import { EmptyConsistencyProofsError, type DecodedConsistencyProof } from "./decode-checkpoint-consistency-proof.js";
|
|
2
|
+
/**
|
|
3
|
+
* The draft-bryce consistency proofs embedded in a v3 checkpoint: one or
|
|
4
|
+
* more, in relay order (`consistency-proofs = [ + consistency-proof ]`,
|
|
5
|
+
* ADR-0066 D2). A checkpoint sealing a single step carries the chain of
|
|
6
|
+
* one; there is no separate single-proof shape.
|
|
7
|
+
*
|
|
8
|
+
* {@link treeSize2} — the LAST proof's — is cross-checked against the
|
|
9
|
+
* checkpoint's SIGNED tree-size-2 (ADR-0066 D1 as amended, D5.5).
|
|
10
|
+
* {@link treeSize1} — the FIRST proof's — is unsigned prover context, not
|
|
11
|
+
* cross-checked here; see {@link verifyCheckpointChain}, which compares it
|
|
12
|
+
* with the trusted origin instead. The sizes between the two are named by
|
|
13
|
+
* no signature: {@link computeCheckpointAccumulator} holds the chain
|
|
14
|
+
* together by requiring each proof to continue the one before it.
|
|
15
|
+
*/
|
|
6
16
|
export type CheckpointConsistencyProof = {
|
|
17
|
+
/** The relayed proofs, in chain order; never empty. */
|
|
18
|
+
proofs: DecodedConsistencyProof[];
|
|
19
|
+
/** `tree-size-1` of the FIRST proof: the size the chain continues from. */
|
|
7
20
|
treeSize1: bigint;
|
|
21
|
+
/** `tree-size-2` of the LAST proof: the size the chain reaches. */
|
|
8
22
|
treeSize2: bigint;
|
|
9
23
|
/** Signed `tree-size-2` (protected header label -65933); equal to
|
|
10
24
|
* {@link treeSize2} — {@link checkpointConsistencyProof} enforces this. */
|
|
11
25
|
signedTreeSize2: bigint;
|
|
12
|
-
/** One inclusion path per tree-size-1 peak, proven at tree-size-2. */
|
|
13
|
-
paths: Uint8Array[][];
|
|
14
|
-
/** New peaks not covered by the proven roots (draft `right-peaks`). */
|
|
15
|
-
rightPeaks: Uint8Array[];
|
|
16
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* A relayed consistency-proof chain does not join up: a proof's declared
|
|
29
|
+
* `tree-size-1` is not the size the fold has reached — the caller's trusted
|
|
30
|
+
* size for the first proof, the previous proof's `tree-size-2` after that —
|
|
31
|
+
* or, having applied every proof, the fold reaches a size other than the
|
|
32
|
+
* chain link's own declared `tree-size-2` (F3: a caller-assembled link —
|
|
33
|
+
* `freshenReceipt` callers build these from on-chain calldata, never
|
|
34
|
+
* through {@link checkpointConsistencyProof} — can set `proofs` and
|
|
35
|
+
* `treeSize2` independently, which `checkpointConsistencyProof` itself
|
|
36
|
+
* never allows to disagree). go-merklelog reuses its equivalent
|
|
37
|
+
* `ErrProofChainNotContiguous` for this same end-of-chain comparison
|
|
38
|
+
* (`checkpointverify.go:245-251`, folded size vs. signed size). Reported as
|
|
39
|
+
* `"size_mismatch"` by {@link verifyCheckpointChain}, the same as any other
|
|
40
|
+
* size disagreement, because the sizes it names are unsigned (ADR-0066 D2):
|
|
41
|
+
* nothing distinguishes a relay assembled in the wrong order from one
|
|
42
|
+
* assembled over a different log.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ConsistencyChainNotContiguousError extends Error {
|
|
45
|
+
constructor(message: string);
|
|
46
|
+
}
|
|
47
|
+
export { EmptyConsistencyProofsError };
|
|
17
48
|
/**
|
|
18
49
|
* The checkpoint's SIGNED `tree-size-2` (protected header, ADR-0066 D1 as
|
|
19
50
|
* amended) differs from the declared `tree-size-2` of its embedded
|
|
@@ -40,12 +71,29 @@ export declare class CheckpointHighSSignatureError extends Error {
|
|
|
40
71
|
constructor(message: string);
|
|
41
72
|
}
|
|
42
73
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
74
|
+
* The checkpoint's protected header carries no integer `alg` (label 1).
|
|
75
|
+
*
|
|
76
|
+
* The univocity contract rejects such a header outright — the structural
|
|
77
|
+
* walk finds the size, and the `alg` requirement in the same call raises
|
|
78
|
+
* `ClaimNotFound(1)` or `UnexpectedMajorType`. Off-chain the header used to
|
|
79
|
+
* read as "no algorithm stated", which both turned OFF the high-s rejection
|
|
80
|
+
* below (gated on the algorithm being ES256) and still yielded a signed size
|
|
81
|
+
* to fold from — so a checkpoint the chain will never anchor verified here
|
|
82
|
+
* under weaker rules than a well-formed one (review finding S-1). Reported
|
|
83
|
+
* as `"proof_malformed"` by {@link verifyCheckpointChain}.
|
|
84
|
+
*/
|
|
85
|
+
export declare class CheckpointProtectedHeaderAlgError extends Error {
|
|
86
|
+
constructor(message: string);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Decode the embedded consistency proofs (`vdp` 396 key -2, one or more in
|
|
90
|
+
* relay order) and require the LAST proof's declared `tree-size-2` to equal
|
|
91
|
+
* the checkpoint's SIGNED `tree-size-2` from the protected header (ADR-0066
|
|
92
|
+
* D1 as amended, D2, D5.5, label -65933). The earlier proofs' sizes are not
|
|
93
|
+
* signed: the fold checks them against each other
|
|
94
|
+
* ({@link computeCheckpointAccumulator}). `tree-size-1` is not signed
|
|
95
|
+
* either; see {@link verifyCheckpointChain} for its comparison against the
|
|
96
|
+
* trusted origin.
|
|
49
97
|
*
|
|
50
98
|
* Also rejects a malleable high-s ES256 signature (see
|
|
51
99
|
* {@link CheckpointHighSSignatureError}) before any fold or WebCrypto verify
|
|
@@ -53,36 +101,60 @@ export declare class CheckpointHighSSignatureError extends Error {
|
|
|
53
101
|
* subject to this check, since it does not go through the P-256 WebCrypto
|
|
54
102
|
* path this guards.
|
|
55
103
|
*
|
|
56
|
-
* @throws {Error} when the
|
|
57
|
-
*
|
|
58
|
-
* {@link
|
|
104
|
+
* @throws {Error} when the unprotected header carries no consistency proof,
|
|
105
|
+
* a proof is structurally malformed (see
|
|
106
|
+
* {@link decodeConsistencyProofsFromUnprotected}), or the protected header
|
|
59
107
|
* carries no signed tree-size-2 label
|
|
108
|
+
* @throws {EmptyConsistencyProofsError} when the consistency-proofs array is
|
|
109
|
+
* present but empty
|
|
60
110
|
* @throws {CheckpointSignedSizeMismatchError} when the signed tree-size-2
|
|
61
|
-
* differs from the declared proof's tree-size-2
|
|
111
|
+
* differs from the LAST declared proof's tree-size-2
|
|
62
112
|
* @throws {CheckpointHighSSignatureError} when the checkpoint is ES256-signed
|
|
63
113
|
* with a high-s (malleable) signature
|
|
114
|
+
* @throws {CheckpointProtectedHeaderAlgError} when the protected header
|
|
115
|
+
* carries no integer `alg` (label 1)
|
|
64
116
|
*/
|
|
65
117
|
export declare function checkpointConsistencyProof(checkpointBytes: Uint8Array): CheckpointConsistencyProof;
|
|
66
118
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
119
|
+
* Fold a checkpoint's relayed consistency proofs, in order, from the
|
|
120
|
+
* CALLER-TRUSTED accumulator at `sizeFrom` to the accumulator at the last
|
|
121
|
+
* proof's `tree-size-2` — the value the checkpoint's signature covers.
|
|
122
|
+
*
|
|
123
|
+
* Each proof is applied by the size-driven {@link consistentRootsForSizes}
|
|
124
|
+
* (ADR-0066 D5), which yields `roots` (the proven prefix); the proof's own
|
|
125
|
+
* right-peaks (the target peaks no path reaches) complete that step's
|
|
126
|
+
* accumulator, and it becomes the next step's input. A checkpoint sealing
|
|
127
|
+
* one step carries the chain of one and runs the same loop once.
|
|
128
|
+
*
|
|
129
|
+
* `sizeFrom` is a parameter, not read off the proofs, because the fold must
|
|
130
|
+
* start from a size the CALLER already trusts (the previous checkpoint's
|
|
131
|
+
* verified `treeSize2`, or the caller's anchor for a first link) — reading
|
|
132
|
+
* it from the relay instead would let an unsigned or substituted proof
|
|
133
|
+
* dictate its own starting point (ADR-0066 D5.4). Every proof after the
|
|
134
|
+
* first is held to the size the previous one reached for the same reason:
|
|
135
|
+
* only the last step's size is signed, so the intermediate sizes are worth
|
|
136
|
+
* no more than their agreement with each other.
|
|
72
137
|
*
|
|
73
|
-
* `
|
|
74
|
-
*
|
|
75
|
-
* `
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
138
|
+
* `proof.proofs` must hold at least one step and, once every step has been
|
|
139
|
+
* applied, the fold must have reached exactly `proof.treeSize2` (F3).
|
|
140
|
+
* `checkpointConsistencyProof` already guarantees both — the decode rejects
|
|
141
|
+
* an empty `consistency-proofs` array (`EmptyConsistencyProofsError`) and
|
|
142
|
+
* sets `treeSize2` from the last decoded proof — but a caller-assembled
|
|
143
|
+
* link (`freshenReceipt` callers building from on-chain calldata) is not
|
|
144
|
+
* decoded through it, so both are re-checked here rather than trusted from
|
|
145
|
+
* the type.
|
|
80
146
|
*
|
|
81
|
-
* @throws {
|
|
82
|
-
*
|
|
147
|
+
* @throws {EmptyConsistencyProofsError} when `proof.proofs` is empty — an
|
|
148
|
+
* already-decoded link the caller assembled themselves rather than one
|
|
149
|
+
* `checkpointConsistencyProof` produced, which never returns one
|
|
150
|
+
* @throws {ConsistencyChainNotContiguousError} when the first proof's
|
|
151
|
+
* `treeSize1` is not `sizeFrom`, a later proof's `treeSize1` is not the
|
|
152
|
+
* previous proof's `treeSize2`, or the size the fold reaches after every
|
|
153
|
+
* proof is not `proof.treeSize2`
|
|
154
|
+
* @throws {Error} when a proof supplies a right-peaks count other than
|
|
83
155
|
* {@link consistentRootsForSizes}'s `expectedRight`
|
|
84
|
-
* @throws {ConsistencyShapeError} (`@forestrie/merklelog`) when
|
|
85
|
-
*
|
|
156
|
+
* @throws {ConsistencyShapeError} (`@forestrie/merklelog`) when a proof does
|
|
157
|
+
* not have the shape its two sizes imply
|
|
86
158
|
*/
|
|
87
159
|
export declare function computeCheckpointAccumulator(proof: CheckpointConsistencyProof, accumulatorFrom: Uint8Array[], sizeFrom: bigint): Promise<Uint8Array[]>;
|
|
88
160
|
/** Detached payload the checkpoint signature covers (ADR-0046): the raw
|
|
@@ -118,8 +190,10 @@ export type CheckpointChainResult = {
|
|
|
118
190
|
* `tree-size-2` (ADR-0066 D1 as amended, D5.5), or a link's declared
|
|
119
191
|
* `tree-size-1` disagrees with the size the fold starts from — the
|
|
120
192
|
* caller's `trustedBase.size` (0 with no `trustedBase`) for the
|
|
121
|
-
* first link, the previous link's `tree-size-2` after that
|
|
122
|
-
*
|
|
193
|
+
* first link, the previous link's `tree-size-2` after that — or a
|
|
194
|
+
* relayed proof WITHIN a checkpoint disagrees with the size the
|
|
195
|
+
* proof before it reached ({@link
|
|
196
|
+
* ConsistencyChainNotContiguousError}). `detail` names both sizes.
|
|
123
197
|
*/
|
|
124
198
|
| "size_mismatch";
|
|
125
199
|
/** Index of the offending checkpoint. */
|
|
@@ -136,23 +210,31 @@ export type CheckpointChainResult = {
|
|
|
136
210
|
* as the fold's starting accumulator (a suffix chain rooted in an
|
|
137
211
|
* already-trusted accumulator supplies both).
|
|
138
212
|
* - A supplied `trustedBase` must describe a state an MMR can be in: its
|
|
139
|
-
* `size` a complete MMR size,
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* count no MMR has
|
|
213
|
+
* `size` a complete MMR size, its `accumulator` holding one peak per peak
|
|
214
|
+
* of that size, and every one of those peaks a 32-byte node value.
|
|
215
|
+
* `peaksBitmap` rounds an incomplete size DOWN to the largest MMR below
|
|
216
|
+
* it, so without the completeness check a size of 5 folds the 4 -> N shape
|
|
217
|
+
* while every link reports a base of 5 — a node count no MMR has; and
|
|
218
|
+
* without the byte-length check an origin peak of any length is copied
|
|
219
|
+
* through the empty-path branch into the detached payload. All three are
|
|
220
|
+
* `proof_malformed`.
|
|
144
221
|
* - The first link's declared `tree-size-1` must equal that trusted
|
|
145
222
|
* starting size, and every subsequent link's must equal the previous
|
|
146
223
|
* link's sealed `tree-size-2`; either disagreement is `size_mismatch`.
|
|
147
|
-
* `tree-size-1` itself is never compared with a signed value (
|
|
148
|
-
*
|
|
224
|
+
* `tree-size-1` itself is never compared with a signed value (the signed
|
|
225
|
+
* origin ADR-0066 D2 first proposed was withdrawn): only this
|
|
226
|
+
* trusted-origin comparison applies. Because it is
|
|
149
227
|
* unsigned, the reason it produces carries no more meaning than the size
|
|
150
228
|
* disagreement itself (ADR-0066 D6: no pre-FOR-410 state is supported, so
|
|
151
229
|
* there is no drift condition to fall back from).
|
|
152
|
-
* -
|
|
153
|
-
*
|
|
154
|
-
* (
|
|
155
|
-
* `
|
|
230
|
+
* - A checkpoint may relay SEVERAL consistency proofs under one signature
|
|
231
|
+
* (ADR-0066 D2; draft `consistency-proofs = [ + consistency-proof ]`).
|
|
232
|
+
* Its SIGNED `tree-size-2` (ADR-0066 D1 as amended) must equal the LAST
|
|
233
|
+
* proof's declared `tree-size-2` ({@link checkpointConsistencyProof}),
|
|
234
|
+
* and each relayed proof must continue the one before it
|
|
235
|
+
* ({@link computeCheckpointAccumulator}); either disagreement is
|
|
236
|
+
* `size_mismatch`. A checkpoint sealing one step is the relay of one and
|
|
237
|
+
* takes the same path.
|
|
156
238
|
* - Each link's signature is checked over its computed accumulator via
|
|
157
239
|
* the injected verifier (the caller owns trust resolution — genesis
|
|
158
240
|
* roots, caller-known keys, or the label-1000 delegation path). A link
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint-chain.d.ts","sourceRoot":"","sources":["../src/checkpoint-chain.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"checkpoint-chain.d.ts","sourceRoot":"","sources":["../src/checkpoint-chain.ts"],"names":[],"mappings":"AA2DA,OAAO,EAEL,2BAA2B,EAC3B,KAAK,uBAAuB,EAC7B,MAAM,0CAA0C,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,uDAAuD;IACvD,MAAM,EAAE,uBAAuB,EAAE,CAAC;IAClC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB;+EAC2E;IAC3E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kCAAmC,SAAQ,KAAK;gBAC/C,OAAO,EAAE,MAAM;CAI5B;AAED,OAAO,EAAE,2BAA2B,EAAE,CAAC;AAEvC;;;;;;GAMG;AACH,qBAAa,iCAAkC,SAAQ,KAAK;gBAC9C,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iCAAkC,SAAQ,KAAK;gBAC9C,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,UAAU,GAC1B,0BAA0B,CAgD5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,0BAA0B,EACjC,eAAe,EAAE,UAAU,EAAE,EAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,EAAE,CAAC,CA+CvB;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,UAAU,CAQxE;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;mBACe;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,2EAA2E;IAC3E,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,GACrE;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EACF,aAAa,GACb,WAAW;IACb;;;;;OAKG;OACD,qBAAqB,GACrB,iBAAiB;IACnB;;;;;;;;;;OAUG;OACD,eAAe,CAAC;IACpB,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B,CAAC;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,eAAe,EAAE,CACf,eAAe,EAAE,UAAU,EAC3B,eAAe,EAAE,UAAU,KACxB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB;+EAC2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;CAC3D,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA6JjC"}
|