zklicensing 0.1.0-beta.2 → 0.1.0-beta.21

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 (317) hide show
  1. package/README.md +417 -201
  2. package/build/agent.d.ts +371 -0
  3. package/build/agent.d.ts.map +1 -0
  4. package/build/agent.js +553 -0
  5. package/build/agent.js.map +1 -0
  6. package/build/agentActions.d.ts +841 -0
  7. package/build/agentActions.d.ts.map +1 -0
  8. package/build/agentActions.js +581 -0
  9. package/build/agentActions.js.map +1 -0
  10. package/build/agentRecord.d.ts +83 -0
  11. package/build/agentRecord.d.ts.map +1 -0
  12. package/build/agentRecord.js +69 -0
  13. package/build/agentRecord.js.map +1 -0
  14. package/build/agentVendor.d.ts +220 -0
  15. package/build/agentVendor.d.ts.map +1 -0
  16. package/build/agentVendor.js +306 -0
  17. package/build/agentVendor.js.map +1 -0
  18. package/build/appsLookup.d.ts +14 -0
  19. package/build/appsLookup.d.ts.map +1 -0
  20. package/build/appsLookup.js +12 -0
  21. package/build/appsLookup.js.map +1 -0
  22. package/build/archiveBootstrap.d.ts +31 -5
  23. package/build/archiveBootstrap.d.ts.map +1 -0
  24. package/build/archiveBootstrap.js +140 -47
  25. package/build/archiveBootstrap.js.map +1 -0
  26. package/build/atomicJson.d.ts +5 -0
  27. package/build/atomicJson.d.ts.map +1 -0
  28. package/build/atomicJson.js +55 -0
  29. package/build/atomicJson.js.map +1 -0
  30. package/build/bindClient.d.ts +85 -0
  31. package/build/bindClient.d.ts.map +1 -0
  32. package/build/bindClient.js +187 -0
  33. package/build/bindClient.js.map +1 -0
  34. package/build/buyClient.d.ts +27 -4
  35. package/build/buyClient.d.ts.map +1 -0
  36. package/build/buyClient.js +72 -26
  37. package/build/buyClient.js.map +1 -0
  38. package/build/chainHead.d.ts +61 -0
  39. package/build/chainHead.d.ts.map +1 -0
  40. package/build/chainHead.js +88 -0
  41. package/build/chainHead.js.map +1 -0
  42. package/build/compactMerkleMap.d.ts +72 -0
  43. package/build/compactMerkleMap.d.ts.map +1 -0
  44. package/build/compactMerkleMap.js +224 -0
  45. package/build/compactMerkleMap.js.map +1 -0
  46. package/build/contractInterface.d.ts +191 -51
  47. package/build/contractInterface.d.ts.map +1 -0
  48. package/build/contractInterface.js +234 -43
  49. package/build/contractInterface.js.map +1 -0
  50. package/build/contractTypes.d.ts +9 -0
  51. package/build/contractTypes.d.ts.map +1 -0
  52. package/build/contractTypes.js +28 -0
  53. package/build/contractTypes.js.map +1 -0
  54. package/build/countryStats.d.ts +12 -3
  55. package/build/countryStats.d.ts.map +1 -0
  56. package/build/countryStats.js +87 -23
  57. package/build/countryStats.js.map +1 -0
  58. package/build/deployClient.d.ts +138 -24
  59. package/build/deployClient.d.ts.map +1 -0
  60. package/build/deployClient.js +91 -31
  61. package/build/deployClient.js.map +1 -0
  62. package/build/deviceIdentity.d.ts +68 -0
  63. package/build/deviceIdentity.d.ts.map +1 -0
  64. package/build/deviceIdentity.js +209 -0
  65. package/build/deviceIdentity.js.map +1 -0
  66. package/build/freezeClient.d.ts +26 -6
  67. package/build/freezeClient.d.ts.map +1 -0
  68. package/build/freezeClient.js +30 -6
  69. package/build/freezeClient.js.map +1 -0
  70. package/build/generationClient.d.ts +6 -3
  71. package/build/generationClient.d.ts.map +1 -0
  72. package/build/generationClient.js +25 -38
  73. package/build/generationClient.js.map +1 -0
  74. package/build/holderSecret.d.ts +70 -0
  75. package/build/holderSecret.d.ts.map +1 -0
  76. package/build/holderSecret.js +76 -0
  77. package/build/holderSecret.js.map +1 -0
  78. package/build/index.d.ts +38 -38
  79. package/build/index.d.ts.map +1 -0
  80. package/build/index.js +97 -121
  81. package/build/index.js.map +1 -0
  82. package/build/keysClient.d.ts +142 -0
  83. package/build/keysClient.d.ts.map +1 -0
  84. package/build/keysClient.js +109 -0
  85. package/build/keysClient.js.map +1 -0
  86. package/build/licenseLeaf.d.ts +76 -0
  87. package/build/licenseLeaf.d.ts.map +1 -0
  88. package/build/licenseLeaf.js +189 -0
  89. package/build/licenseLeaf.js.map +1 -0
  90. package/build/licenseRecord.d.ts +68 -5
  91. package/build/licenseRecord.d.ts.map +1 -0
  92. package/build/licenseRecord.js +26 -1
  93. package/build/licenseRecord.js.map +1 -0
  94. package/build/licenseStore.d.ts +81 -4
  95. package/build/licenseStore.d.ts.map +1 -0
  96. package/build/licenseStore.js +400 -30
  97. package/build/licenseStore.js.map +1 -0
  98. package/build/licenseTrustless.d.ts +43 -0
  99. package/build/licenseTrustless.d.ts.map +1 -0
  100. package/build/licenseTrustless.js +180 -0
  101. package/build/licenseTrustless.js.map +1 -0
  102. package/build/manifestVerify.d.ts +29 -1
  103. package/build/manifestVerify.d.ts.map +1 -0
  104. package/build/manifestVerify.js +46 -4
  105. package/build/manifestVerify.js.map +1 -0
  106. package/build/minaPriceHistory.d.ts +9 -0
  107. package/build/minaPriceHistory.d.ts.map +1 -0
  108. package/build/minaPriceHistory.js +152 -0
  109. package/build/minaPriceHistory.js.map +1 -0
  110. package/build/onChainRoot.d.ts +60 -0
  111. package/build/onChainRoot.d.ts.map +1 -0
  112. package/build/onChainRoot.js +74 -0
  113. package/build/onChainRoot.js.map +1 -0
  114. package/build/ownershipLicenseHash.d.ts +25 -0
  115. package/build/ownershipLicenseHash.d.ts.map +1 -0
  116. package/build/ownershipLicenseHash.js +29 -0
  117. package/build/ownershipLicenseHash.js.map +1 -0
  118. package/build/ownershipSignature.d.ts +153 -0
  119. package/build/ownershipSignature.d.ts.map +1 -0
  120. package/build/ownershipSignature.js +297 -0
  121. package/build/ownershipSignature.js.map +1 -0
  122. package/build/permitCheckFlow.d.ts +155 -0
  123. package/build/permitCheckFlow.d.ts.map +1 -0
  124. package/build/permitCheckFlow.js +196 -0
  125. package/build/permitCheckFlow.js.map +1 -0
  126. package/build/permitDescribe.d.ts +124 -0
  127. package/build/permitDescribe.d.ts.map +1 -0
  128. package/build/permitDescribe.js +324 -0
  129. package/build/permitDescribe.js.map +1 -0
  130. package/build/permitInterface.d.ts +85 -0
  131. package/build/permitInterface.d.ts.map +1 -0
  132. package/build/permitInterface.js +121 -0
  133. package/build/permitInterface.js.map +1 -0
  134. package/build/permitMessages.d.ts +313 -0
  135. package/build/permitMessages.d.ts.map +1 -0
  136. package/build/permitMessages.js +462 -0
  137. package/build/permitMessages.js.map +1 -0
  138. package/build/permitProof.d.ts +108 -0
  139. package/build/permitProof.d.ts.map +1 -0
  140. package/build/permitProof.js +202 -0
  141. package/build/permitProof.js.map +1 -0
  142. package/build/permitRegistryRebuild.d.ts +166 -0
  143. package/build/permitRegistryRebuild.d.ts.map +1 -0
  144. package/build/permitRegistryRebuild.js +347 -0
  145. package/build/permitRegistryRebuild.js.map +1 -0
  146. package/build/permitTree.d.ts +88 -0
  147. package/build/permitTree.d.ts.map +1 -0
  148. package/build/permitTree.js +230 -0
  149. package/build/permitTree.js.map +1 -0
  150. package/build/permitsClient.d.ts +680 -0
  151. package/build/permitsClient.d.ts.map +1 -0
  152. package/build/permitsClient.js +597 -0
  153. package/build/permitsClient.js.map +1 -0
  154. package/build/proveErrors.d.ts +50 -0
  155. package/build/proveErrors.d.ts.map +1 -0
  156. package/build/proveErrors.js +76 -0
  157. package/build/proveErrors.js.map +1 -0
  158. package/build/receipt.d.ts +95 -0
  159. package/build/receipt.d.ts.map +1 -0
  160. package/build/receipt.js +84 -0
  161. package/build/receipt.js.map +1 -0
  162. package/build/refundClient.d.ts +7 -12
  163. package/build/refundClient.d.ts.map +1 -0
  164. package/build/refundClient.js +52 -21
  165. package/build/refundClient.js.map +1 -0
  166. package/build/registryTrustVerify.d.ts +78 -0
  167. package/build/registryTrustVerify.d.ts.map +1 -0
  168. package/build/registryTrustVerify.js +129 -0
  169. package/build/registryTrustVerify.js.map +1 -0
  170. package/build/releaseClient.d.ts +26 -0
  171. package/build/releaseClient.d.ts.map +1 -0
  172. package/build/releaseClient.js +65 -0
  173. package/build/releaseClient.js.map +1 -0
  174. package/build/renewClient.d.ts +28 -2
  175. package/build/renewClient.d.ts.map +1 -0
  176. package/build/renewClient.js +31 -27
  177. package/build/renewClient.js.map +1 -0
  178. package/build/seatName.d.ts +8 -0
  179. package/build/seatName.d.ts.map +1 -0
  180. package/build/seatName.js +136 -0
  181. package/build/seatName.js.map +1 -0
  182. package/build/slotConstants.d.ts +15 -0
  183. package/build/slotConstants.d.ts.map +1 -0
  184. package/build/slotConstants.js +17 -0
  185. package/build/slotConstants.js.map +1 -0
  186. package/build/statsClient.d.ts +61 -7
  187. package/build/statsClient.d.ts.map +1 -0
  188. package/build/statsClient.js +44 -5
  189. package/build/statsClient.js.map +1 -0
  190. package/build/successorChain.d.ts +35 -0
  191. package/build/successorChain.d.ts.map +1 -0
  192. package/build/successorChain.js +69 -0
  193. package/build/successorChain.js.map +1 -0
  194. package/build/taxId.d.ts +44 -0
  195. package/build/taxId.d.ts.map +1 -0
  196. package/build/taxId.js +212 -0
  197. package/build/taxId.js.map +1 -0
  198. package/build/trustAnchorVerify.d.ts +49 -0
  199. package/build/trustAnchorVerify.d.ts.map +1 -0
  200. package/build/trustAnchorVerify.js +432 -0
  201. package/build/trustAnchorVerify.js.map +1 -0
  202. package/build/trustAnchorVerifyCli.d.ts +45 -0
  203. package/build/trustAnchorVerifyCli.d.ts.map +1 -0
  204. package/build/trustAnchorVerifyCli.js +235 -0
  205. package/build/trustAnchorVerifyCli.js.map +1 -0
  206. package/build/txBroadcastClient.d.ts +26 -0
  207. package/build/txBroadcastClient.d.ts.map +1 -0
  208. package/build/txBroadcastClient.js +20 -0
  209. package/build/txBroadcastClient.js.map +1 -0
  210. package/build/unbindClient.d.ts +77 -0
  211. package/build/unbindClient.d.ts.map +1 -0
  212. package/build/unbindClient.js +169 -0
  213. package/build/unbindClient.js.map +1 -0
  214. package/build/units.d.ts +28 -0
  215. package/build/units.d.ts.map +1 -0
  216. package/build/units.js +65 -0
  217. package/build/units.js.map +1 -0
  218. package/build/vatRates.d.ts +54 -0
  219. package/build/vatRates.d.ts.map +1 -0
  220. package/build/vatRates.js +82 -0
  221. package/build/vatRates.js.map +1 -0
  222. package/build/vatRatesData.d.ts +5 -0
  223. package/build/vatRatesData.d.ts.map +1 -0
  224. package/build/vatRatesData.js +42 -0
  225. package/build/vatRatesData.js.map +1 -0
  226. package/build/vendorAccountClient.d.ts +66 -0
  227. package/build/vendorAccountClient.d.ts.map +1 -0
  228. package/build/vendorAccountClient.js +47 -0
  229. package/build/vendorAccountClient.js.map +1 -0
  230. package/build/verifyCore.d.ts +4 -1
  231. package/build/verifyCore.d.ts.map +1 -0
  232. package/build/verifyCore.js +21 -10
  233. package/build/verifyCore.js.map +1 -0
  234. package/build/verifyOffline.d.ts +85 -0
  235. package/build/verifyOffline.d.ts.map +1 -0
  236. package/build/verifyOffline.js +139 -0
  237. package/build/verifyOffline.js.map +1 -0
  238. package/build/verifyService.d.ts.map +1 -0
  239. package/build/verifyService.js +571 -501
  240. package/build/verifyService.js.map +1 -0
  241. package/build/verifyWithAncestors.d.ts +19 -0
  242. package/build/verifyWithAncestors.d.ts.map +1 -0
  243. package/build/verifyWithAncestors.js +127 -0
  244. package/build/verifyWithAncestors.js.map +1 -0
  245. package/build/zipToState.d.ts +2 -0
  246. package/build/zipToState.d.ts.map +1 -0
  247. package/build/zipToState.js +94 -0
  248. package/build/zipToState.js.map +1 -0
  249. package/package.json +141 -16
  250. package/src/agent.ts +682 -0
  251. package/src/agentActions.ts +637 -0
  252. package/src/agentRecord.ts +121 -0
  253. package/src/agentVendor.ts +356 -0
  254. package/src/appsLookup.ts +48 -0
  255. package/src/archiveBootstrap.ts +386 -0
  256. package/src/atomicJson.ts +53 -0
  257. package/src/bindClient.ts +297 -0
  258. package/src/buyClient.ts +344 -0
  259. package/src/chainHead.ts +106 -0
  260. package/src/compactMerkleMap.ts +222 -0
  261. package/src/contractInterface.ts +465 -0
  262. package/src/contractTypes.ts +31 -0
  263. package/src/countryStats.ts +200 -0
  264. package/src/deployClient.ts +517 -0
  265. package/src/deviceIdentity.ts +249 -0
  266. package/src/freezeClient.ts +162 -0
  267. package/src/generationClient.ts +145 -0
  268. package/src/holderSecret.ts +91 -0
  269. package/src/index.ts +362 -0
  270. package/src/keysClient.ts +135 -0
  271. package/src/licenseLeaf.ts +221 -0
  272. package/src/licenseRecord.ts +190 -0
  273. package/src/licenseStore.ts +767 -0
  274. package/src/licenseTrustless.ts +207 -0
  275. package/src/manifestVerify.ts +164 -0
  276. package/src/minaPriceHistory.ts +166 -0
  277. package/src/onChainRoot.ts +90 -0
  278. package/src/ownershipLicenseHash.ts +31 -0
  279. package/src/ownershipSignature.ts +421 -0
  280. package/src/permitCheckFlow.ts +227 -0
  281. package/src/permitDescribe.ts +362 -0
  282. package/src/permitInterface.ts +146 -0
  283. package/src/permitMessages.ts +572 -0
  284. package/src/permitProof.ts +230 -0
  285. package/src/permitRegistryRebuild.ts +423 -0
  286. package/src/permitTree.ts +232 -0
  287. package/src/permitsClient.ts +746 -0
  288. package/src/proveErrors.ts +98 -0
  289. package/src/receipt.ts +156 -0
  290. package/src/refundClient.ts +177 -0
  291. package/src/registryTrustVerify.ts +192 -0
  292. package/src/releaseClient.ts +101 -0
  293. package/src/renewClient.ts +234 -0
  294. package/src/seatName.ts +165 -0
  295. package/src/slotConstants.ts +18 -0
  296. package/src/statsClient.ts +249 -0
  297. package/src/successorChain.ts +87 -0
  298. package/src/taxId.ts +227 -0
  299. package/src/trustAnchorVerify.ts +550 -0
  300. package/src/trustAnchorVerifyCli.ts +234 -0
  301. package/src/txBroadcastClient.ts +48 -0
  302. package/src/unbindClient.ts +233 -0
  303. package/src/units.ts +74 -0
  304. package/src/vatRates.ts +100 -0
  305. package/src/vatRatesData.ts +43 -0
  306. package/src/vendorAccountClient.ts +120 -0
  307. package/src/verifyCore.ts +100 -0
  308. package/src/verifyOffline.ts +212 -0
  309. package/src/verifyService.ts +982 -0
  310. package/src/verifyWithAncestors.ts +163 -0
  311. package/src/zipToState.ts +94 -0
  312. package/build/licenseProof.d.ts +0 -26
  313. package/build/licenseProof.js +0 -41
  314. package/build/migrateClient.d.ts +0 -43
  315. package/build/migrateClient.js +0 -47
  316. package/build/ownership.d.ts +0 -34
  317. package/build/ownership.js +0 -147
@@ -0,0 +1,74 @@
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
+ * The read goes to the node's GraphQL API directly: no o1js is needed
31
+ * and o1js's global Mina instance is never touched, so it is safe in
32
+ * long-lived hosts that talk to several networks.
33
+ */
34
+ // Default read-only public Mina GraphQL endpoints. Devnet points at
35
+ // o1Labs' Mesa cluster (the current stable devnet node published at
36
+ // devnet-plain-1.gcp.o1test.net); mainnet uses Minascan's public node.
37
+ // Neither requires auth or a rate-limit key. Vendors who prefer their
38
+ // own Mina node can override via `graphqlUrl` on the input.
39
+ export const DEFAULT_MINA_GRAPHQL_URLS = {
40
+ mainnet: 'https://api.minascan.io/node/mainnet/v1/graphql',
41
+ devnet: 'https://devnet-plain-1.gcp.o1test.net/graphql',
42
+ };
43
+ /**
44
+ * Read the live `licensesRoot` from the deployed LicensingApp's
45
+ * on-chain state. Returns the field value as a decimal string,
46
+ * matching the shape `verifyOffline` and the licensing service both
47
+ * consume.
48
+ *
49
+ * Throws on any of:
50
+ * - `fetchAccount` returned an error (network down, wrong endpoint,
51
+ * account doesn't exist on this chain);
52
+ * - account exists but has no zkApp state (not a zkApp address, or
53
+ * contract deployed but `initialize()` never ran).
54
+ */
55
+ export async function fetchOnChainLicensesRoot(input) {
56
+ const { zkAppAddress } = input;
57
+ const network = input.network ?? 'devnet';
58
+ const graphqlUrl = input.graphqlUrl ?? DEFAULT_MINA_GRAPHQL_URLS[network];
59
+ // Over the node's GraphQL API: no o1js, no global Mina instance.
60
+ const { readZkappAccount } = await import('./chainHead.js');
61
+ let account;
62
+ try {
63
+ account = await readZkappAccount({ address: zkAppAddress, graphqlUrl });
64
+ }
65
+ catch (e) {
66
+ throw new Error(`fetchAccount(${zkAppAddress}) via ${graphqlUrl} failed: ${e?.message ?? e}`);
67
+ }
68
+ const root = account.found ? account.appState?.[0] : undefined;
69
+ if (!root) {
70
+ throw new Error(`zkApp ${zkAppAddress} has no appState — is the LicensingApp deployed and initialized on ${network}?`);
71
+ }
72
+ return root;
73
+ }
74
+ //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,oEAAoE;AACpE,oEAAoE;AACpE,uEAAuE;AACvE,sEAAsE;AACtE,4DAA4D;AAC5D,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,iDAAiD;IAC1D,MAAM,EAAG,+CAA+C;CAChD,CAAC;AAYX;;;;;;;;;;;GAWG;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,iEAAiE;IACjE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC5D,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,gBAAgB,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,gBAAgB,YAAY,SAAS,UAAU,YAAY,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,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,153 @@
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
+ * The ownership keypair of license `licenseHash`, trying each candidate app
65
+ * address in turn. The key is derived from the address the license was
66
+ * issued on; after a redeploy the license carries over to the successor
67
+ * unchanged, so its key is the earlier generation's. Pass the app's
68
+ * generation addresses (any order; the live one first is cheapest for new
69
+ * licenses). A wrong address only gives a key that doesn't match, so the
70
+ * list needn't be authenticated. Null when none matches (another passphrase).
71
+ */
72
+ export declare function findOwnershipKeypair(passphrase: string, licenseHash: string, candidateAddresses: readonly string[]): Promise<(OwnershipKeypair & {
73
+ zkAppAddress: string;
74
+ }) | null>;
75
+ /**
76
+ * The addresses of every generation of the app at `zkAppAddress` (any of
77
+ * them), live first, from the keeper's generation list; just
78
+ * `zkAppAddress` when the keeper doesn't know it. For findOwnershipKeypair.
79
+ */
80
+ export declare function ownershipCandidateAddresses(keeperUrl: string, zkAppAddress: string, fetcher?: typeof fetch): Promise<string[]>;
81
+ /**
82
+ * Activation assertion shape. Proves the caller holds the passphrase
83
+ * that derived the license's ownerPubKey. Retained as an SDK primitive
84
+ * so external tooling can still request / verify an off-band "I own
85
+ * this license" signature — the on-chain flows themselves do not use
86
+ * it.
87
+ */
88
+ export type ActivationMessage = {
89
+ licenseHash: string;
90
+ nonce: string;
91
+ zkAppAddress: string;
92
+ };
93
+ /**
94
+ * Binding / unbinding message — the /prove/bind and /prove/unbind
95
+ * flows. Authorizes a specific device commitment being placed into or
96
+ * cleared from a specific slot on a specific license.
97
+ */
98
+ export type BindingMessage = {
99
+ licenseHash: string;
100
+ deviceCommitment: string;
101
+ slotIndex: number;
102
+ nonce: string;
103
+ slotState: string;
104
+ expirySlot: number;
105
+ zkAppAddress: string;
106
+ };
107
+ /**
108
+ * Packed (boundAt, freedAt) of one slot as the circuit sees it — the
109
+ * `slotState` a BindingMessage must commit to. Takes the slot entry from a
110
+ * LicenseRecord's `devices[]` (as served by GET /license/:zkApp/:hash);
111
+ * an absent entry is a pristine slot (0).
112
+ */
113
+ export declare function slotStateOf(device?: {
114
+ boundAtSlot: number;
115
+ freedAtSlot: number;
116
+ } | null): string;
117
+ export type ChallengeMessage = ActivationMessage;
118
+ /**
119
+ * Canonicalize the activation message into the Field[] that
120
+ * `Signature.create` / `Signature.verify` hash. The domain-separation
121
+ * tag prefix means no captured activation signature can be replayed as
122
+ * a binding signature, even if all other fields collide.
123
+ */
124
+ export declare function activationMessageFields(msg: ActivationMessage): Field[];
125
+ export declare function bindingMessageFields(msg: BindingMessage): Field[];
126
+ export declare function unbindingMessageFields(msg: BindingMessage): Field[];
127
+ export declare function signActivation(privateKey: PrivateKey, msg: ActivationMessage): Signature;
128
+ export declare function signBinding(privateKey: PrivateKey, msg: BindingMessage): Signature;
129
+ export declare function signUnbinding(privateKey: PrivateKey, msg: BindingMessage): Signature;
130
+ export declare function signChallenge(privateKey: PrivateKey, msg: ChallengeMessage): Signature;
131
+ /**
132
+ * Verify an activation signature. Returns false (never throws) for any
133
+ * failure — malformed pubkey, malformed signature, actual signature
134
+ * mismatch. Callers get a single go/no-go decision without having to
135
+ * distinguish "invalid" from "unparseable."
136
+ */
137
+ export declare function verifyActivation(publicKey: PublicKey, msg: ActivationMessage, signature: Signature): boolean;
138
+ export declare function verifyBinding(publicKey: PublicKey, msg: BindingMessage, signature: Signature): boolean;
139
+ export declare function verifyUnbinding(publicKey: PublicKey, msg: BindingMessage, signature: Signature): boolean;
140
+ export declare function verifyChallenge(publicKey: PublicKey, msg: ChallengeMessage, signature: Signature): boolean;
141
+ /**
142
+ * Encode a signature for the wire (POST body, ProofFile JSON, etc).
143
+ * Base58 matches Mina's canonical serialization for both PublicKey and
144
+ * Signature, keeping the same encoding shape across everything the
145
+ * buyer's SDK produces.
146
+ */
147
+ export declare function encodeSignature(sig: Signature): string;
148
+ export declare function decodeSignature(base58: string): Signature;
149
+ export declare function encodePublicKey(pk: PublicKey): string;
150
+ export declare function decodePublicKey(base58: string): PublicKey;
151
+ export declare function encodeBase64(bytes: Uint8Array): string;
152
+ export declare function decodeBase64(b64: string): Uint8Array;
153
+ //# 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,297 @@
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, UInt32 } from 'o1js';
34
+ import { packSlotTimes } from './licenseLeaf.js';
35
+ import { licenseHashFromPubkey } from './ownershipLicenseHash.js';
36
+ // HKDF info binds the derivation to a specific zkApp address, so the
37
+ // same passphrase yields different keypairs for different apps. This
38
+ // makes credentials per-app: a leaked passphrase for app A cannot be
39
+ // replayed as an activation for app B. RFC 5869 designates `info` as
40
+ // the domain-separation channel (salt is for randomness, which we don't
41
+ // have here — the passphrase IS the secret input).
42
+ //
43
+ // The `v1` prefix reserves room to rotate the KDF or the target key type
44
+ // later without a silent collision — a future scheme bumps to `v2` and
45
+ // derives an unrelated key from the same passphrase.
46
+ const HKDF_INFO_PREFIX = 'zkLicensing/v1/ownership|';
47
+ const HKDF_SALT = new Uint8Array(0);
48
+ // Domain-separation tags baked into every signed message. Prevents a
49
+ // signature captured on one flow (activation) from being replayed on
50
+ // another (binding) — the first Field the signer hashes is the flow ID,
51
+ // so the two signatures live in disjoint spaces even at identical
52
+ // (licenseHash, nonce).
53
+ //
54
+ // Exported so `LicensingApp.bind()` / `unbind()` can rebuild the exact
55
+ // same Field[] preimage inside the circuit for `Signature.verify()`.
56
+ // Bumping these values is a hardfork — every off-chain signer and every
57
+ // deployed circuit would need to agree.
58
+ export const TAG_ACTIVATION = 1n;
59
+ export const TAG_BINDING = 2n;
60
+ export const TAG_UNBINDING = 3n;
61
+ function buildHkdfInfo(zkAppAddress) {
62
+ return new TextEncoder().encode(HKDF_INFO_PREFIX + zkAppAddress);
63
+ }
64
+ function utf8Bytes(s) {
65
+ return new TextEncoder().encode(s);
66
+ }
67
+ function bytesToBase64(bytes) {
68
+ let s = '';
69
+ for (const b of bytes)
70
+ s += String.fromCharCode(b);
71
+ return btoa(s);
72
+ }
73
+ function base64ToBytes(b64) {
74
+ const bin = atob(b64);
75
+ const out = new Uint8Array(bin.length);
76
+ for (let i = 0; i < bin.length; i++)
77
+ out[i] = bin.charCodeAt(i);
78
+ return out;
79
+ }
80
+ function subtle() {
81
+ const s = globalThis.crypto?.subtle;
82
+ if (!s) {
83
+ throw new Error('WebCrypto unavailable — ownershipSignature requires Node ≥ 20 or a modern browser (Chromium 137+, Safari 17+, Firefox 130+).');
84
+ }
85
+ return s;
86
+ }
87
+ function bytesToBigInt(bytes) {
88
+ let n = 0n;
89
+ for (const b of bytes)
90
+ n = (n << 8n) | BigInt(b);
91
+ return n;
92
+ }
93
+ /**
94
+ * HKDF-SHA256 with empty salt and a per-app domain-separation info
95
+ * string (`zkLicensing/v1/ownership|<zkAppAddress>`). Deterministic:
96
+ * same (passphrase, zkAppAddress) → same 32-byte seed. Different
97
+ * zkAppAddress → cryptographically unrelated seed even with the same
98
+ * passphrase.
99
+ */
100
+ export async function deriveSeed(passphrase, zkAppAddress) {
101
+ const baseKey = await subtle().importKey('raw', utf8Bytes(passphrase), 'HKDF', false, ['deriveBits']);
102
+ const info = buildHkdfInfo(zkAppAddress);
103
+ const bits = await subtle().deriveBits({
104
+ name: 'HKDF',
105
+ hash: 'SHA-256',
106
+ salt: HKDF_SALT,
107
+ info: info,
108
+ }, baseKey, 256);
109
+ return new Uint8Array(bits);
110
+ }
111
+ /**
112
+ * Derive a Pallas Schnorr keypair deterministically from (passphrase,
113
+ * zkAppAddress). The keypair is bound to the specific app: the same
114
+ * passphrase used against a different app yields a different keypair,
115
+ * so a leaked passphrase cannot be replayed as an activation for any
116
+ * other app.
117
+ *
118
+ * The 32-byte HKDF output is interpreted as a big-endian integer and
119
+ * passed to `PrivateKey.fromBigInt`, which reduces it modulo the Pallas
120
+ * scalar field order internally. All 2^256 possible HKDF outputs map to
121
+ * valid private keys with (256 - log2(Fq_order))-bit modular bias — the
122
+ * scalar field is ~254 bits, so bias is <2^-253 and lies well below any
123
+ * practical distinguisher.
124
+ */
125
+ export async function deriveOwnershipKeypair(passphrase, zkAppAddress) {
126
+ const seed = await deriveSeed(passphrase, zkAppAddress);
127
+ const privateKey = PrivateKey.fromBigInt(bytesToBigInt(seed));
128
+ const publicKey = privateKey.toPublicKey();
129
+ return { privateKey, publicKey, publicKeyBase58: publicKey.toBase58() };
130
+ }
131
+ /**
132
+ * The ownership keypair of license `licenseHash`, trying each candidate app
133
+ * address in turn. The key is derived from the address the license was
134
+ * issued on; after a redeploy the license carries over to the successor
135
+ * unchanged, so its key is the earlier generation's. Pass the app's
136
+ * generation addresses (any order; the live one first is cheapest for new
137
+ * licenses). A wrong address only gives a key that doesn't match, so the
138
+ * list needn't be authenticated. Null when none matches (another passphrase).
139
+ */
140
+ export async function findOwnershipKeypair(passphrase, licenseHash, candidateAddresses) {
141
+ for (const zkAppAddress of [...new Set(candidateAddresses)]) {
142
+ const kp = await deriveOwnershipKeypair(passphrase, zkAppAddress);
143
+ if (licenseHashFromPubkey(kp.publicKey) === licenseHash)
144
+ return { ...kp, zkAppAddress };
145
+ }
146
+ return null;
147
+ }
148
+ /**
149
+ * The addresses of every generation of the app at `zkAppAddress` (any of
150
+ * them), live first, from the keeper's generation list; just
151
+ * `zkAppAddress` when the keeper doesn't know it. For findOwnershipKeypair.
152
+ */
153
+ export async function ownershipCandidateAddresses(keeperUrl, zkAppAddress, fetcher = fetch) {
154
+ try {
155
+ const r = await fetcher(`${keeperUrl.replace(/\/+$/, '')}/apps/by-address/${encodeURIComponent(zkAppAddress)}`);
156
+ if (!r.ok)
157
+ return [zkAppAddress];
158
+ const body = await r.json();
159
+ const gens = Array.isArray(body.generations) ? [...body.generations].sort((a, b) => b.gen - a.gen).map(g => g.zkAppAddress) : [];
160
+ return [...new Set([zkAppAddress, ...gens])];
161
+ }
162
+ catch {
163
+ return [zkAppAddress];
164
+ }
165
+ }
166
+ /**
167
+ * Packed (boundAt, freedAt) of one slot as the circuit sees it — the
168
+ * `slotState` a BindingMessage must commit to. Takes the slot entry from a
169
+ * LicenseRecord's `devices[]` (as served by GET /license/:zkApp/:hash);
170
+ * an absent entry is a pristine slot (0).
171
+ */
172
+ export function slotStateOf(device) {
173
+ if (!device)
174
+ return '0';
175
+ return packSlotTimes(UInt32.from(device.boundAtSlot), UInt32.from(device.freedAtSlot)).toString();
176
+ }
177
+ /**
178
+ * Canonicalize the activation message into the Field[] that
179
+ * `Signature.create` / `Signature.verify` hash. The domain-separation
180
+ * tag prefix means no captured activation signature can be replayed as
181
+ * a binding signature, even if all other fields collide.
182
+ */
183
+ export function activationMessageFields(msg) {
184
+ const addr = PublicKey.fromBase58(msg.zkAppAddress);
185
+ return [
186
+ Field(TAG_ACTIVATION),
187
+ Field(msg.licenseHash),
188
+ Field(msg.nonce),
189
+ addr.x,
190
+ addr.isOdd.toField(),
191
+ ];
192
+ }
193
+ /**
194
+ * Canonicalize a binding or unbinding message. The `tag` argument
195
+ * distinguishes bind (TAG_BINDING) from unbind (TAG_UNBINDING) so a
196
+ * bind signature captured off-chain cannot be replayed as an unbind
197
+ * even at identical (licenseHash, deviceCommitment, slotIndex, nonce).
198
+ */
199
+ function bindingLikeFields(tag, msg) {
200
+ const addr = PublicKey.fromBase58(msg.zkAppAddress);
201
+ return [
202
+ Field(tag),
203
+ Field(msg.licenseHash),
204
+ Field(msg.deviceCommitment),
205
+ Field(msg.slotIndex),
206
+ Field(msg.nonce),
207
+ Field(msg.slotState),
208
+ Field(msg.expirySlot),
209
+ addr.x,
210
+ addr.isOdd.toField(),
211
+ ];
212
+ }
213
+ export function bindingMessageFields(msg) {
214
+ return bindingLikeFields(TAG_BINDING, msg);
215
+ }
216
+ export function unbindingMessageFields(msg) {
217
+ return bindingLikeFields(TAG_UNBINDING, msg);
218
+ }
219
+ // ── Signing ───────────────────────────────────────────────────────
220
+ export function signActivation(privateKey, msg) {
221
+ return Signature.create(privateKey, activationMessageFields(msg));
222
+ }
223
+ export function signBinding(privateKey, msg) {
224
+ return Signature.create(privateKey, bindingMessageFields(msg));
225
+ }
226
+ export function signUnbinding(privateKey, msg) {
227
+ return Signature.create(privateKey, unbindingMessageFields(msg));
228
+ }
229
+ // Retained for back-compat with the Ed25519-era name. New code should
230
+ // call signActivation directly.
231
+ export function signChallenge(privateKey, msg) {
232
+ return signActivation(privateKey, msg);
233
+ }
234
+ // ── Verification ──────────────────────────────────────────────────
235
+ /**
236
+ * Verify an activation signature. Returns false (never throws) for any
237
+ * failure — malformed pubkey, malformed signature, actual signature
238
+ * mismatch. Callers get a single go/no-go decision without having to
239
+ * distinguish "invalid" from "unparseable."
240
+ */
241
+ export function verifyActivation(publicKey, msg, signature) {
242
+ try {
243
+ return signature.verify(publicKey, activationMessageFields(msg)).toBoolean();
244
+ }
245
+ catch {
246
+ return false;
247
+ }
248
+ }
249
+ export function verifyBinding(publicKey, msg, signature) {
250
+ try {
251
+ return signature.verify(publicKey, bindingMessageFields(msg)).toBoolean();
252
+ }
253
+ catch {
254
+ return false;
255
+ }
256
+ }
257
+ export function verifyUnbinding(publicKey, msg, signature) {
258
+ try {
259
+ return signature.verify(publicKey, unbindingMessageFields(msg)).toBoolean();
260
+ }
261
+ catch {
262
+ return false;
263
+ }
264
+ }
265
+ // Back-compat alias, see signChallenge.
266
+ export function verifyChallenge(publicKey, msg, signature) {
267
+ return verifyActivation(publicKey, msg, signature);
268
+ }
269
+ // ── Wire encoding helpers ─────────────────────────────────────────
270
+ /**
271
+ * Encode a signature for the wire (POST body, ProofFile JSON, etc).
272
+ * Base58 matches Mina's canonical serialization for both PublicKey and
273
+ * Signature, keeping the same encoding shape across everything the
274
+ * buyer's SDK produces.
275
+ */
276
+ export function encodeSignature(sig) {
277
+ return sig.toBase58();
278
+ }
279
+ export function decodeSignature(base58) {
280
+ return Signature.fromBase58(base58);
281
+ }
282
+ export function encodePublicKey(pk) {
283
+ return pk.toBase58();
284
+ }
285
+ export function decodePublicKey(base58) {
286
+ return PublicKey.fromBase58(base58);
287
+ }
288
+ // Retained so callers that already do `encodeBase64(seed)` for logging
289
+ // or persistence keep working — this module no longer produces raw byte
290
+ // pubkeys or signatures, only o1js objects with .toBase58().
291
+ export function encodeBase64(bytes) {
292
+ return bytesToBase64(bytes);
293
+ }
294
+ export function decodeBase64(b64) {
295
+ return base64ToBytes(b64);
296
+ }
297
+ //# sourceMappingURL=ownershipSignature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownershipSignature.js","sourceRoot":"","sources":["../src/ownershipSignature.ts"],"names":[],"mappings":"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"}