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,163 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+
4
+ /**
5
+ * verifyWithAncestors.ts
6
+ *
7
+ * Shared address-routing core used by both verifyService.ts and
8
+ * keeperService.ts. Given an already-resolved AppRecord, an already-fetched
9
+ * current-live on-chain root, and the current set of license records, this
10
+ * helper:
11
+ *
12
+ * 1. Tries verifyLicenseCore against the current live contract (a
13
+ * redeployed app's successor starts with its predecessor's licenses,
14
+ * so licenses bought on any generation land here).
15
+ * 2. On "License not found" in current-live, walks the app's
16
+ * deploymentHistory[] newest-first, fetching each retired contract's
17
+ * own on-chain root and re-checking with records filtered to that
18
+ * retired address (a request naming a retired address before the
19
+ * keeper carried the records over).
20
+ * 3. Applies one lazy-bootstrap replay per address on
21
+ * "License state does not match on-chain root" — same self-heal the
22
+ * callers used to do inline.
23
+ *
24
+ * The helper does NOT do VK-hash validation or version admission — those
25
+ * are caller-specific policy (verifyService uses per-generation
26
+ * CANONICAL_VK_HASHES; the backend uses its single compiled canonicalVkHash).
27
+ * Callers do their VK check up front against the current-live account they
28
+ * fetched, then hand the fetched root here.
29
+ */
30
+
31
+ import { PublicKey, fetchAccount } from 'o1js';
32
+ import { LICENSING_APP_STATE } from './contractInterface.js';
33
+ import { verifyLicenseCore, type VerifyResponse } from './verifyCore.js';
34
+ import { readRecords, type LicenseRecord } from './licenseStore.js';
35
+ import { lazyBootstrap } from './archiveBootstrap.js';
36
+ import type { LookupableDeployment } from './appsLookup.js';
37
+
38
+ export type VerifyWithAncestorsInput = {
39
+ // The per-network deployment slice (current live address + retired history).
40
+ // Callers project this from the resolved AppRecord: for a verify request the
41
+ // caller determines which network's deployment to walk (typically the one
42
+ // whose current address matched, or the one the client-pinned address lives
43
+ // on). Cross-network walks aren't needed — mainnet and devnet each keep
44
+ // their own generation history.
45
+ deployment: LookupableDeployment;
46
+ licenseHash: string;
47
+ currentSlot: number;
48
+ currentLiveRoot: string;
49
+ // The current live app's forkActivationSlot state (0 or absent: none).
50
+ currentLiveForkSlot?: number;
51
+ // Callers pre-load records in parallel with the current-live fetch. The
52
+ // helper may reload once after a lazy-bootstrap replay, so also returns
53
+ // the (possibly refreshed) records for the caller to reuse when looking
54
+ // up per-record fields like purchaseSlot.
55
+ records: LicenseRecord[];
56
+ };
57
+
58
+ export type VerifyWithAncestorsResult = {
59
+ result: VerifyResponse;
60
+ // Address the license was actually found under — usually the current live
61
+ // one, but falls back to a retired address for pre-bump buyers. Callers
62
+ // must key any LicenseRecord lookup on this, not on the request address.
63
+ resolvedAddress: string;
64
+ onChainRoot: string;
65
+ liveRecords: LicenseRecord[];
66
+ };
67
+
68
+ // One-shot internal retry for a single network call. Absorbs a lone
69
+ // transient blip from the Mina GraphQL node. Duplicated shape from
70
+ // verifyService's fetchWithOneRetry — kept local to this module so the
71
+ // helper stays self-contained and the two callers don't need to inject
72
+ // their own retry strategy.
73
+ async function fetchWithOneRetry<T>(fn: () => Promise<T>, delayMs = 500): Promise<T> {
74
+ try {
75
+ return await fn();
76
+ } catch {
77
+ await new Promise(r => setTimeout(r, delayMs));
78
+ return fn();
79
+ }
80
+ }
81
+
82
+ export async function verifyLicenseWithAncestors(
83
+ input: VerifyWithAncestorsInput,
84
+ ): Promise<VerifyWithAncestorsResult> {
85
+ const { deployment, licenseHash, currentSlot, currentLiveRoot } = input;
86
+ let liveRecords = input.records;
87
+ const currentLive = deployment.zkAppAddress;
88
+
89
+ // Step 1: try current-live first.
90
+ let result = verifyLicenseCore({
91
+ zkAppAddress: currentLive,
92
+ licenseHash,
93
+ records: liveRecords,
94
+ onChainRoot: currentLiveRoot,
95
+ currentSlot,
96
+ forkActivationSlot: input.currentLiveForkSlot,
97
+ });
98
+ if (!result.valid && result.reason === 'License state does not match on-chain root') {
99
+ // Local Merkle root diverged from on-chain — either the archive-sync loop
100
+ // hasn't caught up yet or (bug) the local mirror silently drifted. Log the
101
+ // trigger so operators can correlate a spike of these with either a lag
102
+ // window or a genuine drift regression.
103
+ console.log(`[lazy-bootstrap] triggered current-live=${currentLive.slice(0, 12)}… licenseHash=${licenseHash.slice(0, 12)}… reason=root-mismatch`);
104
+ await lazyBootstrap(currentLive);
105
+ liveRecords = await readRecords();
106
+ result = verifyLicenseCore({
107
+ zkAppAddress: currentLive,
108
+ licenseHash,
109
+ records: liveRecords,
110
+ onChainRoot: currentLiveRoot,
111
+ currentSlot,
112
+ forkActivationSlot: input.currentLiveForkSlot,
113
+ });
114
+ }
115
+ if (result.valid || result.reason !== 'License not found') {
116
+ return { result, resolvedAddress: currentLive, onChainRoot: currentLiveRoot, liveRecords };
117
+ }
118
+
119
+ // Step 2: walk retired addresses newest-first.
120
+ const retiredAddrs = (deployment.deploymentHistory ?? []).map(d => d.zkAppAddress).reverse();
121
+ for (const retired of retiredAddrs) {
122
+ let retiredAccount;
123
+ try {
124
+ const fetched = await fetchWithOneRetry(() => fetchAccount({ publicKey: PublicKey.fromBase58(retired) }));
125
+ retiredAccount = fetched.account;
126
+ } catch {
127
+ continue;
128
+ }
129
+ const retiredRoot = retiredAccount?.zkapp?.appState?.[0]?.toString();
130
+ if (!retiredRoot) continue;
131
+ const retiredForkSlot = Number(retiredAccount?.zkapp?.appState?.[LICENSING_APP_STATE.forkActivationSlot]?.toString() ?? '0');
132
+ let retiredResult = verifyLicenseCore({
133
+ zkAppAddress: retired,
134
+ licenseHash,
135
+ records: liveRecords,
136
+ onChainRoot: retiredRoot,
137
+ currentSlot,
138
+ forkActivationSlot: retiredForkSlot,
139
+ });
140
+ if (!retiredResult.valid && retiredResult.reason === 'License state does not match on-chain root') {
141
+ // Same trigger as the current-live path but on a retired generation —
142
+ // e.g. a pre-bump buyer's request address never got its full history
143
+ // replayed after the process restart.
144
+ console.log(`[lazy-bootstrap] triggered retired=${retired.slice(0, 12)}… licenseHash=${licenseHash.slice(0, 12)}… reason=root-mismatch`);
145
+ await lazyBootstrap(retired);
146
+ liveRecords = await readRecords();
147
+ retiredResult = verifyLicenseCore({
148
+ zkAppAddress: retired,
149
+ licenseHash,
150
+ records: liveRecords,
151
+ onChainRoot: retiredRoot,
152
+ currentSlot,
153
+ forkActivationSlot: retiredForkSlot,
154
+ });
155
+ }
156
+ if (retiredResult.valid || retiredResult.reason !== 'License not found') {
157
+ return { result: retiredResult, resolvedAddress: retired, onChainRoot: retiredRoot, liveRecords };
158
+ }
159
+ }
160
+
161
+ // Not found anywhere — return the current-live "License not found" verdict.
162
+ return { result, resolvedAddress: currentLive, onChainRoot: currentLiveRoot, liveRecords };
163
+ }
@@ -0,0 +1,94 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+
4
+ /**
5
+ * zipToState.ts
6
+ *
7
+ * US ZIP-code prefix → 2-letter state code lookup. Used by countryStats.ts to
8
+ * roll US buyers up into per-state buckets for sales-tax reporting. Prefix is
9
+ * the first three digits of the ZIP; every US ZIP starts with three digits
10
+ * that resolve to exactly one state (source: USPS ZIP Code Directory).
11
+ *
12
+ * Data model: inclusive [lo, hi] prefix ranges → state code. Ranges are
13
+ * ordered by lo for readability but the lookup does a linear scan since the
14
+ * table is small (~55 rows) and this runs once per record on a background
15
+ * aggregation, not in a hot loop.
16
+ *
17
+ * Coverage: 50 states, DC, military APO/FPO (AA/AE/AP), Puerto Rico (PR), Guam
18
+ * and adjacent Pacific territories (GU). US Virgin Islands (VI), American
19
+ * Samoa (AS), and Northern Mariana Islands (MP) share prefixes with the
20
+ * Pacific block; we return 'GU' for that range — good enough for
21
+ * digital-goods reporting where those territories don't have their own tax.
22
+ * Returns undefined for prefixes that don't map (very few — USPS reserves
23
+ * some for future assignment).
24
+ */
25
+
26
+ const RANGES: ReadonlyArray<readonly [number, number, string]> = [
27
+ [ 5, 5, 'NY'],
28
+ [ 6, 9, 'PR'],
29
+ [ 10, 27, 'MA'],
30
+ [ 28, 29, 'RI'],
31
+ [ 30, 38, 'NH'],
32
+ [ 39, 49, 'ME'],
33
+ [ 50, 59, 'VT'],
34
+ [ 60, 69, 'CT'],
35
+ [ 70, 89, 'NJ'],
36
+ [ 90, 99, 'AE'],
37
+ [100, 149, 'NY'],
38
+ [150, 196, 'PA'],
39
+ [197, 199, 'DE'],
40
+ [200, 205, 'DC'],
41
+ [206, 219, 'MD'],
42
+ [220, 246, 'VA'],
43
+ [247, 268, 'WV'],
44
+ [270, 289, 'NC'],
45
+ [290, 299, 'SC'],
46
+ [300, 319, 'GA'],
47
+ [320, 349, 'FL'],
48
+ [350, 369, 'AL'],
49
+ [370, 385, 'TN'],
50
+ [386, 397, 'MS'],
51
+ [398, 399, 'GA'],
52
+ [400, 427, 'KY'],
53
+ [430, 459, 'OH'],
54
+ [460, 479, 'IN'],
55
+ [480, 499, 'MI'],
56
+ [500, 528, 'IA'],
57
+ [530, 549, 'WI'],
58
+ [550, 567, 'MN'],
59
+ [570, 577, 'SD'],
60
+ [580, 588, 'ND'],
61
+ [590, 599, 'MT'],
62
+ [600, 629, 'IL'],
63
+ [630, 658, 'MO'],
64
+ [660, 679, 'KS'],
65
+ [680, 693, 'NE'],
66
+ [700, 714, 'LA'],
67
+ [716, 729, 'AR'],
68
+ [730, 749, 'OK'],
69
+ [750, 799, 'TX'],
70
+ [800, 816, 'CO'],
71
+ [820, 831, 'WY'],
72
+ [832, 838, 'ID'],
73
+ [840, 847, 'UT'],
74
+ [850, 865, 'AZ'],
75
+ [870, 884, 'NM'],
76
+ [889, 898, 'NV'],
77
+ [900, 961, 'CA'],
78
+ [962, 966, 'AP'],
79
+ [967, 968, 'HI'],
80
+ [969, 969, 'GU'],
81
+ [970, 979, 'OR'],
82
+ [980, 994, 'WA'],
83
+ [995, 999, 'AK'],
84
+ ];
85
+
86
+ export function zipToState(zip: string): string | undefined {
87
+ const m = /^(\d{3})/.exec(zip);
88
+ if (!m) return undefined;
89
+ const n = parseInt(m[1], 10);
90
+ for (const [lo, hi, st] of RANGES) {
91
+ if (n >= lo && n <= hi) return st;
92
+ }
93
+ return undefined;
94
+ }
@@ -1,26 +0,0 @@
1
- // Hand-written declarations for licenseProof.ts.
2
- // tsc can't emit these automatically because the inferred return types of
3
- // Struct() and ZkProgram() reference internal o1js paths outside this
4
- // package's rootDir, triggering TS2742. Callers get full typing on the
5
- // methods they actually invoke by importing the corresponding o1js types
6
- // directly (Field, Proof, etc.) in their own code.
7
-
8
- export const OwnershipChallenge: any;
9
- export type OwnershipChallenge = any;
10
-
11
- export const LicenseProof: any;
12
-
13
- export class LicenseProofProof {
14
- static publicInputType: any;
15
- static publicOutputType: any;
16
- static fromJSON(json: any): Promise<LicenseProofProof>;
17
- static dummy(...args: any[]): Promise<LicenseProofProof>;
18
- publicInput: any;
19
- publicOutput: any;
20
- proof: any;
21
- maxProofsVerified: 0 | 1 | 2;
22
- shouldVerify: any;
23
- verify(): void;
24
- verifyIf(condition: any): void;
25
- toJSON(): { publicInput: string[]; publicOutput: string[]; maxProofsVerified: 0 | 1 | 2; proof: string };
26
- }
@@ -1,41 +0,0 @@
1
- // Copyright (c) 2025-2026 zkLicensing project developers
2
- // All rights reserved.
3
- /**
4
- * licenseProof.ts — ZkProgram for ownership challenge-response.
5
- *
6
- * Loaded only when the caller needs to *produce* an ownership proof on the
7
- * client (the verify path never imports this file). Lives in a dedicated
8
- * subpath (`zklicensing/prove`) so the verify-only entry stays free of o1js.
9
- *
10
- * Proves: Poseidon.hash([secretHash]) === licenseHash
11
- * where secretHash = Poseidon.hash(Encoding.stringToFields(rawSecret)).
12
- *
13
- * The nonce sits in the public input. Its sole purpose is replay-resistance:
14
- * a proof generated for nonce A is mathematically distinct from one for nonce
15
- * B, so a verifier that issued nonce A will reject any proof not produced for
16
- * exactly that challenge.
17
- */
18
- import { ZkProgram, Field, Poseidon, Struct } from 'o1js';
19
- // Struct() / ZkProgram / ZkProgram.Proof() return types reference o1js
20
- // internal paths outside this package's rootDir, triggering TS2742 on
21
- // declaration emit. Annotating each export as `any` suppresses the inference;
22
- // consumers get real types via the hand-written licenseProof.d.ts shim.
23
- // See contractInterface.ts for the longer rationale on why this workaround.
24
- export const OwnershipChallenge = Struct({
25
- licenseHash: Field,
26
- nonce: Field,
27
- });
28
- export const LicenseProof = ZkProgram({
29
- name: 'license-proof',
30
- publicInput: OwnershipChallenge,
31
- methods: {
32
- prove: {
33
- privateInputs: [Field], // secretHash — never sent anywhere
34
- async method(pub, secretHash) {
35
- Poseidon.hash([secretHash]).assertEquals(pub.licenseHash);
36
- },
37
- },
38
- },
39
- });
40
- export const LicenseProofProof = ZkProgram.Proof(LicenseProof);
41
- //# sourceMappingURL=licenseProof.js.map
@@ -1,43 +0,0 @@
1
- /**
2
- * migrateClient.ts
3
- *
4
- * Browser-safe client for /prove/migrate. Assembles a single-hop cross-
5
- * generation proof against the current live map — the keeper walks the
6
- * deploymentHistory once, gathers all three witnesses (ancestor / pred /
7
- * live-empty), and returns a signable tx.
8
- *
9
- * Failure surfaces:
10
- * - 404 "license refunded or unknown": no leaf anywhere in history for
11
- * this licenseHash. Caller should not retry — it's terminal.
12
- * - 409 "already migrated": leaf is present in the live map. Caller
13
- * should refresh their license record and stop.
14
- * - 409 "migration in flight": another caller is proving right now.
15
- * `retryAfterMs` on the error tells the caller how long to wait
16
- * before trying again. See `ProveMigrateError.retryAfterMs`.
17
- */
18
- export interface ProveMigrateRequest {
19
- keeperUrl: string;
20
- licenseHash: string;
21
- appId: string;
22
- buyerAddress: string;
23
- fetcher?: typeof fetch;
24
- signal?: AbortSignal;
25
- }
26
- export interface ProveMigrateResponse {
27
- provenTxJson: string;
28
- ancestorGen: number;
29
- expirySlot: number;
30
- licenseHash: string;
31
- zkAppAddress: string;
32
- }
33
- /**
34
- * Extends Error with the retryAfterMs hint the keeper returns on the
35
- * "migration in flight" 409. Callers ignore it for other 4xx surfaces.
36
- */
37
- export declare class ProveMigrateError extends Error {
38
- readonly status: number;
39
- readonly retryAfterMs?: number;
40
- constructor(message: string, status: number, retryAfterMs?: number);
41
- }
42
- export declare function proveMigrate(req: ProveMigrateRequest): Promise<ProveMigrateResponse>;
43
- //# sourceMappingURL=migrateClient.d.ts.map
@@ -1,47 +0,0 @@
1
- // Copyright (c) 2025-2026 zkLicensing project developers
2
- // All rights reserved.
3
- /**
4
- * Extends Error with the retryAfterMs hint the keeper returns on the
5
- * "migration in flight" 409. Callers ignore it for other 4xx surfaces.
6
- */
7
- export class ProveMigrateError extends Error {
8
- status;
9
- retryAfterMs;
10
- constructor(message, status, retryAfterMs) {
11
- super(message);
12
- this.name = 'ProveMigrateError';
13
- this.status = status;
14
- this.retryAfterMs = retryAfterMs;
15
- }
16
- }
17
- function baseUrl(url) {
18
- return url.replace(/\/+$/, '');
19
- }
20
- export async function proveMigrate(req) {
21
- const fetcher = req.fetcher ?? fetch;
22
- const resp = await fetcher(`${baseUrl(req.keeperUrl)}/prove/migrate`, {
23
- method: 'POST',
24
- headers: { 'Content-Type': 'application/json' },
25
- signal: req.signal,
26
- body: JSON.stringify({
27
- licenseHash: req.licenseHash,
28
- appId: req.appId,
29
- buyerAddress: req.buyerAddress,
30
- }),
31
- });
32
- if (!resp.ok) {
33
- let msg = `HTTP ${resp.status}`;
34
- let retryAfterMs;
35
- try {
36
- const body = await resp.json();
37
- if (typeof body.error === 'string')
38
- msg = body.error;
39
- if (typeof body.retryAfterMs === 'number')
40
- retryAfterMs = body.retryAfterMs;
41
- }
42
- catch { /* body wasn't JSON */ }
43
- throw new ProveMigrateError(msg, resp.status, retryAfterMs);
44
- }
45
- return await resp.json();
46
- }
47
- //# sourceMappingURL=migrateClient.js.map
@@ -1,34 +0,0 @@
1
- export declare const OWNERSHIP_CHALLENGE_TTL_MS = 600000;
2
- export declare const OWNERSHIP_TOKEN_TTL_MS: number;
3
- export type OwnershipTokenPayload = {
4
- z: string;
5
- l: string;
6
- e: number;
7
- jti?: string;
8
- };
9
- export declare function newJti(): string;
10
- export type ChallengeStore = {
11
- issue(licenseHash: string): {
12
- nonce: string;
13
- ttl: number;
14
- };
15
- consume(licenseHash: string, nonce: string): 'ok' | 'unknown' | 'expired';
16
- size(): number;
17
- };
18
- export declare function createChallengeStore(ttlMs?: number): ChallengeStore;
19
- export declare function signOwnershipToken(key: Buffer, payload: OwnershipTokenPayload): string;
20
- export declare function verifyOwnershipToken(key: Buffer, token: string): OwnershipTokenPayload | null;
21
- export declare function newOwnershipKey(): Buffer;
22
- export type SessionStore = {
23
- insert(licenseHash: string, jti: string, exp: number, cap: number): {
24
- state: 'ok' | 'at-cap';
25
- active: number;
26
- };
27
- has(licenseHash: string, jti: string): boolean;
28
- releaseSeat(licenseHash: string, jti: string): boolean;
29
- reset(licenseHash: string): number;
30
- activeCount(licenseHash: string): number;
31
- size(): number;
32
- };
33
- export declare function createSessionStore(): SessionStore;
34
- //# sourceMappingURL=ownership.d.ts.map
@@ -1,147 +0,0 @@
1
- // Copyright (c) 2025-2026 zkLicensing project developers
2
- // All rights reserved.
3
- /**
4
- * ownership.ts
5
- *
6
- * Ownership-token helpers and a small in-memory nonce store for the
7
- * challenge-response /verify flow.
8
- *
9
- * Replay-resistance: the LicenseProof's public input binds (licenseHash,
10
- * nonce). The keeper hands out a nonce, the client proves against it, the
11
- * keeper consumes the nonce on the way through. Tokens are HMAC-SHA256 of
12
- * the (zkAppAddress, licenseHash, exp, jti?) tuple so signature validation
13
- * is purely cryptographic — no server-side lookup, restart-safe as long as
14
- * the HMAC key persists.
15
- *
16
- * Session tracking: the optional `jti` field lets the caller run a
17
- * revocation / concurrency layer on top of the stateless HMAC — the token
18
- * is still validated cryptographically, but the caller can also check that
19
- * the jti is present in a per-license active-session set. Absent jti keeps
20
- * old callers working unchanged (there is no session tracking to begin with,
21
- * so nothing needs to migrate).
22
- */
23
- import { createHmac, randomBytes, timingSafeEqual } from 'crypto';
24
- import { Field } from 'o1js';
25
- export const OWNERSHIP_CHALLENGE_TTL_MS = 600_000;
26
- // A device that stays active refreshes via a fresh /challenge + /respond;
27
- // a lost/uninstalled device burns its slot for at most this long before
28
- // the server prunes it (see verifyService session store). 7 days keeps
29
- // re-prove pressure low on daily-use apps while still bounding the recovery
30
- // window for owners hitting the concurrent-device cap.
31
- export const OWNERSHIP_TOKEN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
32
- // Generate a 128-bit random jti. base64url so it drops into token payloads
33
- // and query strings without escaping.
34
- export function newJti() {
35
- return randomBytes(16).toString('base64url');
36
- }
37
- export function createChallengeStore(ttlMs = OWNERSHIP_CHALLENGE_TTL_MS) {
38
- const issued = new Map(); // `${lh}:${nonce}` → expiresAt
39
- return {
40
- issue(licenseHash) {
41
- const nonce = Field.random().toString();
42
- issued.set(`${licenseHash}:${nonce}`, Date.now() + ttlMs);
43
- return { nonce, ttl: ttlMs };
44
- },
45
- consume(licenseHash, nonce) {
46
- const key = `${licenseHash}:${nonce}`;
47
- const expiresAt = issued.get(key);
48
- if (expiresAt === undefined)
49
- return 'unknown';
50
- issued.delete(key);
51
- if (Date.now() > expiresAt)
52
- return 'expired';
53
- return 'ok';
54
- },
55
- size: () => issued.size,
56
- };
57
- }
58
- // --- token signing -----------------------------------------------------------
59
- export function signOwnershipToken(key, payload) {
60
- const body = Buffer.from(JSON.stringify(payload)).toString('base64url');
61
- const mac = createHmac('sha256', key).update(body).digest('base64url');
62
- return `${body}.${mac}`;
63
- }
64
- export function verifyOwnershipToken(key, token) {
65
- const dot = token.indexOf('.');
66
- if (dot < 0)
67
- return null;
68
- const body = token.slice(0, dot);
69
- const mac = token.slice(dot + 1);
70
- const expected = createHmac('sha256', key).update(body).digest('base64url');
71
- const a = Buffer.from(mac);
72
- const b = Buffer.from(expected);
73
- if (a.length !== b.length || !timingSafeEqual(a, b))
74
- return null;
75
- try {
76
- const payload = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
77
- if (Date.now() > payload.e)
78
- return null;
79
- return payload;
80
- }
81
- catch {
82
- return null;
83
- }
84
- }
85
- export function newOwnershipKey() {
86
- return randomBytes(32);
87
- }
88
- export function createSessionStore() {
89
- // licenseHash → jti → expiresAt
90
- const sessions = new Map();
91
- const pruneLicense = (licenseHash) => {
92
- const perLic = sessions.get(licenseHash);
93
- if (!perLic)
94
- return undefined;
95
- const now = Date.now();
96
- for (const [jti, exp] of perLic)
97
- if (exp <= now)
98
- perLic.delete(jti);
99
- if (perLic.size === 0) {
100
- sessions.delete(licenseHash);
101
- return undefined;
102
- }
103
- return perLic;
104
- };
105
- return {
106
- insert(licenseHash, jti, exp, cap) {
107
- const perLic = pruneLicense(licenseHash) ?? new Map();
108
- if (cap > 0 && perLic.size >= cap && !perLic.has(jti)) {
109
- return { state: 'at-cap', active: perLic.size };
110
- }
111
- perLic.set(jti, exp);
112
- sessions.set(licenseHash, perLic);
113
- return { state: 'ok', active: perLic.size };
114
- },
115
- has(licenseHash, jti) {
116
- const perLic = pruneLicense(licenseHash);
117
- return perLic?.has(jti) ?? false;
118
- },
119
- releaseSeat(licenseHash, jti) {
120
- const perLic = sessions.get(licenseHash);
121
- if (!perLic)
122
- return false;
123
- const had = perLic.delete(jti);
124
- if (perLic.size === 0)
125
- sessions.delete(licenseHash);
126
- return had;
127
- },
128
- reset(licenseHash) {
129
- const perLic = sessions.get(licenseHash);
130
- if (!perLic)
131
- return 0;
132
- const n = perLic.size;
133
- sessions.delete(licenseHash);
134
- return n;
135
- },
136
- activeCount(licenseHash) {
137
- return pruneLicense(licenseHash)?.size ?? 0;
138
- },
139
- size() {
140
- let n = 0;
141
- for (const perLic of sessions.values())
142
- n += perLic.size;
143
- return n;
144
- },
145
- };
146
- }
147
- //# sourceMappingURL=ownership.js.map