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,232 @@
1
+ // Copyright (c) 2025-2026 zkLicensing project developers
2
+ // SPDX-License-Identifier: BUSL-1.1
3
+
4
+ /**
5
+ * permitTree.ts — `zklicensing/permits/tree`
6
+ *
7
+ * A compact copy of a registry's permits tree, for holders to build their
8
+ * Merkle witness without the keeper learning which permit is theirs, and
9
+ * without rebuilding the 256-level tree (one 256-hash path per permit,
10
+ * about 0.1 s each).
11
+ *
12
+ * The tree is sparse: permits sit at random-looking positions, so below the
13
+ * top few levels nearly every permit is alone in its subtree and everything
14
+ * beside it is empty — known constants. The only non-constant siblings any
15
+ * witness needs are the two children of each node where two permits' paths
16
+ * split: with the permits sorted by position, one such node per adjacent
17
+ * pair, N − 1 in all. The compact tree is every permit's record plus those
18
+ * 2(N − 1) node values. Everyone downloads the same thing; a wrong copy only
19
+ * makes the holder's proof fail against the chain's root, never pass.
20
+ *
21
+ * Updates: `applyTreeDiff` takes the records that changed since a root the
22
+ * holder has and recomputes only the nodes above them.
23
+ */
24
+
25
+ import { Field, MerkleMap, Poseidon } from 'o1js';
26
+ import { permitLeafHash } from './permitMessages.js';
27
+ import { recordToLeaf, type PermitRecord } from './permitRegistryRebuild.js';
28
+ import { CompactMerkleMap, encodeFields, decodeFields } from './compactMerkleMap.js';
29
+
30
+ const HEIGHT = 256;
31
+
32
+ export type CompactTree = {
33
+ registry: string;
34
+ root: string;
35
+ slot: number;
36
+ /** Every permit, sorted by tree position. */
37
+ permits: PermitRecord[];
38
+ /** The 2(N − 1) split-children values, 32 bytes each, big-endian, base64. */
39
+ nodes: string;
40
+ };
41
+
42
+ export type TreeDiff = { registry: string; root: string; slot: number; since: string; changed: PermitRecord[] };
43
+
44
+ // ── field and position helpers ───────────────────────────────────────────────
45
+
46
+ let zeroCache: bigint[] | null = null;
47
+ /** zeros[l]: the value of an empty subtree whose top is at level l. */
48
+ function zeros(): bigint[] {
49
+ if (!zeroCache) {
50
+ const z = [0n];
51
+ for (let i = 1; i < HEIGHT; i++) z.push(Poseidon.hash([Field(z[i - 1]), Field(z[i - 1])]).toBigInt());
52
+ zeroCache = z;
53
+ }
54
+ return zeroCache;
55
+ }
56
+ const H = (a: bigint, b: bigint) => Poseidon.hash([Field(a), Field(b)]).toBigInt();
57
+
58
+ const keyMap = new MerkleMap();
59
+ /** A subject's leaf position (as MerkleMap places it). */
60
+ export function subjectIndex(subject: string): bigint {
61
+ return (keyMap as any)._keyToIndex(Field(subject)) as bigint;
62
+ }
63
+
64
+ function bitLength(x: bigint): number { let n = 0; while (x > 0n) { x >>= 1n; n++; } return n; }
65
+
66
+ /** The split-children positions, [level, index] pairs, in canonical order, for sorted leaf positions. */
67
+ function splitChildren(sorted: bigint[]): Array<[number, bigint]> {
68
+ const out: Array<[number, bigint]> = [];
69
+ for (let i = 0; i + 1 < sorted.length; i++) {
70
+ const a = sorted[i], b = sorted[i + 1];
71
+ const l = bitLength(a ^ b) - 1; // the children's level; their parent at l + 1 is where the paths split
72
+ out.push([l, a >> BigInt(l)], [l, b >> BigInt(l)]);
73
+ }
74
+ return out;
75
+ }
76
+
77
+ function sortPermits(permits: PermitRecord[]): { permits: PermitRecord[]; idx: bigint[] } {
78
+ const withIdx = permits.map(p => ({ p, i: subjectIndex(p.subject) }));
79
+ withIdx.sort((x, y) => (x.i < y.i ? -1 : x.i > y.i ? 1 : 0));
80
+ return { permits: withIdx.map(x => x.p), idx: withIdx.map(x => x.i) };
81
+ }
82
+
83
+ const nodeKey = (l: number, j: bigint) => `${l}:${j}`;
84
+
85
+ // ── keeper side ──────────────────────────────────────────────────────────────
86
+
87
+ /**
88
+ * The compact tree from a full permits tree (the keeper's MerkleMap). Reads
89
+ * node values; no hashing.
90
+ */
91
+ export function compactTreeFrom(args: { registry: string; slot: number; tree: MerkleMap | CompactMerkleMap; permits: Iterable<PermitRecord> }): CompactTree {
92
+ const { permits, idx } = sortPermits([...args.permits]);
93
+ let values: bigint[];
94
+ if (args.tree instanceof CompactMerkleMap) {
95
+ values = args.tree.splitValues();
96
+ } else {
97
+ const full = (args.tree as any).tree as { getNode(level: number, index: bigint): Field };
98
+ values = splitChildren(idx).map(([l, j]) => full.getNode(l, j).toBigInt());
99
+ }
100
+ if (values.length !== Math.max(0, permits.length - 1) * 2) throw new Error('compact tree: the tree and the permits list disagree');
101
+ return { registry: args.registry, root: args.tree.getRoot().toString(), slot: args.slot, permits, nodes: encodeFields(values) };
102
+ }
103
+
104
+ // ── holder side ──────────────────────────────────────────────────────────────
105
+
106
+ type Indexed = { ct: CompactTree; idx: bigint[]; values: Map<string, bigint> };
107
+ const indexed = new WeakMap<CompactTree, Indexed>();
108
+
109
+ function index(ct: CompactTree): Indexed {
110
+ let x = indexed.get(ct);
111
+ if (x) return x;
112
+ const idx = ct.permits.map(p => subjectIndex(p.subject));
113
+ for (let i = 1; i < idx.length; i++) if (idx[i] <= idx[i - 1]) throw new Error('compact tree: permits are not sorted by position');
114
+ const positions = splitChildren(idx);
115
+ const vals = ct.nodes ? decodeFields(ct.nodes) : [];
116
+ if (vals.length !== positions.length) throw new Error(`compact tree: expected ${positions.length} node values, got ${vals.length}`);
117
+ const values = new Map<string, bigint>();
118
+ positions.forEach(([l, j], i) => values.set(nodeKey(l, j), vals[i]));
119
+ x = { ct, idx, values };
120
+ indexed.set(ct, x);
121
+ return x;
122
+ }
123
+
124
+ /**
125
+ * The Merkle witness for `subject` from the compact tree, checked by folding
126
+ * it to the tree's root (one path of hashes).
127
+ */
128
+ export function witnessFromCompactTree(ct: CompactTree, subject: string): { root: string; witness: { isLefts: boolean[]; siblings: string[] }; permit: PermitRecord } {
129
+ const { values } = index(ct);
130
+ const permit = ct.permits.find(p => p.subject === subject);
131
+ if (!permit) throw new Error('The registry has no such permit.');
132
+ const z = zeros();
133
+ const s = subjectIndex(subject);
134
+ const isLefts: boolean[] = [];
135
+ const siblings: string[] = [];
136
+ let h = permitLeafHash(recordToLeaf(permit)).toBigInt();
137
+ for (let l = 0; l < HEIGHT - 1; l++) {
138
+ const at = s >> BigInt(l);
139
+ const left = (at & 1n) === 0n;
140
+ const sib = values.get(nodeKey(l, at ^ 1n)) ?? z[l];
141
+ isLefts.push(left);
142
+ siblings.push(sib.toString());
143
+ h = left ? H(h, sib) : H(sib, h);
144
+ }
145
+ if (h.toString() !== ct.root) throw new Error('The registry copy does not fold to its root; download it again.');
146
+ return { root: ct.root, witness: { isLefts, siblings }, permit };
147
+ }
148
+
149
+ /**
150
+ * Applies the records that changed since `old.root`, recomputing only the
151
+ * nodes above them, and checks the result against `diff.root`.
152
+ */
153
+ export function applyTreeDiff(old: CompactTree, diff: TreeDiff): CompactTree {
154
+ if (diff.since !== old.root) throw new Error('This update is for another version of the registry.');
155
+ if (diff.registry !== old.registry) throw new Error('This update is for another registry.');
156
+ const prev = index(old);
157
+ const bySubject = new Map(old.permits.map(p => [p.subject, p] as const));
158
+ for (const p of diff.changed) bySubject.set(p.subject, p);
159
+ const { permits, idx } = sortPermits([...bySubject.values()]);
160
+ const changedIdx = diff.changed.map(p => subjectIndex(p.subject)).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
161
+ const leafHash = new Map<bigint, bigint>(permits.map((p, i) => [idx[i], permitLeafHash(recordToLeaf(p)).toBigInt()]));
162
+ const z = zeros();
163
+
164
+ // First position >= x in a sorted array.
165
+ const lower = (arr: bigint[], x: bigint) => { let lo = 0, hi = arr.length; while (lo < hi) { const m = (lo + hi) >> 1; if (arr[m] < x) lo = m + 1; else hi = m; } return lo; };
166
+ const inRange = (arr: bigint[], lo: bigint, hi: bigint) => { const a = lower(arr, lo); return { a, b: lower(arr, hi + 1n) }; };
167
+
168
+ const memo = new Map<string, bigint>();
169
+ const node = (l: number, j: bigint): bigint => {
170
+ const k = nodeKey(l, j);
171
+ const m = memo.get(k);
172
+ if (m !== undefined) return m;
173
+ const lo = j << BigInt(l), hi = ((j + 1n) << BigInt(l)) - 1n;
174
+ const leaves = inRange(idx, lo, hi);
175
+ let v: bigint;
176
+ if (leaves.b === leaves.a) v = z[l];
177
+ else {
178
+ const touched = inRange(changedIdx, lo, hi);
179
+ const kept = touched.a === touched.b ? prev.values.get(k) : undefined;
180
+ if (kept !== undefined) v = kept;
181
+ else if (l === 0) v = leafHash.get(j)!;
182
+ else if (leaves.b - leaves.a === 1) {
183
+ // One permit below: hash its path up to here, beside empty subtrees.
184
+ const pos = idx[leaves.a];
185
+ let h = leafHash.get(pos)!;
186
+ for (let t = 0; t < l; t++) h = ((pos >> BigInt(t)) & 1n) === 0n ? H(h, z[t]) : H(z[t], h);
187
+ v = h;
188
+ } else v = H(node(l - 1, 2n * j), node(l - 1, 2n * j + 1n));
189
+ }
190
+ memo.set(k, v);
191
+ return v;
192
+ };
193
+
194
+ const values = splitChildren(idx).map(([l, j]) => node(l, j));
195
+ const root = node(HEIGHT - 1, 0n);
196
+ if (root.toString() !== diff.root) throw new Error('The updated registry copy does not match the new root; download it again.');
197
+ return { registry: old.registry, root: diff.root, slot: diff.slot, permits, nodes: encodeFields(values) };
198
+ }
199
+
200
+ /** Where a holder keeps its registry copies (IndexedDB in a browser, memory or a file elsewhere). */
201
+ export type TreeCache = {
202
+ get(registry: string): Promise<CompactTree | null>;
203
+ set(registry: string, tree: CompactTree): Promise<void>;
204
+ };
205
+
206
+ /**
207
+ * The registry's current compact tree: the cached copy brought up to date
208
+ * with a diff when the keeper has one, else a full download.
209
+ */
210
+ export async function syncRegistryTree(args: {
211
+ registry: string;
212
+ fetchTree: (since?: string) => Promise<CompactTree | TreeDiff | { root: string; slot: number; unchanged: true }>;
213
+ cache?: TreeCache;
214
+ onProgress?: (what: 'cached' | 'diff' | 'full') => void;
215
+ }): Promise<CompactTree> {
216
+ const cached = args.cache ? await args.cache.get(args.registry).catch(() => null) : null;
217
+ if (cached) {
218
+ try {
219
+ const res: any = await args.fetchTree(cached.root);
220
+ let next: CompactTree | null = null;
221
+ if (res.unchanged) { next = { ...cached, slot: res.slot }; args.onProgress?.('cached'); }
222
+ else if (Array.isArray(res.changed)) { next = applyTreeDiff(cached, res as TreeDiff); args.onProgress?.('diff'); }
223
+ else if (Array.isArray(res.permits)) { next = res as CompactTree; args.onProgress?.('full'); }
224
+ if (next) { await args.cache!.set(args.registry, next).catch(() => {}); return next; }
225
+ } catch { /* fall through to a full download */ }
226
+ }
227
+ const full = await args.fetchTree() as CompactTree;
228
+ if (!Array.isArray(full.permits)) throw new Error('The keeper did not send the registry.');
229
+ args.onProgress?.('full');
230
+ await args.cache?.set(args.registry, full).catch(() => {});
231
+ return full;
232
+ }