zklicensing 0.1.0-beta.2 → 0.1.0-beta.4

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.
Files changed (173) hide show
  1. package/README.md +335 -97
  2. package/build/appsLookup.d.ts +14 -0
  3. package/build/appsLookup.d.ts.map +1 -0
  4. package/build/appsLookup.js +12 -0
  5. package/build/appsLookup.js.map +1 -0
  6. package/build/archiveBootstrap.d.ts +15 -1
  7. package/build/archiveBootstrap.d.ts.map +1 -0
  8. package/build/archiveBootstrap.js +62 -9
  9. package/build/archiveBootstrap.js.map +1 -0
  10. package/build/bindClient.d.ts +51 -0
  11. package/build/bindClient.d.ts.map +1 -0
  12. package/build/bindClient.js +115 -0
  13. package/build/bindClient.js.map +1 -0
  14. package/build/buyClient.d.ts +10 -3
  15. package/build/buyClient.d.ts.map +1 -0
  16. package/build/buyClient.js +33 -15
  17. package/build/buyClient.js.map +1 -0
  18. package/build/contractInterface.d.ts +11 -5
  19. package/build/contractInterface.d.ts.map +1 -0
  20. package/build/contractInterface.js +82 -17
  21. package/build/contractInterface.js.map +1 -0
  22. package/build/countryStats.d.ts +6 -1
  23. package/build/countryStats.d.ts.map +1 -0
  24. package/build/countryStats.js +33 -9
  25. package/build/countryStats.js.map +1 -0
  26. package/build/deployClient.d.ts.map +1 -0
  27. package/build/deployClient.js +1 -1
  28. package/build/deployClient.js.map +1 -0
  29. package/build/deviceIdentity.d.ts +68 -0
  30. package/build/deviceIdentity.d.ts.map +1 -0
  31. package/build/deviceIdentity.js +209 -0
  32. package/build/deviceIdentity.js.map +1 -0
  33. package/build/freezeClient.d.ts.map +1 -0
  34. package/build/freezeClient.js +1 -1
  35. package/build/freezeClient.js.map +1 -0
  36. package/build/generationClient.d.ts +1 -0
  37. package/build/generationClient.d.ts.map +1 -0
  38. package/build/generationClient.js +2 -2
  39. package/build/generationClient.js.map +1 -0
  40. package/build/index.d.ts +27 -37
  41. package/build/index.d.ts.map +1 -0
  42. package/build/index.js +85 -116
  43. package/build/index.js.map +1 -0
  44. package/build/licenseLeaf.d.ts +76 -0
  45. package/build/licenseLeaf.d.ts.map +1 -0
  46. package/build/licenseLeaf.js +189 -0
  47. package/build/licenseLeaf.js.map +1 -0
  48. package/build/licenseRecord.d.ts +20 -1
  49. package/build/licenseRecord.d.ts.map +1 -0
  50. package/build/licenseRecord.js +1 -1
  51. package/build/licenseRecord.js.map +1 -0
  52. package/build/licenseStore.d.ts +17 -2
  53. package/build/licenseStore.d.ts.map +1 -0
  54. package/build/licenseStore.js +167 -7
  55. package/build/licenseStore.js.map +1 -0
  56. package/build/manifestVerify.d.ts.map +1 -0
  57. package/build/manifestVerify.js +8 -3
  58. package/build/manifestVerify.js.map +1 -0
  59. package/build/migrateClient.d.ts +6 -21
  60. package/build/migrateClient.d.ts.map +1 -0
  61. package/build/migrateClient.js +30 -17
  62. package/build/migrateClient.js.map +1 -0
  63. package/build/onChainRoot.d.ts +67 -0
  64. package/build/onChainRoot.d.ts.map +1 -0
  65. package/build/onChainRoot.js +87 -0
  66. package/build/onChainRoot.js.map +1 -0
  67. package/build/ownershipLicenseHash.d.ts +25 -0
  68. package/build/ownershipLicenseHash.d.ts.map +1 -0
  69. package/build/ownershipLicenseHash.js +29 -0
  70. package/build/ownershipLicenseHash.js.map +1 -0
  71. package/build/ownershipSignature.d.ts +122 -0
  72. package/build/ownershipSignature.d.ts.map +1 -0
  73. package/build/ownershipSignature.js +244 -0
  74. package/build/ownershipSignature.js.map +1 -0
  75. package/build/proveErrors.d.ts +35 -0
  76. package/build/proveErrors.d.ts.map +1 -0
  77. package/build/proveErrors.js +49 -0
  78. package/build/proveErrors.js.map +1 -0
  79. package/build/refundClient.d.ts +2 -11
  80. package/build/refundClient.d.ts.map +1 -0
  81. package/build/refundClient.js +35 -17
  82. package/build/refundClient.js.map +1 -0
  83. package/build/renewClient.d.ts +4 -1
  84. package/build/renewClient.d.ts.map +1 -0
  85. package/build/renewClient.js +11 -4
  86. package/build/renewClient.js.map +1 -0
  87. package/build/seatName.d.ts +8 -0
  88. package/build/seatName.d.ts.map +1 -0
  89. package/build/seatName.js +136 -0
  90. package/build/seatName.js.map +1 -0
  91. package/build/statsClient.d.ts +47 -4
  92. package/build/statsClient.d.ts.map +1 -0
  93. package/build/statsClient.js +44 -5
  94. package/build/statsClient.js.map +1 -0
  95. package/build/trustAnchorVerify.d.ts +48 -0
  96. package/build/trustAnchorVerify.d.ts.map +1 -0
  97. package/build/trustAnchorVerify.js +431 -0
  98. package/build/trustAnchorVerify.js.map +1 -0
  99. package/build/trustAnchorVerifyCli.d.ts +35 -0
  100. package/build/trustAnchorVerifyCli.d.ts.map +1 -0
  101. package/build/trustAnchorVerifyCli.js +177 -0
  102. package/build/trustAnchorVerifyCli.js.map +1 -0
  103. package/build/txBroadcastClient.d.ts +26 -0
  104. package/build/txBroadcastClient.d.ts.map +1 -0
  105. package/build/txBroadcastClient.js +20 -0
  106. package/build/txBroadcastClient.js.map +1 -0
  107. package/build/unbindClient.d.ts +56 -0
  108. package/build/unbindClient.d.ts.map +1 -0
  109. package/build/unbindClient.js +113 -0
  110. package/build/unbindClient.js.map +1 -0
  111. package/build/vendorAccountClient.d.ts +56 -0
  112. package/build/vendorAccountClient.d.ts.map +1 -0
  113. package/build/vendorAccountClient.js +47 -0
  114. package/build/vendorAccountClient.js.map +1 -0
  115. package/build/verifyCore.d.ts +1 -1
  116. package/build/verifyCore.d.ts.map +1 -0
  117. package/build/verifyCore.js +11 -4
  118. package/build/verifyCore.js.map +1 -0
  119. package/build/verifyOffline.d.ts +85 -0
  120. package/build/verifyOffline.d.ts.map +1 -0
  121. package/build/verifyOffline.js +139 -0
  122. package/build/verifyOffline.js.map +1 -0
  123. package/build/verifyService.d.ts.map +1 -0
  124. package/build/verifyService.js +582 -455
  125. package/build/verifyService.js.map +1 -0
  126. package/build/verifyWithAncestors.d.ts +18 -0
  127. package/build/verifyWithAncestors.d.ts.map +1 -0
  128. package/build/verifyWithAncestors.js +119 -0
  129. package/build/verifyWithAncestors.js.map +1 -0
  130. package/build/zipToState.d.ts +2 -0
  131. package/build/zipToState.d.ts.map +1 -0
  132. package/build/zipToState.js +94 -0
  133. package/build/zipToState.js.map +1 -0
  134. package/package.json +57 -13
  135. package/src/appsLookup.ts +48 -0
  136. package/src/archiveBootstrap.ts +342 -0
  137. package/src/bindClient.ts +175 -0
  138. package/src/buyClient.ts +304 -0
  139. package/src/contractInterface.d.ts +61 -0
  140. package/src/contractInterface.ts +313 -0
  141. package/src/countryStats.ts +171 -0
  142. package/src/deployClient.ts +388 -0
  143. package/src/deviceIdentity.ts +249 -0
  144. package/src/freezeClient.ts +117 -0
  145. package/src/generationClient.ts +165 -0
  146. package/src/index.ts +329 -0
  147. package/src/licenseLeaf.ts +221 -0
  148. package/src/licenseRecord.ts +108 -0
  149. package/src/licenseStore.ts +538 -0
  150. package/src/manifestVerify.ts +111 -0
  151. package/src/migrateClient.ts +84 -0
  152. package/src/onChainRoot.ts +107 -0
  153. package/src/ownershipLicenseHash.ts +31 -0
  154. package/src/ownershipSignature.ts +354 -0
  155. package/src/proveErrors.ts +70 -0
  156. package/src/refundClient.ts +158 -0
  157. package/src/renewClient.ts +214 -0
  158. package/src/seatName.ts +165 -0
  159. package/src/statsClient.ts +235 -0
  160. package/src/trustAnchorVerify.ts +548 -0
  161. package/src/trustAnchorVerifyCli.ts +179 -0
  162. package/src/txBroadcastClient.ts +48 -0
  163. package/src/unbindClient.ts +149 -0
  164. package/src/vendorAccountClient.ts +110 -0
  165. package/src/verifyCore.ts +92 -0
  166. package/src/verifyOffline.ts +212 -0
  167. package/src/verifyService.ts +969 -0
  168. package/src/verifyWithAncestors.ts +153 -0
  169. package/src/zipToState.ts +94 -0
  170. package/build/licenseProof.d.ts +0 -26
  171. package/build/licenseProof.js +0 -41
  172. package/build/ownership.d.ts +0 -34
  173. package/build/ownership.js +0 -147
@@ -0,0 +1,87 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+ /**
4
+ * onChainRoot.ts
5
+ *
6
+ * Non-custodial trust anchor for `verifyOffline`. Reads the live
7
+ * `licensesRoot` (`zkapp.appState[0]`) directly from Mina — no
8
+ * licensing-service round-trip, no trust assumption about whoever
9
+ * served the witness / license record.
10
+ *
11
+ * Pair it with `verifyOffline` to close the trust loop:
12
+ *
13
+ * ```ts
14
+ * import { fetchOnChainLicensesRoot } from 'zklicensing/on-chain-root';
15
+ * import { verifyOffline } from 'zklicensing/verify-offline';
16
+ *
17
+ * // 1. Ask the licensing service for a witness + slot snapshot.
18
+ * const snap = await fetch(`${service}/witness?licenseHash=${h}&zkAppAddress=${a}`).then(r => r.json());
19
+ *
20
+ * // 2. Fetch the on-chain root INDEPENDENTLY, from Mina.
21
+ * const onChainRoot = await fetchOnChainLicensesRoot({ zkAppAddress: a, network: 'devnet' });
22
+ *
23
+ * // 3. Cross-check — if they disagree, the service is stale/wrong; bail.
24
+ * if (snap.root !== onChainRoot) throw new Error('service root disagrees with chain');
25
+ *
26
+ * // 4. Fold-verify the snapshot against the on-chain root.
27
+ * const res = verifyOffline({ licensesRoot: onChainRoot, ...snap });
28
+ * ```
29
+ *
30
+ * o1js is a peer dep — this file imports it dynamically so callers
31
+ * that only want `verifyOffline` (pure fold, no chain read) don't
32
+ * have to install o1js.
33
+ *
34
+ * Global-state caveat: o1js `Mina.setActiveInstance` mutates a module-
35
+ * scoped singleton. Long-lived hosts that talk to multiple Mina
36
+ * networks in the same process should treat this helper as a
37
+ * one-shot and re-set their own preferred network after use, or wrap
38
+ * it in a mutex.
39
+ */
40
+ // GraphQL endpoints published by Minascan. These are read-only public
41
+ // nodes — no auth, no rate-limit key. Vendors who prefer their own
42
+ // Mina node can override via `graphqlUrl` on the input.
43
+ export const DEFAULT_MINA_GRAPHQL_URLS = {
44
+ mainnet: 'https://api.minascan.io/node/mainnet/v1/graphql',
45
+ devnet: 'https://api.minascan.io/node/devnet/v1/graphql',
46
+ };
47
+ /**
48
+ * Read the live `licensesRoot` from the deployed LicensingApp's
49
+ * on-chain state. Returns the field value as a decimal string,
50
+ * matching the shape `verifyOffline` and the licensing service both
51
+ * consume.
52
+ *
53
+ * Throws on any of:
54
+ * - `o1js` not installed (peer dep missing);
55
+ * - `fetchAccount` returned an error (network down, wrong endpoint,
56
+ * account doesn't exist on this chain);
57
+ * - account exists but has no zkApp state (not a zkApp address, or
58
+ * contract deployed but `initialize()` never ran).
59
+ */
60
+ export async function fetchOnChainLicensesRoot(input) {
61
+ const { zkAppAddress } = input;
62
+ const network = input.network ?? 'devnet';
63
+ const graphqlUrl = input.graphqlUrl ?? DEFAULT_MINA_GRAPHQL_URLS[network];
64
+ let o1js;
65
+ try {
66
+ o1js = await import('o1js');
67
+ }
68
+ catch (err) {
69
+ throw new Error(`zklicensing/on-chain-root: o1js not installed — install it as a peer dep (${err?.message ?? err})`);
70
+ }
71
+ // o1js's networkId for devnet is 'testnet'; only mainnet uses 'mainnet'.
72
+ const networkId = network === 'mainnet' ? 'mainnet' : 'testnet';
73
+ const mina = o1js.Mina.Network({ networkId, mina: graphqlUrl });
74
+ o1js.Mina.setActiveInstance(mina);
75
+ const publicKey = o1js.PublicKey.fromBase58(zkAppAddress);
76
+ const { account, error } = await o1js.fetchAccount({ publicKey });
77
+ if (error) {
78
+ const detail = error.statusText ?? error.statusCode ?? JSON.stringify(error);
79
+ throw new Error(`fetchAccount(${zkAppAddress}) via ${graphqlUrl} failed: ${detail}`);
80
+ }
81
+ const root = account?.zkapp?.appState?.[0]?.toString?.();
82
+ if (!root) {
83
+ throw new Error(`zkApp ${zkAppAddress} has no appState — is the LicensingApp deployed and initialized on ${network}?`);
84
+ }
85
+ return root;
86
+ }
87
+ //# sourceMappingURL=onChainRoot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onChainRoot.js","sourceRoot":"","sources":["../src/onChainRoot.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,oCAAoC;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,sEAAsE;AACtE,mEAAmE;AACnE,wDAAwD;AACxD,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,iDAAiD;IAC1D,MAAM,EAAG,gDAAgD;CACjD,CAAC;AAYX;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAoC;IAEpC,MAAM,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;IAC/B,MAAM,OAAO,GAAgB,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC;IACvD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAE1E,IAAI,IAAS,CAAC;IACd,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,6EAA6E,GAAG,EAAE,OAAO,IAAI,GAAG,GAAG,CACpG,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,MAAM,SAAS,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IAChE,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAC1D,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IAClE,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC7E,MAAM,IAAI,KAAK,CAAC,gBAAgB,YAAY,SAAS,UAAU,YAAY,MAAM,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC;IACzD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,SAAS,YAAY,sEAAsE,OAAO,GAAG,CACtG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * ownershipLicenseHash.ts
3
+ *
4
+ * Field-side binding between the buyer's Pallas Schnorr pubkey and the
5
+ * `licenseHash` Field stored on-chain. The buy circuit and the buy client
6
+ * both compute this from the pubkey and assert equality — that mutual
7
+ * assertion is what proves knowledge of the passphrase without ever
8
+ * showing the passphrase to the network.
9
+ *
10
+ * A Pallas `PublicKey` is `(x: Field, isOdd: Bool)` — the two Fields that
11
+ * uniquely identify the compressed point. Poseidon-hashing them yields a
12
+ * single Field that matches the on-chain shape of every other `licenseHash`
13
+ * used by LicensingApp (which treats licenseHash as an opaque Field
14
+ * commitment, not a specific preimage).
15
+ */
16
+ import { PublicKey } from 'o1js';
17
+ /**
18
+ * Derive the Field-side `licenseHash` from a Pallas Schnorr pubkey. The
19
+ * hash is domain-free because the whole platform treats `licenseHash` as
20
+ * an opaque commitment — the on-chain contract never re-derives it from
21
+ * a preimage, so there's no cross-preimage collision surface to guard
22
+ * against here.
23
+ */
24
+ export declare function licenseHashFromPubkey(publicKey: PublicKey): string;
25
+ //# sourceMappingURL=ownershipLicenseHash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownershipLicenseHash.d.ts","sourceRoot":"","sources":["../src/ownershipLicenseHash.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAY,SAAS,EAAE,MAAM,MAAM,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAElE"}
@@ -0,0 +1,29 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+ /**
4
+ * ownershipLicenseHash.ts
5
+ *
6
+ * Field-side binding between the buyer's Pallas Schnorr pubkey and the
7
+ * `licenseHash` Field stored on-chain. The buy circuit and the buy client
8
+ * both compute this from the pubkey and assert equality — that mutual
9
+ * assertion is what proves knowledge of the passphrase without ever
10
+ * showing the passphrase to the network.
11
+ *
12
+ * A Pallas `PublicKey` is `(x: Field, isOdd: Bool)` — the two Fields that
13
+ * uniquely identify the compressed point. Poseidon-hashing them yields a
14
+ * single Field that matches the on-chain shape of every other `licenseHash`
15
+ * used by LicensingApp (which treats licenseHash as an opaque Field
16
+ * commitment, not a specific preimage).
17
+ */
18
+ import { Poseidon } from 'o1js';
19
+ /**
20
+ * Derive the Field-side `licenseHash` from a Pallas Schnorr pubkey. The
21
+ * hash is domain-free because the whole platform treats `licenseHash` as
22
+ * an opaque commitment — the on-chain contract never re-derives it from
23
+ * a preimage, so there's no cross-preimage collision surface to guard
24
+ * against here.
25
+ */
26
+ export function licenseHashFromPubkey(publicKey) {
27
+ return Poseidon.hash([publicKey.x, publicKey.isOdd.toField()]).toString();
28
+ }
29
+ //# sourceMappingURL=ownershipLicenseHash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownershipLicenseHash.js","sourceRoot":"","sources":["../src/ownershipLicenseHash.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,oCAAoC;AAEpC;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,QAAQ,EAAa,MAAM,MAAM,CAAC;AAE3C;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAoB;IACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * ownershipSignature.ts
3
+ *
4
+ * Schnorr-over-Pallas signing primitive for the two on-chain flows that
5
+ * need the passphrase-derived owner key:
6
+ *
7
+ * 1. The /prove/bind and /prove/unbind flows — the passphrase-derived
8
+ * keypair authorizes device slot mutations, and the signature is
9
+ * re-verified INSIDE the circuit by `Signature.verify()` so even a
10
+ * compromised licensing service cannot forge a bind.
11
+ * 2. The retained `ActivationMessage` shape (see below) — kept so external
12
+ * tooling can still sign an "I hold the passphrase for this license"
13
+ * assertion off-band. The platform no longer runs a challenge/respond
14
+ * endpoint; the shape is purely an SDK primitive now.
15
+ *
16
+ * Deterministic HKDF-SHA256 derivation of a seed from the passphrase,
17
+ * reduced to a Pallas scalar → o1js `PrivateKey`. The buyer can
18
+ * re-derive the same keypair anywhere from just the passphrase.
19
+ *
20
+ * o1js-dependent by design: the whole point of switching from Ed25519 to
21
+ * Pallas Schnorr is native in-circuit verification. Any client that
22
+ * signs an activation or binding challenge already ships o1js for the
23
+ * downstream buy/verify surface, so the "keep this module o1js-free"
24
+ * constraint that motivated the Ed25519 version no longer applies.
25
+ *
26
+ * HKDF still needs WebCrypto (Node ≥ 20, Chromium 137+, Safari 17+,
27
+ * Firefox 130+). The module throws at derive time if
28
+ * `globalThis.crypto.subtle` is unavailable — callers cannot silently
29
+ * fall back to a weaker path.
30
+ */
31
+ import { Field, PrivateKey, PublicKey, Signature } from 'o1js';
32
+ export declare const TAG_ACTIVATION = 1n;
33
+ export declare const TAG_BINDING = 2n;
34
+ export declare const TAG_UNBINDING = 3n;
35
+ /**
36
+ * HKDF-SHA256 with empty salt and a per-app domain-separation info
37
+ * string (`zkLicensing/v1/ownership|<zkAppAddress>`). Deterministic:
38
+ * same (passphrase, zkAppAddress) → same 32-byte seed. Different
39
+ * zkAppAddress → cryptographically unrelated seed even with the same
40
+ * passphrase.
41
+ */
42
+ export declare function deriveSeed(passphrase: string, zkAppAddress: string): Promise<Uint8Array>;
43
+ export type OwnershipKeypair = {
44
+ privateKey: PrivateKey;
45
+ publicKey: PublicKey;
46
+ publicKeyBase58: string;
47
+ };
48
+ /**
49
+ * Derive a Pallas Schnorr keypair deterministically from (passphrase,
50
+ * zkAppAddress). The keypair is bound to the specific app: the same
51
+ * passphrase used against a different app yields a different keypair,
52
+ * so a leaked passphrase cannot be replayed as an activation for any
53
+ * other app.
54
+ *
55
+ * The 32-byte HKDF output is interpreted as a big-endian integer and
56
+ * passed to `PrivateKey.fromBigInt`, which reduces it modulo the Pallas
57
+ * scalar field order internally. All 2^256 possible HKDF outputs map to
58
+ * valid private keys with (256 - log2(Fq_order))-bit modular bias — the
59
+ * scalar field is ~254 bits, so bias is <2^-253 and lies well below any
60
+ * practical distinguisher.
61
+ */
62
+ export declare function deriveOwnershipKeypair(passphrase: string, zkAppAddress: string): Promise<OwnershipKeypair>;
63
+ /**
64
+ * Activation assertion shape. Proves the caller holds the passphrase
65
+ * that derived the license's ownerPubKey. Retained as an SDK primitive
66
+ * so external tooling can still request / verify an off-band "I own
67
+ * this license" signature — the on-chain flows themselves do not use
68
+ * it.
69
+ */
70
+ export type ActivationMessage = {
71
+ licenseHash: string;
72
+ nonce: string;
73
+ zkAppAddress: string;
74
+ };
75
+ /**
76
+ * Binding / unbinding message — the /prove/bind and /prove/unbind
77
+ * flows. Authorizes a specific device commitment being placed into or
78
+ * cleared from a specific slot on a specific license.
79
+ */
80
+ export type BindingMessage = {
81
+ licenseHash: string;
82
+ deviceCommitment: string;
83
+ slotIndex: number;
84
+ nonce: string;
85
+ };
86
+ export type ChallengeMessage = ActivationMessage;
87
+ /**
88
+ * Canonicalize the activation message into the Field[] that
89
+ * `Signature.create` / `Signature.verify` hash. The domain-separation
90
+ * tag prefix means no captured activation signature can be replayed as
91
+ * a binding signature, even if all other fields collide.
92
+ */
93
+ export declare function activationMessageFields(msg: ActivationMessage): Field[];
94
+ export declare function bindingMessageFields(msg: BindingMessage): Field[];
95
+ export declare function unbindingMessageFields(msg: BindingMessage): Field[];
96
+ export declare function signActivation(privateKey: PrivateKey, msg: ActivationMessage): Signature;
97
+ export declare function signBinding(privateKey: PrivateKey, msg: BindingMessage): Signature;
98
+ export declare function signUnbinding(privateKey: PrivateKey, msg: BindingMessage): Signature;
99
+ export declare function signChallenge(privateKey: PrivateKey, msg: ChallengeMessage): Signature;
100
+ /**
101
+ * Verify an activation signature. Returns false (never throws) for any
102
+ * failure — malformed pubkey, malformed signature, actual signature
103
+ * mismatch. Callers get a single go/no-go decision without having to
104
+ * distinguish "invalid" from "unparseable."
105
+ */
106
+ export declare function verifyActivation(publicKey: PublicKey, msg: ActivationMessage, signature: Signature): boolean;
107
+ export declare function verifyBinding(publicKey: PublicKey, msg: BindingMessage, signature: Signature): boolean;
108
+ export declare function verifyUnbinding(publicKey: PublicKey, msg: BindingMessage, signature: Signature): boolean;
109
+ export declare function verifyChallenge(publicKey: PublicKey, msg: ChallengeMessage, signature: Signature): boolean;
110
+ /**
111
+ * Encode a signature for the wire (POST body, ProofFile JSON, etc).
112
+ * Base58 matches Mina's canonical serialization for both PublicKey and
113
+ * Signature, keeping the same encoding shape across everything the
114
+ * buyer's SDK produces.
115
+ */
116
+ export declare function encodeSignature(sig: Signature): string;
117
+ export declare function decodeSignature(base58: string): Signature;
118
+ export declare function encodePublicKey(pk: PublicKey): string;
119
+ export declare function decodePublicKey(base58: string): PublicKey;
120
+ export declare function encodeBase64(bytes: Uint8Array): string;
121
+ export declare function decodeBase64(b64: string): Uint8Array;
122
+ //# sourceMappingURL=ownershipSignature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownershipSignature.d.ts","sourceRoot":"","sources":["../src/ownershipSignature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,244 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+ /**
4
+ * ownershipSignature.ts
5
+ *
6
+ * Schnorr-over-Pallas signing primitive for the two on-chain flows that
7
+ * need the passphrase-derived owner key:
8
+ *
9
+ * 1. The /prove/bind and /prove/unbind flows — the passphrase-derived
10
+ * keypair authorizes device slot mutations, and the signature is
11
+ * re-verified INSIDE the circuit by `Signature.verify()` so even a
12
+ * compromised licensing service cannot forge a bind.
13
+ * 2. The retained `ActivationMessage` shape (see below) — kept so external
14
+ * tooling can still sign an "I hold the passphrase for this license"
15
+ * assertion off-band. The platform no longer runs a challenge/respond
16
+ * endpoint; the shape is purely an SDK primitive now.
17
+ *
18
+ * Deterministic HKDF-SHA256 derivation of a seed from the passphrase,
19
+ * reduced to a Pallas scalar → o1js `PrivateKey`. The buyer can
20
+ * re-derive the same keypair anywhere from just the passphrase.
21
+ *
22
+ * o1js-dependent by design: the whole point of switching from Ed25519 to
23
+ * Pallas Schnorr is native in-circuit verification. Any client that
24
+ * signs an activation or binding challenge already ships o1js for the
25
+ * downstream buy/verify surface, so the "keep this module o1js-free"
26
+ * constraint that motivated the Ed25519 version no longer applies.
27
+ *
28
+ * HKDF still needs WebCrypto (Node ≥ 20, Chromium 137+, Safari 17+,
29
+ * Firefox 130+). The module throws at derive time if
30
+ * `globalThis.crypto.subtle` is unavailable — callers cannot silently
31
+ * fall back to a weaker path.
32
+ */
33
+ import { Field, PrivateKey, PublicKey, Signature } from 'o1js';
34
+ // HKDF info binds the derivation to a specific zkApp address, so the
35
+ // same passphrase yields different keypairs for different apps. This
36
+ // makes credentials per-app: a leaked passphrase for app A cannot be
37
+ // replayed as an activation for app B. RFC 5869 designates `info` as
38
+ // the domain-separation channel (salt is for randomness, which we don't
39
+ // have here — the passphrase IS the secret input).
40
+ //
41
+ // The `v1` prefix reserves room to rotate the KDF or the target key type
42
+ // later without a silent collision — a future scheme bumps to `v2` and
43
+ // derives an unrelated key from the same passphrase.
44
+ const HKDF_INFO_PREFIX = 'zkLicensing/v1/ownership|';
45
+ const HKDF_SALT = new Uint8Array(0);
46
+ // Domain-separation tags baked into every signed message. Prevents a
47
+ // signature captured on one flow (activation) from being replayed on
48
+ // another (binding) — the first Field the signer hashes is the flow ID,
49
+ // so the two signatures live in disjoint spaces even at identical
50
+ // (licenseHash, nonce).
51
+ //
52
+ // Exported so `LicensingApp.bind()` / `unbind()` can rebuild the exact
53
+ // same Field[] preimage inside the circuit for `Signature.verify()`.
54
+ // Bumping these values is a hardfork — every off-chain signer and every
55
+ // deployed circuit would need to agree.
56
+ export const TAG_ACTIVATION = 1n;
57
+ export const TAG_BINDING = 2n;
58
+ export const TAG_UNBINDING = 3n;
59
+ function buildHkdfInfo(zkAppAddress) {
60
+ return new TextEncoder().encode(HKDF_INFO_PREFIX + zkAppAddress);
61
+ }
62
+ function utf8Bytes(s) {
63
+ return new TextEncoder().encode(s);
64
+ }
65
+ function bytesToBase64(bytes) {
66
+ let s = '';
67
+ for (const b of bytes)
68
+ s += String.fromCharCode(b);
69
+ return btoa(s);
70
+ }
71
+ function base64ToBytes(b64) {
72
+ const bin = atob(b64);
73
+ const out = new Uint8Array(bin.length);
74
+ for (let i = 0; i < bin.length; i++)
75
+ out[i] = bin.charCodeAt(i);
76
+ return out;
77
+ }
78
+ function subtle() {
79
+ const s = globalThis.crypto?.subtle;
80
+ if (!s) {
81
+ throw new Error('WebCrypto unavailable — ownershipSignature requires Node ≥ 20 or a modern browser (Chromium 137+, Safari 17+, Firefox 130+).');
82
+ }
83
+ return s;
84
+ }
85
+ function bytesToBigInt(bytes) {
86
+ let n = 0n;
87
+ for (const b of bytes)
88
+ n = (n << 8n) | BigInt(b);
89
+ return n;
90
+ }
91
+ /**
92
+ * HKDF-SHA256 with empty salt and a per-app domain-separation info
93
+ * string (`zkLicensing/v1/ownership|<zkAppAddress>`). Deterministic:
94
+ * same (passphrase, zkAppAddress) → same 32-byte seed. Different
95
+ * zkAppAddress → cryptographically unrelated seed even with the same
96
+ * passphrase.
97
+ */
98
+ export async function deriveSeed(passphrase, zkAppAddress) {
99
+ const baseKey = await subtle().importKey('raw', utf8Bytes(passphrase), 'HKDF', false, ['deriveBits']);
100
+ const info = buildHkdfInfo(zkAppAddress);
101
+ const bits = await subtle().deriveBits({
102
+ name: 'HKDF',
103
+ hash: 'SHA-256',
104
+ salt: HKDF_SALT,
105
+ info: info,
106
+ }, baseKey, 256);
107
+ return new Uint8Array(bits);
108
+ }
109
+ /**
110
+ * Derive a Pallas Schnorr keypair deterministically from (passphrase,
111
+ * zkAppAddress). The keypair is bound to the specific app: the same
112
+ * passphrase used against a different app yields a different keypair,
113
+ * so a leaked passphrase cannot be replayed as an activation for any
114
+ * other app.
115
+ *
116
+ * The 32-byte HKDF output is interpreted as a big-endian integer and
117
+ * passed to `PrivateKey.fromBigInt`, which reduces it modulo the Pallas
118
+ * scalar field order internally. All 2^256 possible HKDF outputs map to
119
+ * valid private keys with (256 - log2(Fq_order))-bit modular bias — the
120
+ * scalar field is ~254 bits, so bias is <2^-253 and lies well below any
121
+ * practical distinguisher.
122
+ */
123
+ export async function deriveOwnershipKeypair(passphrase, zkAppAddress) {
124
+ const seed = await deriveSeed(passphrase, zkAppAddress);
125
+ const privateKey = PrivateKey.fromBigInt(bytesToBigInt(seed));
126
+ const publicKey = privateKey.toPublicKey();
127
+ return { privateKey, publicKey, publicKeyBase58: publicKey.toBase58() };
128
+ }
129
+ /**
130
+ * Canonicalize the activation message into the Field[] that
131
+ * `Signature.create` / `Signature.verify` hash. The domain-separation
132
+ * tag prefix means no captured activation signature can be replayed as
133
+ * a binding signature, even if all other fields collide.
134
+ */
135
+ export function activationMessageFields(msg) {
136
+ const addr = PublicKey.fromBase58(msg.zkAppAddress);
137
+ return [
138
+ Field(TAG_ACTIVATION),
139
+ Field(msg.licenseHash),
140
+ Field(msg.nonce),
141
+ addr.x,
142
+ addr.isOdd.toField(),
143
+ ];
144
+ }
145
+ /**
146
+ * Canonicalize a binding or unbinding message. The `tag` argument
147
+ * distinguishes bind (TAG_BINDING) from unbind (TAG_UNBINDING) so a
148
+ * bind signature captured off-chain cannot be replayed as an unbind
149
+ * even at identical (licenseHash, deviceCommitment, slotIndex, nonce).
150
+ */
151
+ function bindingLikeFields(tag, msg) {
152
+ return [
153
+ Field(tag),
154
+ Field(msg.licenseHash),
155
+ Field(msg.deviceCommitment),
156
+ Field(msg.slotIndex),
157
+ Field(msg.nonce),
158
+ ];
159
+ }
160
+ export function bindingMessageFields(msg) {
161
+ return bindingLikeFields(TAG_BINDING, msg);
162
+ }
163
+ export function unbindingMessageFields(msg) {
164
+ return bindingLikeFields(TAG_UNBINDING, msg);
165
+ }
166
+ // ── Signing ───────────────────────────────────────────────────────
167
+ export function signActivation(privateKey, msg) {
168
+ return Signature.create(privateKey, activationMessageFields(msg));
169
+ }
170
+ export function signBinding(privateKey, msg) {
171
+ return Signature.create(privateKey, bindingMessageFields(msg));
172
+ }
173
+ export function signUnbinding(privateKey, msg) {
174
+ return Signature.create(privateKey, unbindingMessageFields(msg));
175
+ }
176
+ // Retained for back-compat with the Ed25519-era name. New code should
177
+ // call signActivation directly.
178
+ export function signChallenge(privateKey, msg) {
179
+ return signActivation(privateKey, msg);
180
+ }
181
+ // ── Verification ──────────────────────────────────────────────────
182
+ /**
183
+ * Verify an activation signature. Returns false (never throws) for any
184
+ * failure — malformed pubkey, malformed signature, actual signature
185
+ * mismatch. Callers get a single go/no-go decision without having to
186
+ * distinguish "invalid" from "unparseable."
187
+ */
188
+ export function verifyActivation(publicKey, msg, signature) {
189
+ try {
190
+ return signature.verify(publicKey, activationMessageFields(msg)).toBoolean();
191
+ }
192
+ catch {
193
+ return false;
194
+ }
195
+ }
196
+ export function verifyBinding(publicKey, msg, signature) {
197
+ try {
198
+ return signature.verify(publicKey, bindingMessageFields(msg)).toBoolean();
199
+ }
200
+ catch {
201
+ return false;
202
+ }
203
+ }
204
+ export function verifyUnbinding(publicKey, msg, signature) {
205
+ try {
206
+ return signature.verify(publicKey, unbindingMessageFields(msg)).toBoolean();
207
+ }
208
+ catch {
209
+ return false;
210
+ }
211
+ }
212
+ // Back-compat alias, see signChallenge.
213
+ export function verifyChallenge(publicKey, msg, signature) {
214
+ return verifyActivation(publicKey, msg, signature);
215
+ }
216
+ // ── Wire encoding helpers ─────────────────────────────────────────
217
+ /**
218
+ * Encode a signature for the wire (POST body, ProofFile JSON, etc).
219
+ * Base58 matches Mina's canonical serialization for both PublicKey and
220
+ * Signature, keeping the same encoding shape across everything the
221
+ * buyer's SDK produces.
222
+ */
223
+ export function encodeSignature(sig) {
224
+ return sig.toBase58();
225
+ }
226
+ export function decodeSignature(base58) {
227
+ return Signature.fromBase58(base58);
228
+ }
229
+ export function encodePublicKey(pk) {
230
+ return pk.toBase58();
231
+ }
232
+ export function decodePublicKey(base58) {
233
+ return PublicKey.fromBase58(base58);
234
+ }
235
+ // Retained so callers that already do `encodeBase64(seed)` for logging
236
+ // or persistence keep working — this module no longer produces raw byte
237
+ // pubkeys or signatures, only o1js objects with .toBase58().
238
+ export function encodeBase64(bytes) {
239
+ return bytesToBase64(bytes);
240
+ }
241
+ export function decodeBase64(b64) {
242
+ return base64ToBytes(b64);
243
+ }
244
+ //# sourceMappingURL=ownershipSignature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownershipSignature.js","sourceRoot":"","sources":["../src/ownershipSignature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * proveErrors.ts
3
+ *
4
+ * Shared typed error for the "proving queue busy" 409 surface returned by
5
+ * /prove/buy, /prove/buy-no-escrow, /prove/renew, /prove/migrate, and
6
+ * /prove/refund. The keeper serializes all five endpoints on a per-app
7
+ * in-flight slot: while a proof is out with a buyer waiting to be signed
8
+ * (or a signed tx is waiting to land), further requests against the same
9
+ * app get an immediate 409 with a `retryAfterMs` hint. Callers should catch
10
+ * this and wait+retry rather than surfacing the failure — the wallet popup
11
+ * never opens for a proof with no live chance of landing.
12
+ *
13
+ * `queueDepth` is 1 in v1 (single slot per app); the field exists so that a
14
+ * future Tier-1 optimistic-chaining pass can widen it without breaking
15
+ * clients written today.
16
+ */
17
+ export declare class ProveQueueBusyError extends Error {
18
+ readonly status = 409;
19
+ readonly retryAfterMs: number;
20
+ readonly queueDepth: number;
21
+ constructor(message: string, retryAfterMs: number, queueDepth: number);
22
+ }
23
+ export type ProveQueueBusyBody = {
24
+ error: 'proving queue busy';
25
+ retryAfterMs: number;
26
+ queueDepth?: number;
27
+ };
28
+ export type ParsedErrorBody = {
29
+ status: number;
30
+ body: Record<string, unknown>;
31
+ message: string;
32
+ };
33
+ export declare function readErrorBody(resp: Response): Promise<ParsedErrorBody>;
34
+ export declare function tryQueueBusyFromBody(parsed: ParsedErrorBody): ProveQueueBusyError | null;
35
+ //# sourceMappingURL=proveErrors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proveErrors.d.ts","sourceRoot":"","sources":["../src/proveErrors.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;GAeG;AAEH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,OAAO;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAI,MAAM,CAAC;gBAClB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM;CAMtE;AAGD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAS,oBAAoB,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAG,MAAM,CAAC;CACtB,CAAC;AAKF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,CAO5E;AAID,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,GAAG,IAAI,CASxF"}
@@ -0,0 +1,49 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+ /**
4
+ * proveErrors.ts
5
+ *
6
+ * Shared typed error for the "proving queue busy" 409 surface returned by
7
+ * /prove/buy, /prove/buy-no-escrow, /prove/renew, /prove/migrate, and
8
+ * /prove/refund. The keeper serializes all five endpoints on a per-app
9
+ * in-flight slot: while a proof is out with a buyer waiting to be signed
10
+ * (or a signed tx is waiting to land), further requests against the same
11
+ * app get an immediate 409 with a `retryAfterMs` hint. Callers should catch
12
+ * this and wait+retry rather than surfacing the failure — the wallet popup
13
+ * never opens for a proof with no live chance of landing.
14
+ *
15
+ * `queueDepth` is 1 in v1 (single slot per app); the field exists so that a
16
+ * future Tier-1 optimistic-chaining pass can widen it without breaking
17
+ * clients written today.
18
+ */
19
+ export class ProveQueueBusyError extends Error {
20
+ status = 409;
21
+ retryAfterMs;
22
+ queueDepth;
23
+ constructor(message, retryAfterMs, queueDepth) {
24
+ super(message);
25
+ this.name = 'ProveQueueBusyError';
26
+ this.retryAfterMs = retryAfterMs;
27
+ this.queueDepth = queueDepth;
28
+ }
29
+ }
30
+ export async function readErrorBody(resp) {
31
+ let body = {};
32
+ try {
33
+ body = await resp.json();
34
+ }
35
+ catch { /* body wasn't JSON */ }
36
+ const message = typeof body.error === 'string' ? body.error : `HTTP ${resp.status}`;
37
+ return { status: resp.status, body, message };
38
+ }
39
+ // Returns a ProveQueueBusyError if the parsed body matches the queue-busy
40
+ // shape; otherwise null. Cheap to call — no I/O.
41
+ export function tryQueueBusyFromBody(parsed) {
42
+ if (parsed.status !== 409)
43
+ return null;
44
+ const { body } = parsed;
45
+ if (body.error !== 'proving queue busy' || typeof body.retryAfterMs !== 'number')
46
+ return null;
47
+ return new ProveQueueBusyError(body.error, body.retryAfterMs, typeof body.queueDepth === 'number' ? body.queueDepth : 1);
48
+ }
49
+ //# sourceMappingURL=proveErrors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proveErrors.js","sourceRoot":"","sources":["../src/proveErrors.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,oCAAoC;AAEpC;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,MAAM,GAAG,GAAG,CAAC;IACb,YAAY,CAAS;IACrB,UAAU,CAAW;IAC9B,YAAY,OAAe,EAAE,YAAoB,EAAE,UAAkB;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAmBD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IAChD,IAAI,IAAI,GAA4B,EAAE,CAAC;IACvC,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAA6B,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;IACpF,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAChD,CAAC;AAED,0EAA0E;AAC1E,iDAAiD;AACjD,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC1D,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;IACxB,IAAI,IAAI,CAAC,KAAK,KAAK,oBAAoB,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC9F,OAAO,IAAI,mBAAmB,CAC5B,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,YAAY,EACjB,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAC1D,CAAC;AACJ,CAAC"}