@cofhe/sdk 0.1.0 → 0.2.0
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/CHANGELOG.md +62 -0
- package/adapters/ethers5.test.ts +174 -0
- package/adapters/ethers5.ts +36 -0
- package/adapters/ethers6.test.ts +169 -0
- package/adapters/ethers6.ts +36 -0
- package/adapters/hardhat-node.ts +167 -0
- package/adapters/hardhat.hh2.test.ts +159 -0
- package/adapters/hardhat.ts +36 -0
- package/adapters/index.test.ts +20 -0
- package/adapters/index.ts +5 -0
- package/adapters/smartWallet.ts +99 -0
- package/adapters/test-utils.ts +53 -0
- package/adapters/types.ts +6 -0
- package/adapters/wagmi.test.ts +156 -0
- package/adapters/wagmi.ts +17 -0
- package/chains/chains/arbSepolia.ts +14 -0
- package/chains/chains/baseSepolia.ts +14 -0
- package/chains/chains/hardhat.ts +15 -0
- package/chains/chains/localcofhe.ts +14 -0
- package/chains/chains/sepolia.ts +14 -0
- package/chains/chains.test.ts +50 -0
- package/chains/defineChain.ts +18 -0
- package/chains/index.ts +35 -0
- package/chains/types.ts +32 -0
- package/core/baseBuilder.ts +119 -0
- package/core/client.test.ts +315 -0
- package/core/client.ts +292 -0
- package/core/clientTypes.ts +108 -0
- package/core/config.test.ts +235 -0
- package/core/config.ts +220 -0
- package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
- package/core/decrypt/cofheMocksSealOutput.ts +57 -0
- package/core/decrypt/decryptHandleBuilder.ts +287 -0
- package/core/decrypt/decryptUtils.ts +28 -0
- package/core/decrypt/tnSealOutputV1.ts +59 -0
- package/core/decrypt/tnSealOutputV2.ts +298 -0
- package/core/encrypt/MockZkVerifierAbi.ts +106 -0
- package/core/encrypt/cofheMocksZkVerifySign.ts +284 -0
- package/core/encrypt/encryptInputsBuilder.test.ts +751 -0
- package/core/encrypt/encryptInputsBuilder.ts +560 -0
- package/core/encrypt/encryptUtils.ts +67 -0
- package/core/encrypt/zkPackProveVerify.ts +335 -0
- package/core/error.ts +168 -0
- package/core/fetchKeys.test.ts +195 -0
- package/core/fetchKeys.ts +144 -0
- package/core/index.ts +89 -0
- package/core/keyStore.test.ts +226 -0
- package/core/keyStore.ts +154 -0
- package/core/permits.test.ts +494 -0
- package/core/permits.ts +200 -0
- package/core/types.ts +398 -0
- package/core/utils.ts +130 -0
- package/dist/adapters.cjs +88 -0
- package/dist/adapters.d.cts +14576 -0
- package/dist/adapters.d.ts +14576 -0
- package/dist/adapters.js +83 -0
- package/dist/chains.cjs +114 -0
- package/dist/chains.d.cts +121 -0
- package/dist/chains.d.ts +121 -0
- package/dist/chains.js +1 -0
- package/dist/chunk-UGBVZNRT.js +818 -0
- package/dist/chunk-WEAZ25JO.js +105 -0
- package/dist/chunk-WGCRJCBR.js +2523 -0
- package/dist/clientTypes-5_1nwtUe.d.cts +914 -0
- package/dist/clientTypes-Es7fyi65.d.ts +914 -0
- package/dist/core.cjs +3414 -0
- package/dist/core.d.cts +111 -0
- package/dist/core.d.ts +111 -0
- package/dist/core.js +3 -0
- package/dist/node.cjs +3286 -0
- package/dist/node.d.cts +22 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +91 -0
- package/dist/permit-fUSe6KKq.d.cts +349 -0
- package/dist/permit-fUSe6KKq.d.ts +349 -0
- package/dist/permits.cjs +871 -0
- package/dist/permits.d.cts +1045 -0
- package/dist/permits.d.ts +1045 -0
- package/dist/permits.js +1 -0
- package/dist/types-KImPrEIe.d.cts +48 -0
- package/dist/types-KImPrEIe.d.ts +48 -0
- package/dist/web.cjs +3478 -0
- package/dist/web.d.cts +38 -0
- package/dist/web.d.ts +38 -0
- package/dist/web.js +240 -0
- package/dist/zkProve.worker.cjs +93 -0
- package/dist/zkProve.worker.d.cts +2 -0
- package/dist/zkProve.worker.d.ts +2 -0
- package/dist/zkProve.worker.js +91 -0
- package/node/client.test.ts +147 -0
- package/node/config.test.ts +68 -0
- package/node/encryptInputs.test.ts +155 -0
- package/node/index.ts +97 -0
- package/node/storage.ts +51 -0
- package/package.json +27 -15
- package/permits/index.ts +68 -0
- package/permits/localstorage.test.ts +117 -0
- package/permits/permit.test.ts +477 -0
- package/permits/permit.ts +405 -0
- package/permits/sealing.test.ts +84 -0
- package/permits/sealing.ts +131 -0
- package/permits/signature.ts +79 -0
- package/permits/store.test.ts +128 -0
- package/permits/store.ts +166 -0
- package/permits/test-utils.ts +20 -0
- package/permits/types.ts +191 -0
- package/permits/utils.ts +62 -0
- package/permits/validation.test.ts +288 -0
- package/permits/validation.ts +369 -0
- package/web/client.web.test.ts +147 -0
- package/web/config.web.test.ts +69 -0
- package/web/encryptInputs.web.test.ts +172 -0
- package/web/index.ts +161 -0
- package/web/storage.ts +34 -0
- package/web/worker.builder.web.test.ts +148 -0
- package/web/worker.config.web.test.ts +329 -0
- package/web/worker.output.web.test.ts +84 -0
- package/web/workerManager.test.ts +80 -0
- package/web/workerManager.ts +214 -0
- package/web/workerManager.web.test.ts +114 -0
- package/web/zkProve.worker.ts +133 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ZkBuilderAndCrsGenerator,
|
|
3
|
+
type ZkProveWorkerFunction,
|
|
4
|
+
zkPack,
|
|
5
|
+
zkProve,
|
|
6
|
+
zkProveWithWorker,
|
|
7
|
+
zkVerify,
|
|
8
|
+
constructZkPoKMetadata,
|
|
9
|
+
} from './zkPackProveVerify.js';
|
|
10
|
+
import { CofhesdkError, CofhesdkErrorCode } from '../error.js';
|
|
11
|
+
import {
|
|
12
|
+
type EncryptStepCallbackFunction,
|
|
13
|
+
EncryptStep,
|
|
14
|
+
type EncryptableItem,
|
|
15
|
+
type EncryptedItemInput,
|
|
16
|
+
type EncryptedItemInputs,
|
|
17
|
+
type TfheInitializer,
|
|
18
|
+
type EncryptStepCallbackContext,
|
|
19
|
+
} from '../types.js';
|
|
20
|
+
import { cofheMocksCheckEncryptableBits, cofheMocksZkVerifySign } from './cofheMocksZkVerifySign.js';
|
|
21
|
+
import { hardhat } from 'viem/chains';
|
|
22
|
+
import { fetchKeys, type FheKeyDeserializer } from '../fetchKeys.js';
|
|
23
|
+
import { getZkVerifierUrlOrThrow } from '../config.js';
|
|
24
|
+
import { type WalletClient } from 'viem';
|
|
25
|
+
import { sleep } from '../utils.js';
|
|
26
|
+
import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder.js';
|
|
27
|
+
import { type KeysStorage } from '../keyStore.js';
|
|
28
|
+
|
|
29
|
+
type EncryptInputsBuilderParams<T extends EncryptableItem[]> = BaseBuilderParams & {
|
|
30
|
+
inputs: [...T];
|
|
31
|
+
securityZone?: number;
|
|
32
|
+
|
|
33
|
+
zkvWalletClient?: WalletClient | undefined;
|
|
34
|
+
|
|
35
|
+
tfhePublicKeyDeserializer: FheKeyDeserializer | undefined;
|
|
36
|
+
compactPkeCrsDeserializer: FheKeyDeserializer | undefined;
|
|
37
|
+
zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator | undefined;
|
|
38
|
+
initTfhe: TfheInitializer | undefined;
|
|
39
|
+
zkProveWorkerFn: ZkProveWorkerFunction | undefined;
|
|
40
|
+
|
|
41
|
+
keysStorage: KeysStorage | undefined;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* EncryptInputsBuilder exposes a builder pattern for encrypting inputs.
|
|
46
|
+
* account, securityZone, and chainId can be overridden in the builder.
|
|
47
|
+
* config, tfhePublicKeyDeserializer, compactPkeCrsDeserializer, and zkBuilderAndCrsGenerator are required to be set in the builder.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuilder {
|
|
51
|
+
private securityZone: number;
|
|
52
|
+
private stepCallback?: EncryptStepCallbackFunction;
|
|
53
|
+
private inputItems: [...T];
|
|
54
|
+
|
|
55
|
+
private zkvWalletClient: WalletClient | undefined;
|
|
56
|
+
|
|
57
|
+
private tfhePublicKeyDeserializer: FheKeyDeserializer;
|
|
58
|
+
private compactPkeCrsDeserializer: FheKeyDeserializer;
|
|
59
|
+
private zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator;
|
|
60
|
+
private initTfhe: TfheInitializer | undefined;
|
|
61
|
+
private zkProveWorkerFn: ZkProveWorkerFunction | undefined;
|
|
62
|
+
|
|
63
|
+
private keysStorage: KeysStorage | undefined;
|
|
64
|
+
|
|
65
|
+
// Worker configuration (from config, overrideable)
|
|
66
|
+
private useWorker: boolean;
|
|
67
|
+
|
|
68
|
+
private stepTimestamps: Record<EncryptStep, number> = {
|
|
69
|
+
[EncryptStep.InitTfhe]: 0,
|
|
70
|
+
[EncryptStep.FetchKeys]: 0,
|
|
71
|
+
[EncryptStep.Pack]: 0,
|
|
72
|
+
[EncryptStep.Prove]: 0,
|
|
73
|
+
[EncryptStep.Verify]: 0,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
constructor(params: EncryptInputsBuilderParams<T>) {
|
|
77
|
+
super({
|
|
78
|
+
config: params.config,
|
|
79
|
+
publicClient: params.publicClient,
|
|
80
|
+
walletClient: params.walletClient,
|
|
81
|
+
chainId: params.chainId,
|
|
82
|
+
account: params.account,
|
|
83
|
+
requireConnected: params.requireConnected,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
this.inputItems = params.inputs;
|
|
87
|
+
this.securityZone = params.securityZone ?? 0;
|
|
88
|
+
|
|
89
|
+
this.zkvWalletClient = params.zkvWalletClient;
|
|
90
|
+
|
|
91
|
+
// Check that tfhePublicKeyDeserializer is provided
|
|
92
|
+
if (!params.tfhePublicKeyDeserializer) {
|
|
93
|
+
throw new CofhesdkError({
|
|
94
|
+
code: CofhesdkErrorCode.MissingTfhePublicKeyDeserializer,
|
|
95
|
+
message: 'EncryptInputsBuilder tfhePublicKeyDeserializer is undefined',
|
|
96
|
+
hint: 'Ensure client has been created with a tfhePublicKeyDeserializer.',
|
|
97
|
+
context: {
|
|
98
|
+
tfhePublicKeyDeserializer: params.tfhePublicKeyDeserializer,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
this.tfhePublicKeyDeserializer = params.tfhePublicKeyDeserializer;
|
|
103
|
+
|
|
104
|
+
// Check that compactPkeCrsDeserializer is provided
|
|
105
|
+
if (!params.compactPkeCrsDeserializer) {
|
|
106
|
+
throw new CofhesdkError({
|
|
107
|
+
code: CofhesdkErrorCode.MissingCompactPkeCrsDeserializer,
|
|
108
|
+
message: 'EncryptInputsBuilder compactPkeCrsDeserializer is undefined',
|
|
109
|
+
hint: 'Ensure client has been created with a compactPkeCrsDeserializer.',
|
|
110
|
+
context: {
|
|
111
|
+
compactPkeCrsDeserializer: params.compactPkeCrsDeserializer,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
this.compactPkeCrsDeserializer = params.compactPkeCrsDeserializer;
|
|
116
|
+
|
|
117
|
+
// Check that zkBuilderAndCrsGenerator is provided
|
|
118
|
+
if (!params.zkBuilderAndCrsGenerator) {
|
|
119
|
+
throw new CofhesdkError({
|
|
120
|
+
code: CofhesdkErrorCode.MissingZkBuilderAndCrsGenerator,
|
|
121
|
+
message: 'EncryptInputsBuilder zkBuilderAndCrsGenerator is undefined',
|
|
122
|
+
hint: 'Ensure client has been created with a zkBuilderAndCrsGenerator.',
|
|
123
|
+
context: {
|
|
124
|
+
zkBuilderAndCrsGenerator: params.zkBuilderAndCrsGenerator,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
this.zkBuilderAndCrsGenerator = params.zkBuilderAndCrsGenerator;
|
|
129
|
+
|
|
130
|
+
// Optional tfhe initialization function, will be run if provided
|
|
131
|
+
this.initTfhe = params.initTfhe;
|
|
132
|
+
|
|
133
|
+
// Optional zkProve worker function, will be used on web if useWorkers is true and worker function is provided
|
|
134
|
+
this.zkProveWorkerFn = params.zkProveWorkerFn;
|
|
135
|
+
|
|
136
|
+
// Keys storage is used to store the FHE key and CRS
|
|
137
|
+
this.keysStorage = params.keysStorage;
|
|
138
|
+
|
|
139
|
+
// Initialize useWorker from config (can be overridden via setUseWorker) - default to true
|
|
140
|
+
this.useWorker = params.config?.useWorkers ?? true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param account - Account that will create the tx using the encrypted inputs.
|
|
145
|
+
*
|
|
146
|
+
* If not provided, the account will be fetched from the connected walletClient.
|
|
147
|
+
*
|
|
148
|
+
* Example:
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
151
|
+
* .setAccount("0x123")
|
|
152
|
+
* .encrypt();
|
|
153
|
+
* ```
|
|
154
|
+
*
|
|
155
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
156
|
+
*/
|
|
157
|
+
setAccount(account: string): EncryptInputsBuilder<T> {
|
|
158
|
+
this.account = account;
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getAccount(): string | undefined {
|
|
163
|
+
return this.account;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param chainId - Chain that will consume the encrypted inputs.
|
|
168
|
+
*
|
|
169
|
+
* If not provided, the chainId will be fetched from the connected publicClient.
|
|
170
|
+
*
|
|
171
|
+
* Example:
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
174
|
+
* .setChainId(11155111)
|
|
175
|
+
* .encrypt();
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
179
|
+
*/
|
|
180
|
+
setChainId(chainId: number): EncryptInputsBuilder<T> {
|
|
181
|
+
this.chainId = chainId;
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
getChainId(): number | undefined {
|
|
186
|
+
return this.chainId;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @param securityZone - Security zone to encrypt the inputs for.
|
|
191
|
+
*
|
|
192
|
+
* If not provided, the default securityZone 0 will be used.
|
|
193
|
+
*
|
|
194
|
+
* Example:
|
|
195
|
+
* ```typescript
|
|
196
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
197
|
+
* .setSecurityZone(1)
|
|
198
|
+
* .encrypt();
|
|
199
|
+
* ```
|
|
200
|
+
*
|
|
201
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
202
|
+
*/
|
|
203
|
+
setSecurityZone(securityZone: number): EncryptInputsBuilder<T> {
|
|
204
|
+
this.securityZone = securityZone;
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
getSecurityZone(): number {
|
|
209
|
+
return this.securityZone;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param useWorker - Whether to use Web Workers for ZK proof generation.
|
|
214
|
+
*
|
|
215
|
+
* Overrides the config-level useWorkers setting for this specific encryption.
|
|
216
|
+
*
|
|
217
|
+
* Example:
|
|
218
|
+
* ```typescript
|
|
219
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
220
|
+
* .setUseWorker(false)
|
|
221
|
+
* .encrypt();
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @returns The chainable EncryptInputsBuilder instance.
|
|
225
|
+
*/
|
|
226
|
+
setUseWorker(useWorker: boolean): EncryptInputsBuilder<T> {
|
|
227
|
+
this.useWorker = useWorker;
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Gets the current worker configuration.
|
|
233
|
+
*
|
|
234
|
+
* @returns Whether Web Workers are enabled for this encryption.
|
|
235
|
+
*
|
|
236
|
+
* Example:
|
|
237
|
+
* ```typescript
|
|
238
|
+
* const builder = encryptInputs([Encryptable.uint128(10n)]);
|
|
239
|
+
* console.log(builder.getUseWorker()); // true (from config)
|
|
240
|
+
* builder.setUseWorker(false);
|
|
241
|
+
* console.log(builder.getUseWorker()); // false (overridden)
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
getUseWorker(): boolean {
|
|
245
|
+
return this.useWorker;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @param callback - Function to be called with the encryption step.
|
|
250
|
+
*
|
|
251
|
+
* Useful for debugging and tracking the progress of the encryption process.
|
|
252
|
+
* Useful for a UI element that shows the progress of the encryption process.
|
|
253
|
+
*
|
|
254
|
+
* Example:
|
|
255
|
+
* ```typescript
|
|
256
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
257
|
+
* .setStepCallback((step: EncryptStep) => console.log(step))
|
|
258
|
+
* .encrypt();
|
|
259
|
+
* ```
|
|
260
|
+
*
|
|
261
|
+
* @returns The EncryptInputsBuilder instance.
|
|
262
|
+
*/
|
|
263
|
+
setStepCallback(callback: EncryptStepCallbackFunction): EncryptInputsBuilder<T> {
|
|
264
|
+
this.stepCallback = callback;
|
|
265
|
+
return this;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
getStepCallback(): EncryptStepCallbackFunction | undefined {
|
|
269
|
+
return this.stepCallback;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Fires the step callback if set
|
|
274
|
+
*/
|
|
275
|
+
private fireStepStart(
|
|
276
|
+
step: EncryptStep,
|
|
277
|
+
context: Omit<EncryptStepCallbackContext, 'isStart' | 'isEnd' | 'duration'> = {}
|
|
278
|
+
) {
|
|
279
|
+
if (!this.stepCallback) return;
|
|
280
|
+
this.stepTimestamps[step] = Date.now();
|
|
281
|
+
this.stepCallback(step, { ...context, isStart: true, isEnd: false, duration: 0 });
|
|
282
|
+
}
|
|
283
|
+
private fireStepEnd(
|
|
284
|
+
step: EncryptStep,
|
|
285
|
+
context: Omit<EncryptStepCallbackContext, 'isStart' | 'isEnd' | 'duration'> = {}
|
|
286
|
+
) {
|
|
287
|
+
if (!this.stepCallback) return;
|
|
288
|
+
const duration = Date.now() - this.stepTimestamps[step];
|
|
289
|
+
this.stepCallback(step, { ...context, isStart: false, isEnd: true, duration });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* zkVerifierUrl is included in the chains exported from cofhesdk/chains for use in CofhesdkConfig.supportedChains
|
|
294
|
+
* Users should generally not set this manually.
|
|
295
|
+
*/
|
|
296
|
+
private async getZkVerifierUrl(): Promise<string> {
|
|
297
|
+
this.assertChainId();
|
|
298
|
+
return getZkVerifierUrlOrThrow(this.config, this.chainId);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* initTfhe is a platform-specific dependency injected into core/createCofhesdkClientBase by web/createCofhesdkClient and node/createCofhesdkClient
|
|
303
|
+
* web/ uses zama "tfhe"
|
|
304
|
+
* node/ uses zama "node-tfhe"
|
|
305
|
+
* Users should not set this manually.
|
|
306
|
+
*/
|
|
307
|
+
private async initTfheOrThrow(): Promise<boolean> {
|
|
308
|
+
if (!this.initTfhe) return false;
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
return await this.initTfhe();
|
|
312
|
+
} catch (error) {
|
|
313
|
+
throw CofhesdkError.fromError(error, {
|
|
314
|
+
code: CofhesdkErrorCode.InitTfheFailed,
|
|
315
|
+
message: `Failed to initialize TFHE`,
|
|
316
|
+
context: {
|
|
317
|
+
initTfhe: this.initTfhe,
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Fetches the FHE key and CRS from the CoFHE API
|
|
325
|
+
* If the key/crs already exists in the store it is returned, else it is fetched, stored, and returned
|
|
326
|
+
*/
|
|
327
|
+
private async fetchFheKeyAndCrs(): Promise<{
|
|
328
|
+
fheKey: string;
|
|
329
|
+
fheKeyFetchedFromCoFHE: boolean;
|
|
330
|
+
crs: string;
|
|
331
|
+
crsFetchedFromCoFHE: boolean;
|
|
332
|
+
}> {
|
|
333
|
+
this.assertChainId();
|
|
334
|
+
const securityZone = this.getSecurityZone();
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
await this.keysStorage?.rehydrateKeysStore();
|
|
338
|
+
} catch (error) {
|
|
339
|
+
throw CofhesdkError.fromError(error, {
|
|
340
|
+
code: CofhesdkErrorCode.RehydrateKeysStoreFailed,
|
|
341
|
+
message: `Failed to rehydrate keys store`,
|
|
342
|
+
context: {
|
|
343
|
+
keysStorage: this.keysStorage,
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let fheKey: string | undefined;
|
|
349
|
+
let fheKeyFetchedFromCoFHE: boolean = false;
|
|
350
|
+
let crs: string | undefined;
|
|
351
|
+
let crsFetchedFromCoFHE: boolean = false;
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
[[fheKey, fheKeyFetchedFromCoFHE], [crs, crsFetchedFromCoFHE]] = await fetchKeys(
|
|
355
|
+
this.config,
|
|
356
|
+
this.chainId,
|
|
357
|
+
securityZone,
|
|
358
|
+
this.tfhePublicKeyDeserializer,
|
|
359
|
+
this.compactPkeCrsDeserializer,
|
|
360
|
+
this.keysStorage
|
|
361
|
+
);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw CofhesdkError.fromError(error, {
|
|
364
|
+
code: CofhesdkErrorCode.FetchKeysFailed,
|
|
365
|
+
message: `Failed to fetch FHE key and CRS`,
|
|
366
|
+
context: {
|
|
367
|
+
config: this.config,
|
|
368
|
+
chainId: this.chainId,
|
|
369
|
+
securityZone,
|
|
370
|
+
compactPkeCrsDeserializer: this.compactPkeCrsDeserializer,
|
|
371
|
+
tfhePublicKeyDeserializer: this.tfhePublicKeyDeserializer,
|
|
372
|
+
},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!fheKey) {
|
|
377
|
+
throw new CofhesdkError({
|
|
378
|
+
code: CofhesdkErrorCode.MissingFheKey,
|
|
379
|
+
message: `FHE key not found`,
|
|
380
|
+
context: {
|
|
381
|
+
chainId: this.chainId,
|
|
382
|
+
securityZone,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (!crs) {
|
|
388
|
+
throw new CofhesdkError({
|
|
389
|
+
code: CofhesdkErrorCode.MissingCrs,
|
|
390
|
+
message: `CRS not found for chainId <${this.chainId}>`,
|
|
391
|
+
context: {
|
|
392
|
+
chainId: this.chainId,
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* @dev Encrypt against the cofheMocks instead of CoFHE
|
|
402
|
+
*
|
|
403
|
+
* In the cofheMocks, the MockZkVerifier contract is deployed on hardhat to a fixed address, this contract handles mocking the zk verifying.
|
|
404
|
+
* cofheMocksInsertPackedHashes - stores the ctHashes and their plaintext values for on-chain mocking of FHE operations.
|
|
405
|
+
* cofheMocksZkCreateProofSignatures - creates signatures to be included in the encrypted inputs. The signers address is known and verified in the mock contracts.
|
|
406
|
+
*/
|
|
407
|
+
private async mocksEncrypt(): Promise<[...EncryptedItemInputs<T>]> {
|
|
408
|
+
this.assertAccount();
|
|
409
|
+
this.assertPublicClient();
|
|
410
|
+
this.assertWalletClient();
|
|
411
|
+
|
|
412
|
+
this.fireStepStart(EncryptStep.InitTfhe);
|
|
413
|
+
await sleep(100);
|
|
414
|
+
this.fireStepEnd(EncryptStep.InitTfhe, { tfheInitializationExecuted: false });
|
|
415
|
+
|
|
416
|
+
this.fireStepStart(EncryptStep.FetchKeys);
|
|
417
|
+
await sleep(100);
|
|
418
|
+
this.fireStepEnd(EncryptStep.FetchKeys, { fheKeyFetchedFromCoFHE: false, crsFetchedFromCoFHE: false });
|
|
419
|
+
|
|
420
|
+
this.fireStepStart(EncryptStep.Pack);
|
|
421
|
+
await cofheMocksCheckEncryptableBits(this.inputItems);
|
|
422
|
+
await sleep(100);
|
|
423
|
+
this.fireStepEnd(EncryptStep.Pack);
|
|
424
|
+
|
|
425
|
+
this.fireStepStart(EncryptStep.Prove);
|
|
426
|
+
await sleep(500);
|
|
427
|
+
this.fireStepEnd(EncryptStep.Prove);
|
|
428
|
+
|
|
429
|
+
this.fireStepStart(EncryptStep.Verify);
|
|
430
|
+
await sleep(500);
|
|
431
|
+
const signedResults = await cofheMocksZkVerifySign(
|
|
432
|
+
this.inputItems,
|
|
433
|
+
this.account,
|
|
434
|
+
this.securityZone,
|
|
435
|
+
this.publicClient,
|
|
436
|
+
this.walletClient,
|
|
437
|
+
this.zkvWalletClient
|
|
438
|
+
);
|
|
439
|
+
const encryptedInputs: EncryptedItemInput[] = signedResults.map(({ ct_hash, signature }, index) => ({
|
|
440
|
+
ctHash: BigInt(ct_hash),
|
|
441
|
+
securityZone: this.securityZone,
|
|
442
|
+
utype: this.inputItems[index].utype,
|
|
443
|
+
signature,
|
|
444
|
+
}));
|
|
445
|
+
this.fireStepEnd(EncryptStep.Verify);
|
|
446
|
+
|
|
447
|
+
return encryptedInputs as [...EncryptedItemInputs<T>];
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* In the production context, perform a true encryption with the CoFHE coprocessor.
|
|
452
|
+
*/
|
|
453
|
+
private async productionEncrypt(): Promise<[...EncryptedItemInputs<T>]> {
|
|
454
|
+
this.assertAccount();
|
|
455
|
+
this.assertChainId();
|
|
456
|
+
|
|
457
|
+
this.fireStepStart(EncryptStep.InitTfhe);
|
|
458
|
+
|
|
459
|
+
// Deferred initialization of tfhe wasm until encrypt is called
|
|
460
|
+
// Returns true if tfhe was initialized, false if already initialized
|
|
461
|
+
const tfheInitializationExecuted = await this.initTfheOrThrow();
|
|
462
|
+
|
|
463
|
+
this.fireStepEnd(EncryptStep.InitTfhe, { tfheInitializationExecuted });
|
|
464
|
+
|
|
465
|
+
this.fireStepStart(EncryptStep.FetchKeys);
|
|
466
|
+
|
|
467
|
+
// Deferred fetching of fheKey and crs until encrypt is called
|
|
468
|
+
// if the key/crs is already in the store, it is not fetched from the CoFHE API
|
|
469
|
+
const { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE } = await this.fetchFheKeyAndCrs();
|
|
470
|
+
|
|
471
|
+
let { zkBuilder, zkCrs } = this.zkBuilderAndCrsGenerator(fheKey, crs);
|
|
472
|
+
|
|
473
|
+
this.fireStepEnd(EncryptStep.FetchKeys, { fheKeyFetchedFromCoFHE, crsFetchedFromCoFHE });
|
|
474
|
+
|
|
475
|
+
this.fireStepStart(EncryptStep.Pack);
|
|
476
|
+
|
|
477
|
+
zkBuilder = zkPack(this.inputItems, zkBuilder);
|
|
478
|
+
|
|
479
|
+
this.fireStepEnd(EncryptStep.Pack);
|
|
480
|
+
|
|
481
|
+
this.fireStepStart(EncryptStep.Prove);
|
|
482
|
+
|
|
483
|
+
// Construct metadata once (used by both worker and main thread paths)
|
|
484
|
+
const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
|
|
485
|
+
|
|
486
|
+
let proof: Uint8Array | null = null;
|
|
487
|
+
let usedWorker = false;
|
|
488
|
+
let workerFailedError: string | undefined;
|
|
489
|
+
|
|
490
|
+
// Decision logic: try worker if enabled and available, fallback to main thread
|
|
491
|
+
if (this.useWorker && this.zkProveWorkerFn) {
|
|
492
|
+
try {
|
|
493
|
+
// Call worker function directly (no packing needed, worker does it)
|
|
494
|
+
proof = await zkProveWithWorker(this.zkProveWorkerFn, fheKey, crs, this.inputItems, metadata);
|
|
495
|
+
usedWorker = true;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
// Worker failed - capture error for debugging
|
|
498
|
+
workerFailedError = error instanceof Error ? error.message : String(error);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (proof == null) {
|
|
503
|
+
// Use main thread directly (workers disabled or unavailable)
|
|
504
|
+
proof = await zkProve(zkBuilder, zkCrs, metadata);
|
|
505
|
+
usedWorker = false;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
this.fireStepEnd(EncryptStep.Prove, {
|
|
509
|
+
useWorker: this.useWorker,
|
|
510
|
+
usedWorker,
|
|
511
|
+
workerFailedError,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
this.fireStepStart(EncryptStep.Verify);
|
|
515
|
+
|
|
516
|
+
const zkVerifierUrl = await this.getZkVerifierUrl();
|
|
517
|
+
|
|
518
|
+
const verifyResults = await zkVerify(zkVerifierUrl, proof, this.account, this.securityZone, this.chainId);
|
|
519
|
+
// Add securityZone and utype to the verify results
|
|
520
|
+
const encryptedInputs: EncryptedItemInput[] = verifyResults.map(
|
|
521
|
+
({ ct_hash, signature }: { ct_hash: string; signature: string }, index: number) => ({
|
|
522
|
+
ctHash: BigInt(ct_hash),
|
|
523
|
+
securityZone: this.securityZone,
|
|
524
|
+
utype: this.inputItems[index].utype,
|
|
525
|
+
signature,
|
|
526
|
+
})
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
this.fireStepEnd(EncryptStep.Verify);
|
|
530
|
+
|
|
531
|
+
return encryptedInputs as [...EncryptedItemInputs<T>];
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
|
|
536
|
+
*
|
|
537
|
+
* This will:
|
|
538
|
+
* - Pack the encryptable items into a zk proof
|
|
539
|
+
* - Prove the zk proof
|
|
540
|
+
* - Verify the zk proof with CoFHE
|
|
541
|
+
* - Package and return the encrypted inputs
|
|
542
|
+
*
|
|
543
|
+
* Example:
|
|
544
|
+
* ```typescript
|
|
545
|
+
* const encrypted = await encryptInputs([Encryptable.uint128(10n)])
|
|
546
|
+
* .setAccount('0x123...890') // optional
|
|
547
|
+
* .setChainId(11155111) // optional
|
|
548
|
+
* .encrypt(); // execute
|
|
549
|
+
* ```
|
|
550
|
+
*
|
|
551
|
+
* @returns The encrypted inputs.
|
|
552
|
+
*/
|
|
553
|
+
async encrypt(): Promise<[...EncryptedItemInputs<T>]> {
|
|
554
|
+
// On hardhat chain, interact with MockZkVerifier contract instead of CoFHE
|
|
555
|
+
if (this.chainId === hardhat.id) return this.mocksEncrypt();
|
|
556
|
+
|
|
557
|
+
// On other chains, interact with CoFHE coprocessor
|
|
558
|
+
return this.productionEncrypt();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* eslint-disable no-redeclare */
|
|
2
|
+
import {
|
|
3
|
+
type EncryptableItem,
|
|
4
|
+
isEncryptableItem,
|
|
5
|
+
type EncryptedItemInput,
|
|
6
|
+
type EncryptedItemInputs,
|
|
7
|
+
} from '../types.js';
|
|
8
|
+
|
|
9
|
+
export function encryptExtract<T>(item: T): EncryptableItem[];
|
|
10
|
+
export function encryptExtract<T extends any[]>(item: [...T]): EncryptableItem[];
|
|
11
|
+
export function encryptExtract<T>(item: T) {
|
|
12
|
+
if (isEncryptableItem(item)) {
|
|
13
|
+
return item;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Object | Array
|
|
17
|
+
if (typeof item === 'object' && item !== null) {
|
|
18
|
+
if (Array.isArray(item)) {
|
|
19
|
+
// Array - recurse
|
|
20
|
+
return item.flatMap((nestedItem) => encryptExtract(nestedItem));
|
|
21
|
+
} else {
|
|
22
|
+
// Object - recurse
|
|
23
|
+
return Object.values(item).flatMap((value) => encryptExtract(value));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function encryptReplace<T>(
|
|
31
|
+
item: T,
|
|
32
|
+
encryptedItems: EncryptedItemInput[]
|
|
33
|
+
): [EncryptedItemInputs<T>, EncryptedItemInput[]];
|
|
34
|
+
export function encryptReplace<T extends any[]>(
|
|
35
|
+
item: [...T],
|
|
36
|
+
encryptedItems: EncryptedItemInput[]
|
|
37
|
+
): [...EncryptedItemInputs<T>, EncryptedItemInput[]];
|
|
38
|
+
export function encryptReplace<T>(item: T, encryptedItems: EncryptedItemInput[]) {
|
|
39
|
+
if (isEncryptableItem(item)) {
|
|
40
|
+
return [encryptedItems[0], encryptedItems.slice(1)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Object | Array
|
|
44
|
+
if (typeof item === 'object' && item !== null) {
|
|
45
|
+
if (Array.isArray(item)) {
|
|
46
|
+
// Array - recurse
|
|
47
|
+
return item.reduce<[any[], EncryptedItemInput[]]>(
|
|
48
|
+
([acc, remaining], item) => {
|
|
49
|
+
const [newItem, newRemaining] = encryptReplace(item, remaining);
|
|
50
|
+
return [[...acc, newItem], newRemaining];
|
|
51
|
+
},
|
|
52
|
+
[[], encryptedItems]
|
|
53
|
+
);
|
|
54
|
+
} else {
|
|
55
|
+
// Object - recurse
|
|
56
|
+
return Object.entries(item).reduce<[Record<string, any>, EncryptedItemInput[]]>(
|
|
57
|
+
([acc, remaining], [key, value]) => {
|
|
58
|
+
const [newValue, newRemaining] = encryptReplace(value, remaining);
|
|
59
|
+
return [{ ...acc, [key]: newValue }, newRemaining];
|
|
60
|
+
},
|
|
61
|
+
[{}, encryptedItems]
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return [item, encryptedItems];
|
|
67
|
+
}
|