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.
- package/README.md +417 -201
- package/build/agent.d.ts +371 -0
- package/build/agent.d.ts.map +1 -0
- package/build/agent.js +553 -0
- package/build/agent.js.map +1 -0
- package/build/agentActions.d.ts +841 -0
- package/build/agentActions.d.ts.map +1 -0
- package/build/agentActions.js +581 -0
- package/build/agentActions.js.map +1 -0
- package/build/agentRecord.d.ts +83 -0
- package/build/agentRecord.d.ts.map +1 -0
- package/build/agentRecord.js +69 -0
- package/build/agentRecord.js.map +1 -0
- package/build/agentVendor.d.ts +220 -0
- package/build/agentVendor.d.ts.map +1 -0
- package/build/agentVendor.js +306 -0
- package/build/agentVendor.js.map +1 -0
- package/build/appsLookup.d.ts +14 -0
- package/build/appsLookup.d.ts.map +1 -0
- package/build/appsLookup.js +12 -0
- package/build/appsLookup.js.map +1 -0
- package/build/archiveBootstrap.d.ts +31 -5
- package/build/archiveBootstrap.d.ts.map +1 -0
- package/build/archiveBootstrap.js +140 -47
- package/build/archiveBootstrap.js.map +1 -0
- package/build/atomicJson.d.ts +5 -0
- package/build/atomicJson.d.ts.map +1 -0
- package/build/atomicJson.js +55 -0
- package/build/atomicJson.js.map +1 -0
- package/build/bindClient.d.ts +85 -0
- package/build/bindClient.d.ts.map +1 -0
- package/build/bindClient.js +187 -0
- package/build/bindClient.js.map +1 -0
- package/build/buyClient.d.ts +27 -4
- package/build/buyClient.d.ts.map +1 -0
- package/build/buyClient.js +72 -26
- package/build/buyClient.js.map +1 -0
- package/build/chainHead.d.ts +61 -0
- package/build/chainHead.d.ts.map +1 -0
- package/build/chainHead.js +88 -0
- package/build/chainHead.js.map +1 -0
- package/build/compactMerkleMap.d.ts +72 -0
- package/build/compactMerkleMap.d.ts.map +1 -0
- package/build/compactMerkleMap.js +224 -0
- package/build/compactMerkleMap.js.map +1 -0
- package/build/contractInterface.d.ts +191 -51
- package/build/contractInterface.d.ts.map +1 -0
- package/build/contractInterface.js +234 -43
- package/build/contractInterface.js.map +1 -0
- package/build/contractTypes.d.ts +9 -0
- package/build/contractTypes.d.ts.map +1 -0
- package/build/contractTypes.js +28 -0
- package/build/contractTypes.js.map +1 -0
- package/build/countryStats.d.ts +12 -3
- package/build/countryStats.d.ts.map +1 -0
- package/build/countryStats.js +87 -23
- package/build/countryStats.js.map +1 -0
- package/build/deployClient.d.ts +138 -24
- package/build/deployClient.d.ts.map +1 -0
- package/build/deployClient.js +91 -31
- package/build/deployClient.js.map +1 -0
- package/build/deviceIdentity.d.ts +68 -0
- package/build/deviceIdentity.d.ts.map +1 -0
- package/build/deviceIdentity.js +209 -0
- package/build/deviceIdentity.js.map +1 -0
- package/build/freezeClient.d.ts +26 -6
- package/build/freezeClient.d.ts.map +1 -0
- package/build/freezeClient.js +30 -6
- package/build/freezeClient.js.map +1 -0
- package/build/generationClient.d.ts +6 -3
- package/build/generationClient.d.ts.map +1 -0
- package/build/generationClient.js +25 -38
- package/build/generationClient.js.map +1 -0
- package/build/holderSecret.d.ts +70 -0
- package/build/holderSecret.d.ts.map +1 -0
- package/build/holderSecret.js +76 -0
- package/build/holderSecret.js.map +1 -0
- package/build/index.d.ts +38 -38
- package/build/index.d.ts.map +1 -0
- package/build/index.js +97 -121
- package/build/index.js.map +1 -0
- package/build/keysClient.d.ts +142 -0
- package/build/keysClient.d.ts.map +1 -0
- package/build/keysClient.js +109 -0
- package/build/keysClient.js.map +1 -0
- package/build/licenseLeaf.d.ts +76 -0
- package/build/licenseLeaf.d.ts.map +1 -0
- package/build/licenseLeaf.js +189 -0
- package/build/licenseLeaf.js.map +1 -0
- package/build/licenseRecord.d.ts +68 -5
- package/build/licenseRecord.d.ts.map +1 -0
- package/build/licenseRecord.js +26 -1
- package/build/licenseRecord.js.map +1 -0
- package/build/licenseStore.d.ts +81 -4
- package/build/licenseStore.d.ts.map +1 -0
- package/build/licenseStore.js +400 -30
- package/build/licenseStore.js.map +1 -0
- package/build/licenseTrustless.d.ts +43 -0
- package/build/licenseTrustless.d.ts.map +1 -0
- package/build/licenseTrustless.js +180 -0
- package/build/licenseTrustless.js.map +1 -0
- package/build/manifestVerify.d.ts +29 -1
- package/build/manifestVerify.d.ts.map +1 -0
- package/build/manifestVerify.js +46 -4
- package/build/manifestVerify.js.map +1 -0
- package/build/minaPriceHistory.d.ts +9 -0
- package/build/minaPriceHistory.d.ts.map +1 -0
- package/build/minaPriceHistory.js +152 -0
- package/build/minaPriceHistory.js.map +1 -0
- package/build/onChainRoot.d.ts +60 -0
- package/build/onChainRoot.d.ts.map +1 -0
- package/build/onChainRoot.js +74 -0
- package/build/onChainRoot.js.map +1 -0
- package/build/ownershipLicenseHash.d.ts +25 -0
- package/build/ownershipLicenseHash.d.ts.map +1 -0
- package/build/ownershipLicenseHash.js +29 -0
- package/build/ownershipLicenseHash.js.map +1 -0
- package/build/ownershipSignature.d.ts +153 -0
- package/build/ownershipSignature.d.ts.map +1 -0
- package/build/ownershipSignature.js +297 -0
- package/build/ownershipSignature.js.map +1 -0
- package/build/permitCheckFlow.d.ts +155 -0
- package/build/permitCheckFlow.d.ts.map +1 -0
- package/build/permitCheckFlow.js +196 -0
- package/build/permitCheckFlow.js.map +1 -0
- package/build/permitDescribe.d.ts +124 -0
- package/build/permitDescribe.d.ts.map +1 -0
- package/build/permitDescribe.js +324 -0
- package/build/permitDescribe.js.map +1 -0
- package/build/permitInterface.d.ts +85 -0
- package/build/permitInterface.d.ts.map +1 -0
- package/build/permitInterface.js +121 -0
- package/build/permitInterface.js.map +1 -0
- package/build/permitMessages.d.ts +313 -0
- package/build/permitMessages.d.ts.map +1 -0
- package/build/permitMessages.js +462 -0
- package/build/permitMessages.js.map +1 -0
- package/build/permitProof.d.ts +108 -0
- package/build/permitProof.d.ts.map +1 -0
- package/build/permitProof.js +202 -0
- package/build/permitProof.js.map +1 -0
- package/build/permitRegistryRebuild.d.ts +166 -0
- package/build/permitRegistryRebuild.d.ts.map +1 -0
- package/build/permitRegistryRebuild.js +347 -0
- package/build/permitRegistryRebuild.js.map +1 -0
- package/build/permitTree.d.ts +88 -0
- package/build/permitTree.d.ts.map +1 -0
- package/build/permitTree.js +230 -0
- package/build/permitTree.js.map +1 -0
- package/build/permitsClient.d.ts +680 -0
- package/build/permitsClient.d.ts.map +1 -0
- package/build/permitsClient.js +597 -0
- package/build/permitsClient.js.map +1 -0
- package/build/proveErrors.d.ts +50 -0
- package/build/proveErrors.d.ts.map +1 -0
- package/build/proveErrors.js +76 -0
- package/build/proveErrors.js.map +1 -0
- package/build/receipt.d.ts +95 -0
- package/build/receipt.d.ts.map +1 -0
- package/build/receipt.js +84 -0
- package/build/receipt.js.map +1 -0
- package/build/refundClient.d.ts +7 -12
- package/build/refundClient.d.ts.map +1 -0
- package/build/refundClient.js +52 -21
- package/build/refundClient.js.map +1 -0
- package/build/registryTrustVerify.d.ts +78 -0
- package/build/registryTrustVerify.d.ts.map +1 -0
- package/build/registryTrustVerify.js +129 -0
- package/build/registryTrustVerify.js.map +1 -0
- package/build/releaseClient.d.ts +26 -0
- package/build/releaseClient.d.ts.map +1 -0
- package/build/releaseClient.js +65 -0
- package/build/releaseClient.js.map +1 -0
- package/build/renewClient.d.ts +28 -2
- package/build/renewClient.d.ts.map +1 -0
- package/build/renewClient.js +31 -27
- package/build/renewClient.js.map +1 -0
- package/build/seatName.d.ts +8 -0
- package/build/seatName.d.ts.map +1 -0
- package/build/seatName.js +136 -0
- package/build/seatName.js.map +1 -0
- package/build/slotConstants.d.ts +15 -0
- package/build/slotConstants.d.ts.map +1 -0
- package/build/slotConstants.js +17 -0
- package/build/slotConstants.js.map +1 -0
- package/build/statsClient.d.ts +61 -7
- package/build/statsClient.d.ts.map +1 -0
- package/build/statsClient.js +44 -5
- package/build/statsClient.js.map +1 -0
- package/build/successorChain.d.ts +35 -0
- package/build/successorChain.d.ts.map +1 -0
- package/build/successorChain.js +69 -0
- package/build/successorChain.js.map +1 -0
- package/build/taxId.d.ts +44 -0
- package/build/taxId.d.ts.map +1 -0
- package/build/taxId.js +212 -0
- package/build/taxId.js.map +1 -0
- package/build/trustAnchorVerify.d.ts +49 -0
- package/build/trustAnchorVerify.d.ts.map +1 -0
- package/build/trustAnchorVerify.js +432 -0
- package/build/trustAnchorVerify.js.map +1 -0
- package/build/trustAnchorVerifyCli.d.ts +45 -0
- package/build/trustAnchorVerifyCli.d.ts.map +1 -0
- package/build/trustAnchorVerifyCli.js +235 -0
- package/build/trustAnchorVerifyCli.js.map +1 -0
- package/build/txBroadcastClient.d.ts +26 -0
- package/build/txBroadcastClient.d.ts.map +1 -0
- package/build/txBroadcastClient.js +20 -0
- package/build/txBroadcastClient.js.map +1 -0
- package/build/unbindClient.d.ts +77 -0
- package/build/unbindClient.d.ts.map +1 -0
- package/build/unbindClient.js +169 -0
- package/build/unbindClient.js.map +1 -0
- package/build/units.d.ts +28 -0
- package/build/units.d.ts.map +1 -0
- package/build/units.js +65 -0
- package/build/units.js.map +1 -0
- package/build/vatRates.d.ts +54 -0
- package/build/vatRates.d.ts.map +1 -0
- package/build/vatRates.js +82 -0
- package/build/vatRates.js.map +1 -0
- package/build/vatRatesData.d.ts +5 -0
- package/build/vatRatesData.d.ts.map +1 -0
- package/build/vatRatesData.js +42 -0
- package/build/vatRatesData.js.map +1 -0
- package/build/vendorAccountClient.d.ts +66 -0
- package/build/vendorAccountClient.d.ts.map +1 -0
- package/build/vendorAccountClient.js +47 -0
- package/build/vendorAccountClient.js.map +1 -0
- package/build/verifyCore.d.ts +4 -1
- package/build/verifyCore.d.ts.map +1 -0
- package/build/verifyCore.js +21 -10
- package/build/verifyCore.js.map +1 -0
- package/build/verifyOffline.d.ts +85 -0
- package/build/verifyOffline.d.ts.map +1 -0
- package/build/verifyOffline.js +139 -0
- package/build/verifyOffline.js.map +1 -0
- package/build/verifyService.d.ts.map +1 -0
- package/build/verifyService.js +571 -501
- package/build/verifyService.js.map +1 -0
- package/build/verifyWithAncestors.d.ts +19 -0
- package/build/verifyWithAncestors.d.ts.map +1 -0
- package/build/verifyWithAncestors.js +127 -0
- package/build/verifyWithAncestors.js.map +1 -0
- package/build/zipToState.d.ts +2 -0
- package/build/zipToState.d.ts.map +1 -0
- package/build/zipToState.js +94 -0
- package/build/zipToState.js.map +1 -0
- package/package.json +141 -16
- package/src/agent.ts +682 -0
- package/src/agentActions.ts +637 -0
- package/src/agentRecord.ts +121 -0
- package/src/agentVendor.ts +356 -0
- package/src/appsLookup.ts +48 -0
- package/src/archiveBootstrap.ts +386 -0
- package/src/atomicJson.ts +53 -0
- package/src/bindClient.ts +297 -0
- package/src/buyClient.ts +344 -0
- package/src/chainHead.ts +106 -0
- package/src/compactMerkleMap.ts +222 -0
- package/src/contractInterface.ts +465 -0
- package/src/contractTypes.ts +31 -0
- package/src/countryStats.ts +200 -0
- package/src/deployClient.ts +517 -0
- package/src/deviceIdentity.ts +249 -0
- package/src/freezeClient.ts +162 -0
- package/src/generationClient.ts +145 -0
- package/src/holderSecret.ts +91 -0
- package/src/index.ts +362 -0
- package/src/keysClient.ts +135 -0
- package/src/licenseLeaf.ts +221 -0
- package/src/licenseRecord.ts +190 -0
- package/src/licenseStore.ts +767 -0
- package/src/licenseTrustless.ts +207 -0
- package/src/manifestVerify.ts +164 -0
- package/src/minaPriceHistory.ts +166 -0
- package/src/onChainRoot.ts +90 -0
- package/src/ownershipLicenseHash.ts +31 -0
- package/src/ownershipSignature.ts +421 -0
- package/src/permitCheckFlow.ts +227 -0
- package/src/permitDescribe.ts +362 -0
- package/src/permitInterface.ts +146 -0
- package/src/permitMessages.ts +572 -0
- package/src/permitProof.ts +230 -0
- package/src/permitRegistryRebuild.ts +423 -0
- package/src/permitTree.ts +232 -0
- package/src/permitsClient.ts +746 -0
- package/src/proveErrors.ts +98 -0
- package/src/receipt.ts +156 -0
- package/src/refundClient.ts +177 -0
- package/src/registryTrustVerify.ts +192 -0
- package/src/releaseClient.ts +101 -0
- package/src/renewClient.ts +234 -0
- package/src/seatName.ts +165 -0
- package/src/slotConstants.ts +18 -0
- package/src/statsClient.ts +249 -0
- package/src/successorChain.ts +87 -0
- package/src/taxId.ts +227 -0
- package/src/trustAnchorVerify.ts +550 -0
- package/src/trustAnchorVerifyCli.ts +234 -0
- package/src/txBroadcastClient.ts +48 -0
- package/src/unbindClient.ts +233 -0
- package/src/units.ts +74 -0
- package/src/vatRates.ts +100 -0
- package/src/vatRatesData.ts +43 -0
- package/src/vendorAccountClient.ts +120 -0
- package/src/verifyCore.ts +100 -0
- package/src/verifyOffline.ts +212 -0
- package/src/verifyService.ts +982 -0
- package/src/verifyWithAncestors.ts +163 -0
- package/src/zipToState.ts +94 -0
- package/build/licenseProof.d.ts +0 -26
- package/build/licenseProof.js +0 -41
- package/build/migrateClient.d.ts +0 -43
- package/build/migrateClient.js +0 -47
- package/build/ownership.d.ts +0 -34
- package/build/ownership.js +0 -147
package/src/chainHead.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* chainHead.ts — reads a zkApp's first state field (its tree root) and the
|
|
6
|
+
* chain's current slot from a Mina node over GraphQL, in one query, without
|
|
7
|
+
* o1js and without touching o1js's global Mina instance.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { DEFAULT_MINA_GRAPHQL_URLS } from './onChainRoot.js';
|
|
11
|
+
|
|
12
|
+
/** `state`: the account's whole app state (decimal strings). */
|
|
13
|
+
export type ChainHead = { root: string; slot: number; state: string[] };
|
|
14
|
+
|
|
15
|
+
export async function readChainHead(args: {
|
|
16
|
+
address: string;
|
|
17
|
+
network: 'devnet' | 'mainnet';
|
|
18
|
+
graphqlUrl?: string;
|
|
19
|
+
fetcher?: typeof fetch;
|
|
20
|
+
}): Promise<ChainHead> {
|
|
21
|
+
const url = args.graphqlUrl ?? DEFAULT_MINA_GRAPHQL_URLS[args.network];
|
|
22
|
+
const { account, slot } = await readZkappHead({ address: args.address, graphqlUrl: url, fetcher: args.fetcher });
|
|
23
|
+
const root = account.appState?.[0];
|
|
24
|
+
if (!root) throw new Error(`${args.address} is not a zkApp on ${args.network}`);
|
|
25
|
+
if (slot === undefined) throw new MinaNodeError(`Mina node ${url} did not report the current slot`, 0);
|
|
26
|
+
return { root, slot, state: account.appState! };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Permission names as a Mina node reports them: 'Proof', 'Signature', 'Either', 'None', 'Impossible'. */
|
|
30
|
+
export type ZkappAccount = {
|
|
31
|
+
found: boolean;
|
|
32
|
+
zkappUri?: string;
|
|
33
|
+
vkHash?: string;
|
|
34
|
+
appState?: string[];
|
|
35
|
+
permissions?: {
|
|
36
|
+
editState?: string; send?: string; setZkappUri?: string; setPermissions?: string;
|
|
37
|
+
setDelegate?: string; setTiming?: string;
|
|
38
|
+
setVerificationKey?: { auth?: string; txnVersion?: number };
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** A Mina node that couldn't be reached or answered with an error; `status` 0 when there was no HTTP answer. */
|
|
43
|
+
export class MinaNodeError extends Error {
|
|
44
|
+
constructor(message: string, readonly status: number) { super(message); }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Reads a zkApp account (state, verification-key hash, URI, permissions)
|
|
49
|
+
* from a Mina node over GraphQL — the fields o1js's fetchAccount reads,
|
|
50
|
+
* without o1js and its global Mina instance. `found: false` when the node
|
|
51
|
+
* has no such account.
|
|
52
|
+
*/
|
|
53
|
+
export async function readZkappAccount(args: { address: string; graphqlUrl: string; fetcher?: typeof fetch }): Promise<ZkappAccount> {
|
|
54
|
+
return (await readZkappHead(args)).account;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The one GraphQL read behind readZkappAccount and readChainHead: the
|
|
59
|
+
* account and the chain's current slot (`undefined` when the node didn't
|
|
60
|
+
* report it) in one query. Throws MinaNodeError.
|
|
61
|
+
*/
|
|
62
|
+
export async function readZkappHead(args: { address: string; graphqlUrl: string; fetcher?: typeof fetch }): Promise<{ account: ZkappAccount; slot?: number }> {
|
|
63
|
+
const query = `query($pk: PublicKey!) {
|
|
64
|
+
account(publicKey: $pk) {
|
|
65
|
+
zkappUri
|
|
66
|
+
zkappState
|
|
67
|
+
verificationKey { hash }
|
|
68
|
+
permissions { editState send setZkappUri setPermissions setDelegate setTiming setVerificationKey { auth txnVersion } }
|
|
69
|
+
}
|
|
70
|
+
bestChain(maxLength: 1) { protocolState { consensusState { slotSinceGenesis } } }
|
|
71
|
+
}`;
|
|
72
|
+
let resp: Response;
|
|
73
|
+
try {
|
|
74
|
+
resp = await (args.fetcher ?? fetch)(args.graphqlUrl, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify({ query, variables: { pk: args.address } }),
|
|
78
|
+
});
|
|
79
|
+
} catch (e: any) {
|
|
80
|
+
throw new MinaNodeError(`Mina node ${args.graphqlUrl}: ${e?.message ?? e}`, 0);
|
|
81
|
+
}
|
|
82
|
+
const body = await resp.json().catch(() => null) as { data?: { account?: any; bestChain?: any[] }; errors?: Array<{ message?: string }> } | null;
|
|
83
|
+
if (!resp.ok || !body?.data) {
|
|
84
|
+
throw new MinaNodeError(`Mina node ${args.graphqlUrl}: ${body?.errors?.map(e => e.message).join('; ') || `HTTP ${resp.status}`}`, resp.status);
|
|
85
|
+
}
|
|
86
|
+
const rawSlot = body.data.bestChain?.[0]?.protocolState?.consensusState?.slotSinceGenesis;
|
|
87
|
+
const slot = rawSlot === undefined || rawSlot === null || !Number.isFinite(Number(rawSlot)) ? undefined : Number(rawSlot);
|
|
88
|
+
const a = body.data.account;
|
|
89
|
+
if (!a) return { account: { found: false }, slot };
|
|
90
|
+
const p = a.permissions ?? {};
|
|
91
|
+
const txn = p.setVerificationKey?.txnVersion;
|
|
92
|
+
return {
|
|
93
|
+
slot,
|
|
94
|
+
account: {
|
|
95
|
+
found: true,
|
|
96
|
+
zkappUri: a.zkappUri ?? undefined,
|
|
97
|
+
vkHash: a.verificationKey?.hash != null ? String(a.verificationKey.hash) : undefined,
|
|
98
|
+
appState: Array.isArray(a.zkappState) ? a.zkappState.map(String) : undefined,
|
|
99
|
+
permissions: {
|
|
100
|
+
editState: p.editState, send: p.send, setZkappUri: p.setZkappUri, setPermissions: p.setPermissions,
|
|
101
|
+
setDelegate: p.setDelegate, setTiming: p.setTiming,
|
|
102
|
+
setVerificationKey: p.setVerificationKey ? { auth: p.setVerificationKey.auth, txnVersion: txn === undefined || txn === null ? undefined : Number(txn) } : undefined,
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Copyright (c) 2025-2026 zkLicensing project developers
|
|
2
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* compactMerkleMap.ts
|
|
6
|
+
*
|
|
7
|
+
* A drop-in for o1js's MerkleMap (height 256: set, getRoot, getWitness — same
|
|
8
|
+
* positions, hashes and witnesses) that stores only what the tree can't
|
|
9
|
+
* compute cheaply: the leaves, and the values of the two children of every
|
|
10
|
+
* node where two leaves' paths split ("split children", 2(N − 1) of them).
|
|
11
|
+
* Every other node a witness needs is an empty subtree, a known constant.
|
|
12
|
+
*
|
|
13
|
+
* So it can be saved and loaded without re-hashing (toCompact / fromCompact):
|
|
14
|
+
* a MerkleMap would have to be rebuilt, one 256-hash path per leaf. Changes
|
|
15
|
+
* (setMany) re-hash only the paths above the changed leaves.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { Bool, Field, MerkleMap, MerkleMapWitness, Poseidon } from 'o1js';
|
|
19
|
+
|
|
20
|
+
export const TREE_HEIGHT = 256;
|
|
21
|
+
|
|
22
|
+
let zeroCache: bigint[] | null = null;
|
|
23
|
+
/** zeros()[l]: the value of an empty subtree whose top is at level l. */
|
|
24
|
+
export function zeros(): bigint[] {
|
|
25
|
+
if (!zeroCache) {
|
|
26
|
+
const z = [0n];
|
|
27
|
+
for (let i = 1; i < TREE_HEIGHT; i++) z.push(Poseidon.hash([Field(z[i - 1]), Field(z[i - 1])]).toBigInt());
|
|
28
|
+
zeroCache = z;
|
|
29
|
+
}
|
|
30
|
+
return zeroCache;
|
|
31
|
+
}
|
|
32
|
+
const H = (a: bigint, b: bigint) => Poseidon.hash([Field(a), Field(b)]).toBigInt();
|
|
33
|
+
|
|
34
|
+
const keyMap = new MerkleMap();
|
|
35
|
+
/** A key's leaf position, as MerkleMap places it. */
|
|
36
|
+
export function keyIndex(key: Field | string | bigint): bigint {
|
|
37
|
+
return (keyMap as any)._keyToIndex(Field(key)) as bigint;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function bitLength(x: bigint): number { let n = 0; while (x > 0n) { x >>= 1n; n++; } return n; }
|
|
41
|
+
|
|
42
|
+
/** The split-children positions [level, index], in canonical order, for sorted leaf positions. */
|
|
43
|
+
export function splitChildren(sorted: bigint[]): Array<[number, bigint]> {
|
|
44
|
+
const out: Array<[number, bigint]> = [];
|
|
45
|
+
for (let i = 0; i + 1 < sorted.length; i++) {
|
|
46
|
+
const a = sorted[i], b = sorted[i + 1];
|
|
47
|
+
const l = bitLength(a ^ b) - 1; // the children's level; they meet at l + 1
|
|
48
|
+
out.push([l, a >> BigInt(l)], [l, b >> BigInt(l)]);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Field values as 32-byte big-endian words, base64. */
|
|
54
|
+
export function encodeFields(values: bigint[]): string {
|
|
55
|
+
const bytes = new Uint8Array(values.length * 32);
|
|
56
|
+
values.forEach((v, i) => { for (let k = 31; k >= 0; k--) { bytes[i * 32 + k] = Number(v & 0xffn); v >>= 8n; } });
|
|
57
|
+
let bin = '';
|
|
58
|
+
for (let i = 0; i < bytes.length; i += 0x8000) bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
59
|
+
return btoa(bin);
|
|
60
|
+
}
|
|
61
|
+
export function decodeFields(b64: string): bigint[] {
|
|
62
|
+
const bin = atob(b64);
|
|
63
|
+
if (bin.length % 32) throw new Error('compact data is not a whole number of fields');
|
|
64
|
+
const out: bigint[] = [];
|
|
65
|
+
for (let i = 0; i < bin.length; i += 32) {
|
|
66
|
+
let v = 0n;
|
|
67
|
+
for (let k = 0; k < 32; k++) v = (v << 8n) | BigInt(bin.charCodeAt(i + k));
|
|
68
|
+
out.push(v);
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const nodeKey = (l: number, j: bigint) => `${l}:${j}`;
|
|
74
|
+
const cmp = (a: bigint, b: bigint) => (a < b ? -1 : a > b ? 1 : 0);
|
|
75
|
+
/** First position >= x in a sorted array. */
|
|
76
|
+
function lower(arr: bigint[], x: bigint): number {
|
|
77
|
+
let lo = 0, hi = arr.length;
|
|
78
|
+
while (lo < hi) { const m = (lo + hi) >> 1; if (arr[m] < x) lo = m + 1; else hi = m; }
|
|
79
|
+
return lo;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type CompactMapData = {
|
|
83
|
+
/** Leaf positions, sorted. */
|
|
84
|
+
indices: bigint[];
|
|
85
|
+
/** Leaf values, in the same order. */
|
|
86
|
+
leaves: bigint[];
|
|
87
|
+
/** Split-children values, in splitChildren order. */
|
|
88
|
+
splits: bigint[];
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export class CompactMerkleMap {
|
|
92
|
+
private idx: bigint[] = [];
|
|
93
|
+
private leaves = new Map<bigint, bigint>();
|
|
94
|
+
private split = new Map<string, bigint>();
|
|
95
|
+
private root: bigint = zeros()[TREE_HEIGHT - 1];
|
|
96
|
+
|
|
97
|
+
/** Number of non-empty leaves. */
|
|
98
|
+
get size(): number { return this.idx.length; }
|
|
99
|
+
|
|
100
|
+
getRoot(): Field { return Field(this.root); }
|
|
101
|
+
|
|
102
|
+
set(key: Field, value: Field): void { this.setMany([[key, value]]); }
|
|
103
|
+
|
|
104
|
+
/** Sets several leaves, re-hashing only above them. */
|
|
105
|
+
setMany(entries: Iterable<[Field | string, Field | bigint]>): void {
|
|
106
|
+
const changed: bigint[] = [];
|
|
107
|
+
for (const [k, v] of entries) {
|
|
108
|
+
const i = keyIndex(k);
|
|
109
|
+
const val = typeof v === 'bigint' ? v : v.toBigInt();
|
|
110
|
+
if (!this.leaves.has(i)) this.idx.splice(lower(this.idx, i), 0, i);
|
|
111
|
+
this.leaves.set(i, val);
|
|
112
|
+
changed.push(i);
|
|
113
|
+
}
|
|
114
|
+
if (changed.length === 0) return;
|
|
115
|
+
changed.sort(cmp);
|
|
116
|
+
this.recompute(changed);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** A MerkleMapWitness for `key`, as MerkleMap.getWitness gives it. */
|
|
120
|
+
getWitness(key: Field): MerkleMapWitness {
|
|
121
|
+
const { isLefts, siblings } = this.witnessValues(keyIndex(key));
|
|
122
|
+
return new MerkleMapWitness(isLefts.map(b => Bool(b)), siblings.map(s => Field(s)));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
witnessValues(s: bigint): { isLefts: boolean[]; siblings: bigint[] } {
|
|
126
|
+
const isLefts: boolean[] = [];
|
|
127
|
+
const siblings: bigint[] = [];
|
|
128
|
+
const memo = new Map<string, bigint>();
|
|
129
|
+
for (let l = 0; l < TREE_HEIGHT - 1; l++) {
|
|
130
|
+
const at = s >> BigInt(l);
|
|
131
|
+
isLefts.push((at & 1n) === 0n);
|
|
132
|
+
// A split child, or empty — except for a key not in the map, whose
|
|
133
|
+
// path meets one non-empty subtree that isn't a split child: computed.
|
|
134
|
+
siblings.push(this.split.get(nodeKey(l, at ^ 1n)) ?? this.valueOf(l, at ^ 1n, [], memo));
|
|
135
|
+
}
|
|
136
|
+
return { isLefts, siblings };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** A node's value, when it is a leaf, a split child, or empty; otherwise undefined. */
|
|
140
|
+
getNode(level: number, index: bigint): bigint | undefined {
|
|
141
|
+
if (level === 0 && this.leaves.has(index)) return this.leaves.get(index);
|
|
142
|
+
const v = this.split.get(nodeKey(level, index));
|
|
143
|
+
if (v !== undefined) return v;
|
|
144
|
+
const lo = index << BigInt(level), hi = ((index + 1n) << BigInt(level)) - 1n;
|
|
145
|
+
return lower(this.idx, lo) === lower(this.idx, hi + 1n) ? zeros()[level] : undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** The split-children values in canonical order (for compact copies). */
|
|
149
|
+
splitValues(): bigint[] {
|
|
150
|
+
return splitChildren(this.idx).map(([l, j]) => this.split.get(nodeKey(l, j))!);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
toCompact(): CompactMapData {
|
|
154
|
+
return { indices: [...this.idx], leaves: this.idx.map(i => this.leaves.get(i)!), splits: this.splitValues() };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Loads a saved copy without re-hashing the tree: only the path from the
|
|
159
|
+
* root down to the first split. Throws if the data is inconsistent, or if
|
|
160
|
+
* `expectedRoot` is given and the copy doesn't fold to it. Values deeper
|
|
161
|
+
* down aren't re-checked (that would cost the full rebuild): protect a
|
|
162
|
+
* stored copy with a checksum.
|
|
163
|
+
*/
|
|
164
|
+
static fromCompact(d: CompactMapData, expectedRoot?: string): CompactMerkleMap {
|
|
165
|
+
const m = new CompactMerkleMap();
|
|
166
|
+
for (let i = 1; i < d.indices.length; i++) if (d.indices[i] <= d.indices[i - 1]) throw new Error('compact map: positions are not sorted');
|
|
167
|
+
if (d.leaves.length !== d.indices.length) throw new Error('compact map: one value per leaf expected');
|
|
168
|
+
const positions = splitChildren(d.indices);
|
|
169
|
+
if (d.splits.length !== positions.length) throw new Error(`compact map: expected ${positions.length} split values, got ${d.splits.length}`);
|
|
170
|
+
m.idx = [...d.indices];
|
|
171
|
+
d.indices.forEach((i, k) => m.leaves.set(i, d.leaves[k]));
|
|
172
|
+
positions.forEach(([l, j], k) => m.split.set(nodeKey(l, j), d.splits[k]));
|
|
173
|
+
m.root = m.computeRoot([]);
|
|
174
|
+
if (expectedRoot !== undefined && m.root.toString() !== expectedRoot) throw new Error('compact map: the saved copy does not fold to its root');
|
|
175
|
+
return m;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Recomputes split values above the changed positions (sorted), and the root. */
|
|
179
|
+
private recompute(changed: bigint[]): void {
|
|
180
|
+
this.root = this.computeRoot(changed);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private computeRoot(changed: bigint[]): bigint {
|
|
184
|
+
const memo = new Map<string, bigint>();
|
|
185
|
+
const next = new Map<string, bigint>();
|
|
186
|
+
for (const [l, j] of splitChildren(this.idx)) next.set(nodeKey(l, j), this.valueOf(l, j, changed, memo));
|
|
187
|
+
const root = this.valueOf(TREE_HEIGHT - 1, 0n, changed, memo);
|
|
188
|
+
this.split = next;
|
|
189
|
+
return root;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* A node's value: empty, a leaf, a split child kept from before when no
|
|
194
|
+
* changed leaf is below it, one leaf's path, or the hash of its children.
|
|
195
|
+
*/
|
|
196
|
+
private valueOf(l: number, j: bigint, changed: bigint[], memo: Map<string, bigint>): bigint {
|
|
197
|
+
const k = nodeKey(l, j);
|
|
198
|
+
const m = memo.get(k);
|
|
199
|
+
if (m !== undefined) return m;
|
|
200
|
+
const z = zeros();
|
|
201
|
+
const idx = this.idx;
|
|
202
|
+
const lo = j << BigInt(l), hi = ((j + 1n) << BigInt(l)) - 1n;
|
|
203
|
+
const a = lower(idx, lo), b = lower(idx, hi + 1n);
|
|
204
|
+
let v: bigint;
|
|
205
|
+
if (a === b) v = z[l];
|
|
206
|
+
else if (l === 0) v = this.leaves.get(j)!;
|
|
207
|
+
else {
|
|
208
|
+
const touched = lower(changed, lo) !== lower(changed, hi + 1n);
|
|
209
|
+
const kept = touched ? undefined : this.split.get(k);
|
|
210
|
+
if (kept !== undefined) v = kept;
|
|
211
|
+
else if (b - a === 1) {
|
|
212
|
+
// One leaf below: its path up to here, beside empty subtrees.
|
|
213
|
+
const pos = idx[a];
|
|
214
|
+
let h = this.leaves.get(pos)!;
|
|
215
|
+
for (let t = 0; t < l; t++) h = ((pos >> BigInt(t)) & 1n) === 0n ? H(h, z[t]) : H(z[t], h);
|
|
216
|
+
v = h;
|
|
217
|
+
} else v = H(this.valueOf(l - 1, 2n * j, changed, memo), this.valueOf(l - 1, 2n * j + 1n, changed, memo));
|
|
218
|
+
}
|
|
219
|
+
memo.set(k, v);
|
|
220
|
+
return v;
|
|
221
|
+
}
|
|
222
|
+
}
|