@aztec/pxe 0.37.0 → 0.38.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.
Files changed (51) hide show
  1. package/dest/config/index.d.ts +17 -2
  2. package/dest/config/index.d.ts.map +1 -1
  3. package/dest/config/index.js +1 -1
  4. package/dest/index.d.ts +1 -0
  5. package/dest/index.d.ts.map +1 -1
  6. package/dest/index.js +3 -1
  7. package/dest/kernel_oracle/index.d.ts +1 -1
  8. package/dest/kernel_oracle/index.d.ts.map +1 -1
  9. package/dest/kernel_oracle/index.js +2 -2
  10. package/dest/kernel_prover/bb_prover/bb_native_proof_creator.d.ts +95 -0
  11. package/dest/kernel_prover/bb_prover/bb_native_proof_creator.d.ts.map +1 -0
  12. package/dest/kernel_prover/bb_prover/bb_native_proof_creator.js +437 -0
  13. package/dest/kernel_prover/{proof_creator.d.ts → interface/proof_creator.d.ts} +16 -35
  14. package/dest/kernel_prover/interface/proof_creator.d.ts.map +1 -0
  15. package/dest/kernel_prover/interface/proof_creator.js +2 -0
  16. package/dest/kernel_prover/kernel_prover.d.ts +4 -4
  17. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  18. package/dest/kernel_prover/kernel_prover.js +10 -37
  19. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.d.ts +1 -1
  20. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.d.ts.map +1 -1
  21. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.js +4 -33
  22. package/dest/kernel_prover/proving_data_oracle.d.ts +1 -1
  23. package/dest/kernel_prover/proving_data_oracle.d.ts.map +1 -1
  24. package/dest/kernel_prover/test/test_circuit_prover.d.ts +16 -0
  25. package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -0
  26. package/dest/kernel_prover/test/test_circuit_prover.js +67 -0
  27. package/dest/pxe_service/create_pxe_service.d.ts +3 -1
  28. package/dest/pxe_service/create_pxe_service.d.ts.map +1 -1
  29. package/dest/pxe_service/create_pxe_service.js +16 -3
  30. package/dest/pxe_service/pxe_service.d.ts +3 -2
  31. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  32. package/dest/pxe_service/pxe_service.js +11 -56
  33. package/dest/simulator_oracle/index.d.ts +3 -3
  34. package/dest/simulator_oracle/index.d.ts.map +1 -1
  35. package/dest/simulator_oracle/index.js +3 -3
  36. package/package.json +14 -12
  37. package/src/config/index.ts +19 -2
  38. package/src/index.ts +3 -0
  39. package/src/kernel_oracle/index.ts +1 -1
  40. package/src/kernel_prover/bb_prover/bb_native_proof_creator.ts +713 -0
  41. package/src/kernel_prover/interface/proof_creator.ts +79 -0
  42. package/src/kernel_prover/kernel_prover.ts +19 -48
  43. package/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts +5 -40
  44. package/src/kernel_prover/proving_data_oracle.ts +1 -1
  45. package/src/kernel_prover/test/test_circuit_prover.ts +96 -0
  46. package/src/pxe_service/create_pxe_service.ts +17 -1
  47. package/src/pxe_service/pxe_service.ts +17 -66
  48. package/src/simulator_oracle/index.ts +4 -7
  49. package/dest/kernel_prover/proof_creator.d.ts.map +0 -1
  50. package/dest/kernel_prover/proof_creator.js +0 -68
  51. package/src/kernel_prover/proof_creator.ts +0 -157
@@ -0,0 +1,713 @@
1
+ import {
2
+ Fr,
3
+ type PrivateCircuitPublicInputs,
4
+ type PrivateKernelCircuitPublicInputs,
5
+ type PrivateKernelInitCircuitPrivateInputs,
6
+ type PrivateKernelInnerCircuitPrivateInputs,
7
+ type PrivateKernelTailCircuitPrivateInputs,
8
+ type PrivateKernelTailCircuitPublicInputs,
9
+ Proof,
10
+ type VERIFICATION_KEY_LENGTH_IN_FIELDS,
11
+ makeEmptyProof,
12
+ } from '@aztec/circuits.js';
13
+ import { siloNoteHash } from '@aztec/circuits.js/hash';
14
+ import { randomBytes, sha256 } from '@aztec/foundation/crypto';
15
+ import { type LogFn, createDebugLogger } from '@aztec/foundation/log';
16
+ import { type Tuple } from '@aztec/foundation/serialize';
17
+ import { Timer } from '@aztec/foundation/timer';
18
+ import {
19
+ ClientCircuitArtifacts,
20
+ type ClientProtocolArtifact,
21
+ convertPrivateKernelInitInputsToWitnessMap,
22
+ convertPrivateKernelInitOutputsFromWitnessMap,
23
+ convertPrivateKernelInnerInputsToWitnessMap,
24
+ convertPrivateKernelInnerOutputsFromWitnessMap,
25
+ convertPrivateKernelTailForPublicOutputsFromWitnessMap,
26
+ convertPrivateKernelTailOutputsFromWitnessMap,
27
+ executeTail,
28
+ executeTailForPublic,
29
+ } from '@aztec/noir-protocol-circuits-types';
30
+ import { type ACVMField, WASMSimulator } from '@aztec/simulator';
31
+ import { type NoirCompiledCircuit } from '@aztec/types/noir';
32
+
33
+ import { type WitnessMap } from '@noir-lang/acvm_js';
34
+ import { serializeWitness } from '@noir-lang/noirc_abi';
35
+ import * as proc from 'child_process';
36
+ import * as fs from 'fs/promises';
37
+
38
+ import { type ProofCreator, type ProofOutput } from '../interface/proof_creator.js';
39
+
40
+ /**
41
+ * Temporary implementation of ProofCreator using the native bb binary.
42
+ * Will be replaced by the WASM equivalent once ready
43
+ */
44
+
45
+ const VK_FILENAME = 'vk';
46
+ const VK_FIELDS_FILENAME = 'vk_fields.json';
47
+ const PROOF_FILENAME = 'proof';
48
+ //const PROOF_FIELDS_FILENAME = 'proof_fields.json';
49
+
50
+ //const AGGREGATION_OBJECT_SIZE = 16;
51
+ const CIRCUIT_SIZE_INDEX = 3;
52
+ const CIRCUIT_PUBLIC_INPUTS_INDEX = 4;
53
+ const CIRCUIT_RECURSIVE_INDEX = 5;
54
+
55
+ enum BB_RESULT {
56
+ SUCCESS,
57
+ FAILURE,
58
+ ALREADY_PRESENT,
59
+ }
60
+
61
+ type BBSuccess = {
62
+ status: BB_RESULT.SUCCESS | BB_RESULT.ALREADY_PRESENT;
63
+ duration: number;
64
+ pkPath?: string;
65
+ vkPath?: string;
66
+ proofPath?: string;
67
+ };
68
+
69
+ type BBFailure = {
70
+ status: BB_RESULT.FAILURE;
71
+ reason: string;
72
+ };
73
+
74
+ /**
75
+ * Invokes the Barretenberg binary with the provided command and args
76
+ * @param pathToBB - The path to the BB binary
77
+ * @param command - The command to execute
78
+ * @param args - The arguments to pass
79
+ * @param logger - A log function
80
+ * @param resultParser - An optional handler for detecting success or failure
81
+ * @returns The completed partial witness outputted from the circuit
82
+ */
83
+ function executeBB(
84
+ pathToBB: string,
85
+ command: string,
86
+ args: string[],
87
+ logger: LogFn,
88
+ resultParser = (code: number) => code === 0,
89
+ ) {
90
+ return new Promise<BB_RESULT.SUCCESS | BB_RESULT.FAILURE>((resolve, reject) => {
91
+ // spawn the bb process
92
+ const bb = proc.spawn(pathToBB, [command, ...args]);
93
+ bb.stdout.on('data', data => {
94
+ const message = data.toString('utf-8').replace(/\n$/, '');
95
+ logger(message);
96
+ });
97
+ bb.stderr.on('data', data => {
98
+ const message = data.toString('utf-8').replace(/\n$/, '');
99
+ logger(message);
100
+ });
101
+ bb.on('close', (code: number) => {
102
+ if (resultParser(code)) {
103
+ resolve(BB_RESULT.SUCCESS);
104
+ } else {
105
+ reject();
106
+ }
107
+ });
108
+ }).catch(_ => BB_RESULT.FAILURE);
109
+ }
110
+
111
+ /**
112
+ * Used for generating proofs of noir circuits.
113
+ * It is assumed that the working directory is a temporary and/or random directory used solely for generating this proof.
114
+ * @param pathToBB - The full path to the bb binary
115
+ * @param workingDirectory - A working directory for use by bb
116
+ * @param circuitName - An identifier for the circuit
117
+ * @param bytecode - The compiled circuit bytecode
118
+ * @param inputWitnessFile - The circuit input witness
119
+ * @param log - A logging function
120
+ * @returns An object containing a result indication, the location of the proof and the duration taken
121
+ */
122
+ export async function generateProof(
123
+ pathToBB: string,
124
+ workingDirectory: string,
125
+ circuitName: string,
126
+ bytecode: Buffer,
127
+ inputWitnessFile: string,
128
+ log: LogFn,
129
+ ): Promise<BBFailure | BBSuccess> {
130
+ // Check that the working directory exists
131
+ try {
132
+ await fs.access(workingDirectory);
133
+ } catch (error) {
134
+ return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
135
+ }
136
+
137
+ // The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
138
+ const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
139
+
140
+ // The proof is written to e.g. /workingDirectory/proof
141
+ const outputPath = `${workingDirectory}`;
142
+
143
+ const binaryPresent = await fs
144
+ .access(pathToBB, fs.constants.R_OK)
145
+ .then(_ => true)
146
+ .catch(_ => false);
147
+ if (!binaryPresent) {
148
+ return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
149
+ }
150
+
151
+ try {
152
+ // Write the bytecode to the working directory
153
+ await fs.writeFile(bytecodePath, bytecode);
154
+ const args = ['-o', outputPath, '-b', bytecodePath, '-w', inputWitnessFile, '-v'];
155
+ const timer = new Timer();
156
+ const logFunction = (message: string) => {
157
+ log(`${circuitName} BB out - ${message}`);
158
+ };
159
+ const result = await executeBB(pathToBB, 'prove_output_all', args, logFunction);
160
+ const duration = timer.ms();
161
+ // cleanup the bytecode
162
+ await fs.rm(bytecodePath, { force: true });
163
+ if (result == BB_RESULT.SUCCESS) {
164
+ return {
165
+ status: BB_RESULT.SUCCESS,
166
+ duration,
167
+ proofPath: `${outputPath}`,
168
+ pkPath: undefined,
169
+ vkPath: `${outputPath}`,
170
+ };
171
+ }
172
+ // Not a great error message here but it is difficult to decipher what comes from bb
173
+ return { status: BB_RESULT.FAILURE, reason: `Failed to generate proof` };
174
+ } catch (error) {
175
+ return { status: BB_RESULT.FAILURE, reason: `${error}` };
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Used for verifying proofs of noir circuits
181
+ * @param pathToBB - The full path to the bb binary
182
+ * @param proofFullPath - The full path to the proof to be verified
183
+ * @param verificationKeyPath - The full path to the circuit verification key
184
+ * @param log - A logging function
185
+ * @returns An object containing a result indication and duration taken
186
+ */
187
+ async function verifyProof(
188
+ pathToBB: string,
189
+ proofFullPath: string,
190
+ verificationKeyPath: string,
191
+ log: LogFn,
192
+ ): Promise<BBFailure | BBSuccess> {
193
+ const binaryPresent = await fs
194
+ .access(pathToBB, fs.constants.R_OK)
195
+ .then(_ => true)
196
+ .catch(_ => false);
197
+ if (!binaryPresent) {
198
+ return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
199
+ }
200
+
201
+ try {
202
+ const args = ['-p', proofFullPath, '-k', verificationKeyPath];
203
+ const timer = new Timer();
204
+ const result = await executeBB(pathToBB, 'verify', args, log);
205
+ const duration = timer.ms();
206
+ if (result == BB_RESULT.SUCCESS) {
207
+ return { status: BB_RESULT.SUCCESS, duration };
208
+ }
209
+ // Not a great error message here but it is difficult to decipher what comes from bb
210
+ return { status: BB_RESULT.FAILURE, reason: `Failed to verify proof` };
211
+ } catch (error) {
212
+ return { status: BB_RESULT.FAILURE, reason: `${error}` };
213
+ }
214
+ }
215
+
216
+ const bytecodeHashFilename = 'bytecode_hash';
217
+ const bytecodeFilename = 'bytecode';
218
+
219
+ /**
220
+ * Used for generating either a proving or verification key, will exit early if the key already exists
221
+ * It assumes the provided working directory is one where the caller wishes to maintain a permanent set of keys
222
+ * It is not considered a temporary directory
223
+ * @param pathToBB - The full path to the bb binary
224
+ * @param workingDirectory - The directory into which the key should be created
225
+ * @param circuitName - An identifier for the circuit
226
+ * @param compiledCircuit - The compiled circuit
227
+ * @param key - The type of key, either 'pk' or 'vk'
228
+ * @param log - A logging function
229
+ * @param force - Force the key to be regenerated even if it already exists
230
+ * @returns An instance of BBResult
231
+ */
232
+ export async function generateKeyForNoirCircuit(
233
+ pathToBB: string,
234
+ workingDirectory: string,
235
+ circuitName: string,
236
+ compiledCircuit: NoirCompiledCircuit,
237
+ key: 'vk' | 'pk',
238
+ log: LogFn,
239
+ force = false,
240
+ ): Promise<BBSuccess | BBFailure> {
241
+ const bytecode = Buffer.from(compiledCircuit.bytecode, 'base64');
242
+
243
+ // The key generation is written to e.g. /workingDirectory/pk/BaseParityArtifact/pk
244
+ // The bytecode hash file is also written here as /workingDirectory/pk/BaseParityArtifact/bytecode-hash
245
+ // The bytecode is written to e.g. /workingDirectory/pk/BaseParityArtifact/bytecode
246
+ // The bytecode is removed after the key is generated, leaving just the hash file
247
+ const circuitOutputDirectory = `${workingDirectory}/${key}/${circuitName}`;
248
+ const bytecodeHashPath = `${circuitOutputDirectory}/${bytecodeHashFilename}`;
249
+ const bytecodePath = `${circuitOutputDirectory}/${bytecodeFilename}`;
250
+ const bytecodeHash = sha256(bytecode);
251
+
252
+ const outputPath = `${circuitOutputDirectory}`;
253
+
254
+ // ensure the directory exists
255
+ await fs.mkdir(circuitOutputDirectory, { recursive: true });
256
+
257
+ // Generate the key if we have been told to, or there is no bytecode hash
258
+ let mustRegenerate =
259
+ force ||
260
+ (await fs
261
+ .access(bytecodeHashPath, fs.constants.R_OK)
262
+ .then(_ => false)
263
+ .catch(_ => true));
264
+
265
+ if (!mustRegenerate) {
266
+ // Check to see if the bytecode hash has changed from the stored value
267
+ const data: Buffer = await fs.readFile(bytecodeHashPath).catch(_ => Buffer.alloc(0));
268
+ mustRegenerate = data.length == 0 || !data.equals(bytecodeHash);
269
+ }
270
+
271
+ if (!mustRegenerate) {
272
+ // No need to generate, early out
273
+ return {
274
+ status: BB_RESULT.ALREADY_PRESENT,
275
+ duration: 0,
276
+ pkPath: key === 'pk' ? outputPath : undefined,
277
+ vkPath: key === 'vk' ? outputPath : undefined,
278
+ proofPath: undefined,
279
+ };
280
+ }
281
+
282
+ // Check we have access to bb
283
+ const binaryPresent = await fs
284
+ .access(pathToBB, fs.constants.R_OK)
285
+ .then(_ => true)
286
+ .catch(_ => false);
287
+ if (!binaryPresent) {
288
+ return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
289
+ }
290
+
291
+ // We are now going to generate the key
292
+ try {
293
+ // Write the bytecode to the working directory
294
+ await fs.writeFile(bytecodePath, bytecode);
295
+
296
+ // args are the output path and the input bytecode path
297
+ const args = ['-o', outputPath, '-b', bytecodePath];
298
+ const timer = new Timer();
299
+ let result = await executeBB(pathToBB, `write_${key}`, args, log);
300
+ // If we succeeded and the type of key if verification, have bb write the 'fields' version too
301
+ if (result == BB_RESULT.SUCCESS && key === 'vk') {
302
+ const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v'];
303
+ result = await executeBB(pathToBB, `vk_as_fields`, asFieldsArgs, log);
304
+ }
305
+ const duration = timer.ms();
306
+ // Cleanup the bytecode file
307
+ await fs.rm(bytecodePath, { force: true });
308
+ if (result == BB_RESULT.SUCCESS) {
309
+ // Store the bytecode hash so we don't need to regenerate at a later time
310
+ await fs.writeFile(bytecodeHashPath, bytecodeHash);
311
+ return {
312
+ status: BB_RESULT.SUCCESS,
313
+ duration,
314
+ pkPath: key === 'pk' ? outputPath : undefined,
315
+ vkPath: key === 'vk' ? outputPath : undefined,
316
+ proofPath: undefined,
317
+ };
318
+ }
319
+ // Not a great error message here but it is difficult to decipher what comes from bb
320
+ return { status: BB_RESULT.FAILURE, reason: `Failed to generate key` };
321
+ } catch (error) {
322
+ return { status: BB_RESULT.FAILURE, reason: `${error}` };
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Used for verifying proofs of noir circuits
328
+ * @param pathToBB - The full path to the bb binary
329
+ * @param verificationKeyPath - The directory containing the binary verification key
330
+ * @param verificationKeyFilename - The filename of the verification key
331
+ * @param log - A logging function
332
+ * @returns An object containing a result indication and duration taken
333
+ */
334
+ // async function writeVkAsFields(
335
+ // pathToBB: string,
336
+ // verificationKeyPath: string,
337
+ // verificationKeyFilename: string,
338
+ // log: LogFn,
339
+ // ): Promise<BBFailure | BBSuccess> {
340
+ // const binaryPresent = await fs
341
+ // .access(pathToBB, fs.constants.R_OK)
342
+ // .then(_ => true)
343
+ // .catch(_ => false);
344
+ // if (!binaryPresent) {
345
+ // return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
346
+ // }
347
+
348
+ // try {
349
+ // const args = ['-k', `${verificationKeyPath}/${verificationKeyFilename}`, '-v'];
350
+ // const timer = new Timer();
351
+ // const result = await executeBB(pathToBB, 'vk_as_fields', args, log);
352
+ // const duration = timer.ms();
353
+ // if (result == BB_RESULT.SUCCESS) {
354
+ // return { status: BB_RESULT.SUCCESS, duration, vkPath: verificationKeyPath };
355
+ // }
356
+ // // Not a great error message here but it is difficult to decipher what comes from bb
357
+ // return { status: BB_RESULT.FAILURE, reason: `Failed to create vk as fields` };
358
+ // } catch (error) {
359
+ // return { status: BB_RESULT.FAILURE, reason: `${error}` };
360
+ // }
361
+ // }
362
+
363
+ /**
364
+ * Used for verifying proofs of noir circuits
365
+ * @param pathToBB - The full path to the bb binary
366
+ * @param proofPath - The directory containing the binary proof
367
+ * @param proofFileName - The filename of the proof
368
+ * @param log - A logging function
369
+ * @returns An object containing a result indication and duration taken
370
+ */
371
+ // async function writeProofAsFields(
372
+ // pathToBB: string,
373
+ // proofPath: string,
374
+ // proofFileName: string,
375
+ // log: LogFn,
376
+ // ): Promise<BBFailure | BBSuccess> {
377
+ // const binaryPresent = await fs
378
+ // .access(pathToBB, fs.constants.R_OK)
379
+ // .then(_ => true)
380
+ // .catch(_ => false);
381
+ // if (!binaryPresent) {
382
+ // return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
383
+ // }
384
+
385
+ // try {
386
+ // const args = ['-p', `${proofPath}/${proofFileName}`, '-v'];
387
+ // const timer = new Timer();
388
+ // const result = await executeBB(pathToBB, 'proof_as_fields', args, log);
389
+ // const duration = timer.ms();
390
+ // if (result == BB_RESULT.SUCCESS) {
391
+ // return { status: BB_RESULT.SUCCESS, duration, proofPath: proofPath };
392
+ // }
393
+ // // Not a great error message here but it is difficult to decipher what comes from bb
394
+ // return { status: BB_RESULT.FAILURE, reason: `Failed to create proof as fields` };
395
+ // } catch (error) {
396
+ // return { status: BB_RESULT.FAILURE, reason: `${error}` };
397
+ // }
398
+ // }
399
+
400
+ type PrivateKernelProvingOps = {
401
+ convertOutputs: (outputs: WitnessMap) => PrivateKernelCircuitPublicInputs | PrivateKernelTailCircuitPublicInputs;
402
+ };
403
+
404
+ const KernelArtifactMapping: Record<ClientProtocolArtifact, PrivateKernelProvingOps> = {
405
+ PrivateKernelInitArtifact: {
406
+ convertOutputs: convertPrivateKernelInitOutputsFromWitnessMap,
407
+ },
408
+ PrivateKernelInnerArtifact: {
409
+ convertOutputs: convertPrivateKernelInnerOutputsFromWitnessMap,
410
+ },
411
+ PrivateKernelTailArtifact: {
412
+ convertOutputs: convertPrivateKernelTailOutputsFromWitnessMap,
413
+ },
414
+ PrivateKernelTailToPublicArtifact: {
415
+ convertOutputs: convertPrivateKernelTailForPublicOutputsFromWitnessMap,
416
+ },
417
+ };
418
+
419
+ type VerificationKeyData = {
420
+ hash: Fr;
421
+ keyAsFields: Tuple<Fr, typeof VERIFICATION_KEY_LENGTH_IN_FIELDS>;
422
+ keyAsBytes: Buffer;
423
+ numPublicInputs: number;
424
+ circuitSize: number;
425
+ isRecursive: boolean;
426
+ };
427
+
428
+ /**
429
+ * This proof creator implementation uses the native bb binary.
430
+ * This is a temporary implementation until we make the WASM version work.
431
+ */
432
+ export class BBNativeProofCreator implements ProofCreator {
433
+ private simulator = new WASMSimulator();
434
+
435
+ private verificationKeys: Map<ClientProtocolArtifact, Promise<VerificationKeyData>> = new Map<
436
+ ClientProtocolArtifact,
437
+ Promise<VerificationKeyData>
438
+ >();
439
+
440
+ constructor(
441
+ private bbBinaryPath: string,
442
+ private bbWorkingDirectory: string,
443
+ private log = createDebugLogger('aztec:bb-native-prover'),
444
+ ) {}
445
+
446
+ public getSiloedCommitments(publicInputs: PrivateCircuitPublicInputs) {
447
+ const contractAddress = publicInputs.callContext.storageContractAddress;
448
+
449
+ return Promise.resolve(
450
+ publicInputs.newNoteHashes.map(commitment => siloNoteHash(contractAddress, commitment.value)),
451
+ );
452
+ }
453
+
454
+ public async createProofInit(
455
+ inputs: PrivateKernelInitCircuitPrivateInputs,
456
+ ): Promise<ProofOutput<PrivateKernelCircuitPublicInputs>> {
457
+ const witnessMap = convertPrivateKernelInitInputsToWitnessMap(inputs);
458
+ return await this.createSafeProof(witnessMap, 'PrivateKernelInitArtifact');
459
+ }
460
+
461
+ public async createProofInner(
462
+ inputs: PrivateKernelInnerCircuitPrivateInputs,
463
+ ): Promise<ProofOutput<PrivateKernelCircuitPublicInputs>> {
464
+ const witnessMap = convertPrivateKernelInnerInputsToWitnessMap(inputs);
465
+ return await this.createSafeProof(witnessMap, 'PrivateKernelInnerArtifact');
466
+ }
467
+
468
+ public async createProofTail(
469
+ inputs: PrivateKernelTailCircuitPrivateInputs,
470
+ ): Promise<ProofOutput<PrivateKernelTailCircuitPublicInputs>> {
471
+ // if (!inputs.isForPublic()) {
472
+ // const witnessMap = convertPrivateKernelTailInputsToWitnessMap(inputs);
473
+ // return await this.createSafeProof(witnessMap, 'PrivateKernelTailArtifact');
474
+ // }
475
+
476
+ if (!inputs.isForPublic()) {
477
+ const result = await executeTail(inputs);
478
+ return {
479
+ publicInputs: result,
480
+ proof: makeEmptyProof(),
481
+ };
482
+ }
483
+ // const witnessMap = convertPrivateKernelTailToPublicInputsToWitnessMap(inputs);
484
+ // return await this.createSafeProof(witnessMap, 'PrivateKernelTailToPublicArtifact');
485
+ const result = await executeTailForPublic(inputs);
486
+ return {
487
+ publicInputs: result,
488
+ proof: makeEmptyProof(),
489
+ };
490
+ }
491
+
492
+ public async createAppCircuitProof(partialWitness: Map<number, ACVMField>, bytecode: Buffer): Promise<Proof> {
493
+ const directory = `${this.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`;
494
+ await fs.mkdir(directory, { recursive: true });
495
+ this.log.debug(`Created directory: ${directory}`);
496
+ try {
497
+ this.log.debug(`Proving app circuit`);
498
+ const proof = await this.createProof(directory, partialWitness, bytecode, 'App');
499
+ return new Proof(proof);
500
+ } finally {
501
+ await fs.rm(directory, { recursive: true, force: true });
502
+ this.log.debug(`Deleted directory: ${directory}`);
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Verifies a proof, will generate the verification key if one is not cached internally
508
+ * @param circuitType - The type of circuit whose proof is to be verified
509
+ * @param proof - The proof to be verified
510
+ */
511
+ public async verifyProof(circuitType: ClientProtocolArtifact, proof: Proof) {
512
+ // Create random directory to be used for temp files
513
+ const bbWorkingDirectory = `${this.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`;
514
+ await fs.mkdir(bbWorkingDirectory, { recursive: true });
515
+
516
+ const proofFileName = `${bbWorkingDirectory}/proof`;
517
+ const verificationKeyPath = `${bbWorkingDirectory}/vk`;
518
+ const verificationKey = await this.getVerificationKeyDataForCircuit(circuitType);
519
+
520
+ this.log.debug(`Verifying with key: ${verificationKey.hash.toString()}`);
521
+
522
+ await fs.writeFile(proofFileName, proof.buffer);
523
+ await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes);
524
+
525
+ const logFunction = (message: string) => {
526
+ this.log.debug(`${circuitType} BB out - ${message}`);
527
+ };
528
+
529
+ const result = await verifyProof(this.bbBinaryPath, proofFileName, verificationKeyPath!, logFunction);
530
+
531
+ await fs.rm(bbWorkingDirectory, { recursive: true, force: true });
532
+
533
+ if (result.status === BB_RESULT.FAILURE) {
534
+ const errorMessage = `Failed to verify ${circuitType} proof!`;
535
+ throw new Error(errorMessage);
536
+ }
537
+
538
+ this.log.info(`Successfully verified ${circuitType} proof in ${result.duration} ms`);
539
+ }
540
+
541
+ /**
542
+ * Returns the verification key data for a circuit, will generate and cache it if not cached internally
543
+ * @param circuitType - The type of circuit for which the verification key is required
544
+ * @returns The verification key data
545
+ */
546
+ private async getVerificationKeyDataForCircuit(circuitType: ClientProtocolArtifact): Promise<VerificationKeyData> {
547
+ let promise = this.verificationKeys.get(circuitType);
548
+ if (!promise) {
549
+ promise = generateKeyForNoirCircuit(
550
+ this.bbBinaryPath,
551
+ this.bbWorkingDirectory,
552
+ circuitType,
553
+ ClientCircuitArtifacts[circuitType],
554
+ 'vk',
555
+ this.log.debug,
556
+ ).then(result => {
557
+ if (result.status === BB_RESULT.FAILURE) {
558
+ throw new Error(`Failed to generate verification key for ${circuitType}, ${result.reason}`);
559
+ }
560
+ return this.convertVk(result.vkPath!);
561
+ });
562
+ this.verificationKeys.set(circuitType, promise);
563
+ }
564
+ return await promise;
565
+ }
566
+
567
+ /**
568
+ * Reads the verification key data stored at the specified location and parses into a VerificationKeyData
569
+ * @param filePath - The directory containing the verification key data files
570
+ * @returns The verification key data
571
+ */
572
+ private async convertVk(filePath: string): Promise<VerificationKeyData> {
573
+ const [rawFields, rawBinary] = await Promise.all([
574
+ fs.readFile(`${filePath}/${VK_FIELDS_FILENAME}`, { encoding: 'utf-8' }),
575
+ fs.readFile(`${filePath}/${VK_FILENAME}`),
576
+ ]);
577
+ const fieldsJson = JSON.parse(rawFields);
578
+ const fields = fieldsJson.map(Fr.fromString);
579
+ // The first item is the hash, this is not part of the actual VK
580
+ const vkHash = fields[0];
581
+ const actualVk = fields.slice(1);
582
+ const vk: VerificationKeyData = {
583
+ hash: vkHash,
584
+ keyAsFields: actualVk as Tuple<Fr, typeof VERIFICATION_KEY_LENGTH_IN_FIELDS>,
585
+ keyAsBytes: rawBinary,
586
+ numPublicInputs: Number(actualVk[CIRCUIT_PUBLIC_INPUTS_INDEX]),
587
+ circuitSize: Number(actualVk[CIRCUIT_SIZE_INDEX]),
588
+ isRecursive: actualVk[CIRCUIT_RECURSIVE_INDEX] == Fr.ONE,
589
+ };
590
+ return vk;
591
+ }
592
+
593
+ /**
594
+ * Ensures our verification key cache includes the key data located at the specified directory
595
+ * @param filePath - The directory containing the verification key data files
596
+ * @param circuitType - The type of circuit to which the verification key corresponds
597
+ */
598
+ private async updateVerificationKeyAfterProof(filePath: string, circuitType: ClientProtocolArtifact) {
599
+ let promise = this.verificationKeys.get(circuitType);
600
+ if (!promise) {
601
+ promise = this.convertVk(filePath);
602
+ this.log.debug(`Updated verification key for circuit: ${circuitType}`);
603
+ this.verificationKeys.set(circuitType, promise);
604
+ }
605
+ await promise;
606
+ }
607
+
608
+ private async createSafeProof<T>(inputs: WitnessMap, circuitType: ClientProtocolArtifact): Promise<ProofOutput<T>> {
609
+ const directory = `${this.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`;
610
+ await fs.mkdir(directory, { recursive: true });
611
+ this.log.debug(`Created directory: ${directory}`);
612
+ try {
613
+ return await this.generateWitnessAndCreateProof(inputs, circuitType, directory);
614
+ } finally {
615
+ await fs.rm(directory, { recursive: true, force: true });
616
+ this.log.debug(`Deleted directory: ${directory}`);
617
+ }
618
+ }
619
+
620
+ private async generateWitnessAndCreateProof<T>(
621
+ inputs: WitnessMap,
622
+ circuitType: ClientProtocolArtifact,
623
+ directory: string,
624
+ ): Promise<ProofOutput<T>> {
625
+ this.log.debug(`Generating witness for ${circuitType}`);
626
+ const compiledCircuit: NoirCompiledCircuit = ClientCircuitArtifacts[circuitType];
627
+
628
+ const outputWitness = await this.simulator.simulateCircuit(inputs, compiledCircuit);
629
+
630
+ this.log.debug(`Generated witness for ${circuitType}`);
631
+
632
+ const publicInputs = KernelArtifactMapping[circuitType].convertOutputs(outputWitness) as T;
633
+
634
+ const proofBuffer = await this.createProof(
635
+ directory,
636
+ outputWitness,
637
+ Buffer.from(compiledCircuit.bytecode, 'base64'),
638
+ circuitType,
639
+ );
640
+
641
+ const proofOutput: ProofOutput<T> = {
642
+ publicInputs,
643
+ proof: new Proof(proofBuffer),
644
+ };
645
+ return proofOutput;
646
+ }
647
+
648
+ private async createProof(
649
+ directory: string,
650
+ partialWitness: WitnessMap,
651
+ bytecode: Buffer,
652
+ circuitType: ClientProtocolArtifact | 'App',
653
+ ) {
654
+ const compressedBincodedWitness = serializeWitness(partialWitness);
655
+
656
+ const inputsWitnessFile = `${directory}/witness.gz`;
657
+
658
+ await fs.writeFile(inputsWitnessFile, compressedBincodedWitness);
659
+
660
+ this.log.debug(`Written ${inputsWitnessFile}`);
661
+
662
+ const provingResult = await generateProof(
663
+ this.bbBinaryPath,
664
+ directory,
665
+ circuitType,
666
+ bytecode,
667
+ inputsWitnessFile,
668
+ this.log.debug,
669
+ );
670
+
671
+ if (provingResult.status === BB_RESULT.FAILURE) {
672
+ this.log.error(`Failed to generate proof for ${circuitType}: ${provingResult.reason}`);
673
+ throw new Error(provingResult.reason);
674
+ }
675
+
676
+ if (circuitType !== 'App') {
677
+ await this.updateVerificationKeyAfterProof(directory, circuitType);
678
+ }
679
+ const proofFile = `${directory}/${PROOF_FILENAME}`;
680
+ return await fs.readFile(proofFile);
681
+ }
682
+
683
+ /**
684
+ * Parses and returns the proof data stored at the specified directory
685
+ * @param filePath - The directory containing the proof data
686
+ * @param circuitType - The type of circuit proven
687
+ * @returns The proof
688
+ */
689
+ // private async readProofAsFields<PROOF_LENGTH extends number>(
690
+ // filePath: string,
691
+ // circuitType: ClientProtocolArtifact,
692
+ // ): Promise<RecursiveProof<PROOF_LENGTH>> {
693
+ // const [binaryProof, proofString] = await Promise.all([
694
+ // fs.readFile(`${filePath}/${PROOF_FILENAME}`),
695
+ // fs.readFile(`${filePath}/${PROOF_FIELDS_FILENAME}`, { encoding: 'utf-8' }),
696
+ // ]);
697
+ // const json = JSON.parse(proofString);
698
+ // const fields = json.map(Fr.fromString);
699
+ // const vkData = await this.verificationKeys.get(circuitType);
700
+ // if (!vkData) {
701
+ // throw new Error(`Invalid verification key for ${circuitType}`);
702
+ // }
703
+ // const numPublicInputs = CIRCUITS_WITHOUT_AGGREGATION.has(circuitType)
704
+ // ? vkData.numPublicInputs
705
+ // : vkData.numPublicInputs - AGGREGATION_OBJECT_SIZE;
706
+ // const fieldsWithoutPublicInputs = fields.slice(numPublicInputs);
707
+ // logger.debug(
708
+ // `Circuit type: ${circuitType}, complete proof length: ${fields.length}, without public inputs: ${fieldsWithoutPublicInputs.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProof.length}`,
709
+ // );
710
+ // const proof = new RecursiveProof<PROOF_LENGTH>(fieldsWithoutPublicInputs, new Proof(binaryProof));
711
+ // return proof;
712
+ // }
713
+ }