@aztec/bb-prover 0.40.1 → 0.42.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/dest/bb/cli.d.ts.map +1 -1
- package/dest/bb/cli.js +24 -2
- package/dest/bb/execute.d.ts +37 -2
- package/dest/bb/execute.d.ts.map +1 -1
- package/dest/bb/execute.js +276 -75
- package/dest/config.d.ts +9 -0
- package/dest/config.d.ts.map +1 -0
- package/dest/config.js +2 -0
- package/dest/index.d.ts +2 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +3 -1
- package/dest/mappings/mappings.d.ts +1 -0
- package/dest/mappings/mappings.d.ts.map +1 -1
- package/dest/mappings/mappings.js +27 -8
- package/dest/prover/bb_native_proof_creator.d.ts +4 -9
- package/dest/prover/bb_native_proof_creator.d.ts.map +1 -1
- package/dest/prover/bb_native_proof_creator.js +77 -101
- package/dest/prover/bb_prover.d.ts +35 -23
- package/dest/prover/bb_prover.d.ts.map +1 -1
- package/dest/prover/bb_prover.js +247 -155
- package/dest/stats.d.ts +2 -5
- package/dest/stats.d.ts.map +1 -1
- package/dest/stats.js +27 -26
- package/dest/test/index.d.ts +1 -0
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +2 -1
- package/dest/test/test_circuit_prover.d.ts +9 -7
- package/dest/test/test_circuit_prover.d.ts.map +1 -1
- package/dest/test/test_circuit_prover.js +31 -15
- package/dest/test/test_verifier.d.ts +7 -0
- package/dest/test/test_verifier.d.ts.map +1 -0
- package/dest/test/test_verifier.js +10 -0
- package/dest/verification_key/verification_key_data.d.ts +8 -0
- package/dest/verification_key/verification_key_data.d.ts.map +1 -0
- package/dest/verification_key/verification_key_data.js +24 -0
- package/dest/verifier/bb_verifier.d.ts +18 -0
- package/dest/verifier/bb_verifier.d.ts.map +1 -0
- package/dest/verifier/bb_verifier.js +90 -0
- package/dest/verifier/index.d.ts +2 -0
- package/dest/verifier/index.d.ts.map +1 -0
- package/dest/verifier/index.js +2 -0
- package/package.json +6 -6
- package/src/bb/cli.ts +36 -1
- package/src/bb/execute.ts +371 -83
- package/src/config.ts +9 -0
- package/src/index.ts +2 -0
- package/src/mappings/mappings.ts +38 -12
- package/src/prover/bb_native_proof_creator.ts +139 -119
- package/src/prover/bb_prover.ts +454 -242
- package/src/stats.ts +30 -45
- package/src/test/index.ts +1 -0
- package/src/test/test_circuit_prover.ts +84 -21
- package/src/test/test_verifier.ts +12 -0
- package/src/verification_key/verification_key_data.ts +35 -0
- package/src/verifier/bb_verifier.ts +156 -0
- package/src/verifier/index.ts +1 -0
- package/dest/prover/verification_key_data.d.ts +0 -16
- package/dest/prover/verification_key_data.d.ts.map +0 -1
- package/dest/prover/verification_key_data.js +0 -5
- package/src/prover/verification_key_data.ts +0 -16
package/src/bb/execute.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AvmCircuitInputs } from '@aztec/circuits.js';
|
|
1
2
|
import { sha256 } from '@aztec/foundation/crypto';
|
|
2
3
|
import { type LogFn } from '@aztec/foundation/log';
|
|
3
4
|
import { Timer } from '@aztec/foundation/timer';
|
|
@@ -5,6 +6,7 @@ import { type NoirCompiledCircuit } from '@aztec/types/noir';
|
|
|
5
6
|
|
|
6
7
|
import * as proc from 'child_process';
|
|
7
8
|
import * as fs from 'fs/promises';
|
|
9
|
+
import { basename, dirname, join } from 'path';
|
|
8
10
|
|
|
9
11
|
export const VK_FILENAME = 'vk';
|
|
10
12
|
export const VK_FIELDS_FILENAME = 'vk_fields.json';
|
|
@@ -20,9 +22,14 @@ export enum BB_RESULT {
|
|
|
20
22
|
export type BBSuccess = {
|
|
21
23
|
status: BB_RESULT.SUCCESS | BB_RESULT.ALREADY_PRESENT;
|
|
22
24
|
duration: number;
|
|
25
|
+
/** Full path of the public key. */
|
|
23
26
|
pkPath?: string;
|
|
27
|
+
/** Base directory for the VKs (raw, fields). */
|
|
24
28
|
vkPath?: string;
|
|
29
|
+
/** Full path of the proof. */
|
|
25
30
|
proofPath?: string;
|
|
31
|
+
/** Full path of the contract. */
|
|
32
|
+
contractPath?: string;
|
|
26
33
|
};
|
|
27
34
|
|
|
28
35
|
export type BBFailure = {
|
|
@@ -32,6 +39,14 @@ export type BBFailure = {
|
|
|
32
39
|
|
|
33
40
|
export type BBResult = BBSuccess | BBFailure;
|
|
34
41
|
|
|
42
|
+
export type VerificationFunction = typeof verifyProof | typeof verifyAvmProof;
|
|
43
|
+
|
|
44
|
+
type BBExecResult = {
|
|
45
|
+
status: BB_RESULT;
|
|
46
|
+
exitCode: number;
|
|
47
|
+
signal: string | undefined;
|
|
48
|
+
};
|
|
49
|
+
|
|
35
50
|
/**
|
|
36
51
|
* Invokes the Barretenberg binary with the provided command and args
|
|
37
52
|
* @param pathToBB - The path to the BB binary
|
|
@@ -47,10 +62,15 @@ export function executeBB(
|
|
|
47
62
|
args: string[],
|
|
48
63
|
logger: LogFn,
|
|
49
64
|
resultParser = (code: number) => code === 0,
|
|
50
|
-
) {
|
|
51
|
-
return new Promise<
|
|
65
|
+
): Promise<BBExecResult> {
|
|
66
|
+
return new Promise<BBExecResult>(resolve => {
|
|
52
67
|
// spawn the bb process
|
|
53
|
-
const
|
|
68
|
+
const { HARDWARE_CONCURRENCY: _, ...envWithoutConcurrency } = process.env;
|
|
69
|
+
const env = process.env.HARDWARE_CONCURRENCY ? process.env : envWithoutConcurrency;
|
|
70
|
+
logger(`Executing BB with: ${command} ${args.join(' ')}`);
|
|
71
|
+
const bb = proc.spawn(pathToBB, [command, ...args], {
|
|
72
|
+
env,
|
|
73
|
+
});
|
|
54
74
|
bb.stdout.on('data', data => {
|
|
55
75
|
const message = data.toString('utf-8').replace(/\n$/, '');
|
|
56
76
|
logger(message);
|
|
@@ -59,17 +79,16 @@ export function executeBB(
|
|
|
59
79
|
const message = data.toString('utf-8').replace(/\n$/, '');
|
|
60
80
|
logger(message);
|
|
61
81
|
});
|
|
62
|
-
bb.on('close', (
|
|
63
|
-
if (resultParser(
|
|
64
|
-
resolve(BB_RESULT.SUCCESS);
|
|
82
|
+
bb.on('close', (exitCode: number, signal?: string) => {
|
|
83
|
+
if (resultParser(exitCode)) {
|
|
84
|
+
resolve({ status: BB_RESULT.SUCCESS, exitCode, signal });
|
|
65
85
|
} else {
|
|
66
|
-
|
|
86
|
+
resolve({ status: BB_RESULT.FAILURE, exitCode, signal });
|
|
67
87
|
}
|
|
68
88
|
});
|
|
69
|
-
}).catch(_ => BB_RESULT.FAILURE);
|
|
89
|
+
}).catch(_ => ({ status: BB_RESULT.FAILURE, exitCode: -1, signal: undefined }));
|
|
70
90
|
}
|
|
71
91
|
|
|
72
|
-
const bytecodeHashFilename = 'bytecode_hash';
|
|
73
92
|
const bytecodeFilename = 'bytecode';
|
|
74
93
|
|
|
75
94
|
/**
|
|
@@ -101,82 +120,67 @@ export async function generateKeyForNoirCircuit(
|
|
|
101
120
|
// The bytecode is written to e.g. /workingDirectory/pk/BaseParityArtifact/bytecode
|
|
102
121
|
// The bytecode is removed after the key is generated, leaving just the hash file
|
|
103
122
|
const circuitOutputDirectory = `${workingDirectory}/${key}/${circuitName}`;
|
|
104
|
-
const bytecodeHashPath = `${circuitOutputDirectory}/${bytecodeHashFilename}`;
|
|
105
|
-
const bytecodePath = `${circuitOutputDirectory}/${bytecodeFilename}`;
|
|
106
|
-
const bytecodeHash = sha256(bytecode);
|
|
107
|
-
|
|
108
123
|
const outputPath = `${circuitOutputDirectory}`;
|
|
124
|
+
const bytecodeHash = sha256(bytecode);
|
|
109
125
|
|
|
110
126
|
// ensure the directory exists
|
|
111
127
|
await fs.mkdir(circuitOutputDirectory, { recursive: true });
|
|
112
128
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
.
|
|
118
|
-
|
|
119
|
-
.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
const res = await fsCache<BBSuccess | BBFailure>(circuitOutputDirectory, bytecodeHash, log, force, async () => {
|
|
130
|
+
const binaryPresent = await fs
|
|
131
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
132
|
+
.then(_ => true)
|
|
133
|
+
.catch(_ => false);
|
|
134
|
+
if (!binaryPresent) {
|
|
135
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// We are now going to generate the key
|
|
139
|
+
try {
|
|
140
|
+
const bytecodePath = `${circuitOutputDirectory}/${bytecodeFilename}`;
|
|
141
|
+
// Write the bytecode to the working directory
|
|
142
|
+
await fs.writeFile(bytecodePath, bytecode);
|
|
126
143
|
|
|
127
|
-
|
|
128
|
-
|
|
144
|
+
// args are the output path and the input bytecode path
|
|
145
|
+
const args = ['-o', `${outputPath}/${VK_FILENAME}`, '-b', bytecodePath];
|
|
146
|
+
const timer = new Timer();
|
|
147
|
+
let result = await executeBB(pathToBB, `write_${key}`, args, log);
|
|
148
|
+
// If we succeeded and the type of key if verification, have bb write the 'fields' version too
|
|
149
|
+
if (result.status == BB_RESULT.SUCCESS && key === 'vk') {
|
|
150
|
+
const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v'];
|
|
151
|
+
result = await executeBB(pathToBB, `vk_as_fields`, asFieldsArgs, log);
|
|
152
|
+
}
|
|
153
|
+
const duration = timer.ms();
|
|
154
|
+
|
|
155
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
156
|
+
return {
|
|
157
|
+
status: BB_RESULT.SUCCESS,
|
|
158
|
+
duration,
|
|
159
|
+
pkPath: key === 'pk' ? outputPath : undefined,
|
|
160
|
+
vkPath: key === 'vk' ? outputPath : undefined,
|
|
161
|
+
proofPath: undefined,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
165
|
+
return {
|
|
166
|
+
status: BB_RESULT.FAILURE,
|
|
167
|
+
reason: `Failed to generate key. Exit code: ${result.exitCode}. Signal ${result.signal}.`,
|
|
168
|
+
};
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (!res) {
|
|
129
175
|
return {
|
|
130
176
|
status: BB_RESULT.ALREADY_PRESENT,
|
|
131
177
|
duration: 0,
|
|
132
178
|
pkPath: key === 'pk' ? outputPath : undefined,
|
|
133
179
|
vkPath: key === 'vk' ? outputPath : undefined,
|
|
134
|
-
proofPath: undefined,
|
|
135
180
|
};
|
|
136
181
|
}
|
|
137
182
|
|
|
138
|
-
|
|
139
|
-
const binaryPresent = await fs
|
|
140
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
141
|
-
.then(_ => true)
|
|
142
|
-
.catch(_ => false);
|
|
143
|
-
if (!binaryPresent) {
|
|
144
|
-
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// We are now going to generate the key
|
|
148
|
-
try {
|
|
149
|
-
// Write the bytecode to the working directory
|
|
150
|
-
await fs.writeFile(bytecodePath, bytecode);
|
|
151
|
-
|
|
152
|
-
// args are the output path and the input bytecode path
|
|
153
|
-
const args = ['-o', outputPath, '-b', bytecodePath];
|
|
154
|
-
const timer = new Timer();
|
|
155
|
-
let result = await executeBB(pathToBB, `write_${key}`, args, log);
|
|
156
|
-
// If we succeeded and the type of key if verification, have bb write the 'fields' version too
|
|
157
|
-
if (result == BB_RESULT.SUCCESS && key === 'vk') {
|
|
158
|
-
const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v'];
|
|
159
|
-
result = await executeBB(pathToBB, `vk_as_fields`, asFieldsArgs, log);
|
|
160
|
-
}
|
|
161
|
-
const duration = timer.ms();
|
|
162
|
-
// Cleanup the bytecode file
|
|
163
|
-
await fs.rm(bytecodePath, { force: true });
|
|
164
|
-
if (result == BB_RESULT.SUCCESS) {
|
|
165
|
-
// Store the bytecode hash so we don't need to regenerate at a later time
|
|
166
|
-
await fs.writeFile(bytecodeHashPath, bytecodeHash);
|
|
167
|
-
return {
|
|
168
|
-
status: BB_RESULT.SUCCESS,
|
|
169
|
-
duration,
|
|
170
|
-
pkPath: key === 'pk' ? outputPath : undefined,
|
|
171
|
-
vkPath: key === 'vk' ? outputPath : undefined,
|
|
172
|
-
proofPath: undefined,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
176
|
-
return { status: BB_RESULT.FAILURE, reason: `Failed to generate key` };
|
|
177
|
-
} catch (error) {
|
|
178
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
179
|
-
}
|
|
183
|
+
return res;
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
/**
|
|
@@ -229,9 +233,8 @@ export async function generateProof(
|
|
|
229
233
|
};
|
|
230
234
|
const result = await executeBB(pathToBB, 'prove_output_all', args, logFunction);
|
|
231
235
|
const duration = timer.ms();
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (result == BB_RESULT.SUCCESS) {
|
|
236
|
+
|
|
237
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
235
238
|
return {
|
|
236
239
|
status: BB_RESULT.SUCCESS,
|
|
237
240
|
duration,
|
|
@@ -241,7 +244,119 @@ export async function generateProof(
|
|
|
241
244
|
};
|
|
242
245
|
}
|
|
243
246
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
244
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
status: BB_RESULT.FAILURE,
|
|
249
|
+
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
250
|
+
};
|
|
251
|
+
} catch (error) {
|
|
252
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Used for generating AVM proofs.
|
|
258
|
+
* It is assumed that the working directory is a temporary and/or random directory used solely for generating this proof.
|
|
259
|
+
* @param pathToBB - The full path to the bb binary
|
|
260
|
+
* @param workingDirectory - A working directory for use by bb
|
|
261
|
+
* @param bytecode - The AVM bytecode for the public function to be proven (expected to be decompressed)
|
|
262
|
+
* @param log - A logging function
|
|
263
|
+
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
264
|
+
*/
|
|
265
|
+
export async function generateAvmProof(
|
|
266
|
+
pathToBB: string,
|
|
267
|
+
workingDirectory: string,
|
|
268
|
+
input: AvmCircuitInputs,
|
|
269
|
+
log: LogFn,
|
|
270
|
+
): Promise<BBFailure | BBSuccess> {
|
|
271
|
+
// Check that the working directory exists
|
|
272
|
+
try {
|
|
273
|
+
await fs.access(workingDirectory);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Paths for the inputs
|
|
279
|
+
const bytecodePath = join(workingDirectory, 'avm_bytecode.bin');
|
|
280
|
+
const calldataPath = join(workingDirectory, 'avm_calldata.bin');
|
|
281
|
+
const publicInputsPath = join(workingDirectory, 'avm_public_inputs.bin');
|
|
282
|
+
const avmHintsPath = join(workingDirectory, 'avm_hints.bin');
|
|
283
|
+
|
|
284
|
+
// The proof is written to e.g. /workingDirectory/proof
|
|
285
|
+
const outputPath = workingDirectory;
|
|
286
|
+
|
|
287
|
+
const filePresent = async (file: string) =>
|
|
288
|
+
await fs
|
|
289
|
+
.access(file, fs.constants.R_OK)
|
|
290
|
+
.then(_ => true)
|
|
291
|
+
.catch(_ => false);
|
|
292
|
+
|
|
293
|
+
const binaryPresent = await filePresent(pathToBB);
|
|
294
|
+
if (!binaryPresent) {
|
|
295
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
// Write the inputs to the working directory.
|
|
300
|
+
await fs.writeFile(bytecodePath, input.bytecode);
|
|
301
|
+
if (!filePresent(bytecodePath)) {
|
|
302
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write bytecode at ${bytecodePath}` };
|
|
303
|
+
}
|
|
304
|
+
await fs.writeFile(
|
|
305
|
+
calldataPath,
|
|
306
|
+
input.calldata.map(fr => fr.toBuffer()),
|
|
307
|
+
);
|
|
308
|
+
if (!filePresent(calldataPath)) {
|
|
309
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write calldata at ${calldataPath}` };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// public inputs are used directly as a vector of fields in C++,
|
|
313
|
+
// so we serialize them as such here instead of just using toBuffer
|
|
314
|
+
await fs.writeFile(
|
|
315
|
+
publicInputsPath,
|
|
316
|
+
input.publicInputs.toFields().map(fr => fr.toBuffer()),
|
|
317
|
+
);
|
|
318
|
+
if (!filePresent(publicInputsPath)) {
|
|
319
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write publicInputs at ${publicInputsPath}` };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
await fs.writeFile(avmHintsPath, input.avmHints.toBuffer());
|
|
323
|
+
if (!filePresent(avmHintsPath)) {
|
|
324
|
+
return { status: BB_RESULT.FAILURE, reason: `Could not write avmHints at ${avmHintsPath}` };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const args = [
|
|
328
|
+
'--avm-bytecode',
|
|
329
|
+
bytecodePath,
|
|
330
|
+
'--avm-calldata',
|
|
331
|
+
calldataPath,
|
|
332
|
+
'--avm-public-inputs',
|
|
333
|
+
publicInputsPath,
|
|
334
|
+
'--avm-hints',
|
|
335
|
+
avmHintsPath,
|
|
336
|
+
'-o',
|
|
337
|
+
outputPath,
|
|
338
|
+
];
|
|
339
|
+
const timer = new Timer();
|
|
340
|
+
const logFunction = (message: string) => {
|
|
341
|
+
log(`AvmCircuit (prove) BB out - ${message}`);
|
|
342
|
+
};
|
|
343
|
+
const result = await executeBB(pathToBB, 'avm_prove', args, logFunction);
|
|
344
|
+
const duration = timer.ms();
|
|
345
|
+
|
|
346
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
347
|
+
return {
|
|
348
|
+
status: BB_RESULT.SUCCESS,
|
|
349
|
+
duration,
|
|
350
|
+
proofPath: join(outputPath, PROOF_FILENAME),
|
|
351
|
+
pkPath: undefined,
|
|
352
|
+
vkPath: outputPath,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
356
|
+
return {
|
|
357
|
+
status: BB_RESULT.FAILURE,
|
|
358
|
+
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
359
|
+
};
|
|
245
360
|
} catch (error) {
|
|
246
361
|
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
247
362
|
}
|
|
@@ -260,6 +375,42 @@ export async function verifyProof(
|
|
|
260
375
|
proofFullPath: string,
|
|
261
376
|
verificationKeyPath: string,
|
|
262
377
|
log: LogFn,
|
|
378
|
+
): Promise<BBFailure | BBSuccess> {
|
|
379
|
+
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'verify', log);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Used for verifying proofs of the AVM
|
|
384
|
+
* @param pathToBB - The full path to the bb binary
|
|
385
|
+
* @param proofFullPath - The full path to the proof to be verified
|
|
386
|
+
* @param verificationKeyPath - The full path to the circuit verification key
|
|
387
|
+
* @param log - A logging function
|
|
388
|
+
* @returns An object containing a result indication and duration taken
|
|
389
|
+
*/
|
|
390
|
+
export async function verifyAvmProof(
|
|
391
|
+
pathToBB: string,
|
|
392
|
+
proofFullPath: string,
|
|
393
|
+
verificationKeyPath: string,
|
|
394
|
+
log: LogFn,
|
|
395
|
+
): Promise<BBFailure | BBSuccess> {
|
|
396
|
+
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm_verify', log);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Used for verifying proofs with BB
|
|
401
|
+
* @param pathToBB - The full path to the bb binary
|
|
402
|
+
* @param proofFullPath - The full path to the proof to be verified
|
|
403
|
+
* @param verificationKeyPath - The full path to the circuit verification key
|
|
404
|
+
* @param command - The BB command to execute (verify/avm_verify)
|
|
405
|
+
* @param log - A logging function
|
|
406
|
+
* @returns An object containing a result indication and duration taken
|
|
407
|
+
*/
|
|
408
|
+
async function verifyProofInternal(
|
|
409
|
+
pathToBB: string,
|
|
410
|
+
proofFullPath: string,
|
|
411
|
+
verificationKeyPath: string,
|
|
412
|
+
command: 'verify' | 'avm_verify',
|
|
413
|
+
log: LogFn,
|
|
263
414
|
): Promise<BBFailure | BBSuccess> {
|
|
264
415
|
const binaryPresent = await fs
|
|
265
416
|
.access(pathToBB, fs.constants.R_OK)
|
|
@@ -272,13 +423,16 @@ export async function verifyProof(
|
|
|
272
423
|
try {
|
|
273
424
|
const args = ['-p', proofFullPath, '-k', verificationKeyPath];
|
|
274
425
|
const timer = new Timer();
|
|
275
|
-
const result = await executeBB(pathToBB,
|
|
426
|
+
const result = await executeBB(pathToBB, command, args, log);
|
|
276
427
|
const duration = timer.ms();
|
|
277
|
-
if (result == BB_RESULT.SUCCESS) {
|
|
428
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
278
429
|
return { status: BB_RESULT.SUCCESS, duration };
|
|
279
430
|
}
|
|
280
431
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
281
|
-
return {
|
|
432
|
+
return {
|
|
433
|
+
status: BB_RESULT.FAILURE,
|
|
434
|
+
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
435
|
+
};
|
|
282
436
|
} catch (error) {
|
|
283
437
|
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
284
438
|
}
|
|
@@ -311,11 +465,14 @@ export async function writeVkAsFields(
|
|
|
311
465
|
const timer = new Timer();
|
|
312
466
|
const result = await executeBB(pathToBB, 'vk_as_fields', args, log);
|
|
313
467
|
const duration = timer.ms();
|
|
314
|
-
if (result == BB_RESULT.SUCCESS) {
|
|
468
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
315
469
|
return { status: BB_RESULT.SUCCESS, duration, vkPath: verificationKeyPath };
|
|
316
470
|
}
|
|
317
471
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
318
|
-
return {
|
|
472
|
+
return {
|
|
473
|
+
status: BB_RESULT.FAILURE,
|
|
474
|
+
reason: `Failed to create vk as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
475
|
+
};
|
|
319
476
|
} catch (error) {
|
|
320
477
|
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
321
478
|
}
|
|
@@ -326,6 +483,7 @@ export async function writeVkAsFields(
|
|
|
326
483
|
* @param pathToBB - The full path to the bb binary
|
|
327
484
|
* @param proofPath - The directory containing the binary proof
|
|
328
485
|
* @param proofFileName - The filename of the proof
|
|
486
|
+
* @param vkFileName - The filename of the verification key
|
|
329
487
|
* @param log - A logging function
|
|
330
488
|
* @returns An object containing a result indication and duration taken
|
|
331
489
|
*/
|
|
@@ -333,6 +491,7 @@ export async function writeProofAsFields(
|
|
|
333
491
|
pathToBB: string,
|
|
334
492
|
proofPath: string,
|
|
335
493
|
proofFileName: string,
|
|
494
|
+
vkFilePath: string,
|
|
336
495
|
log: LogFn,
|
|
337
496
|
): Promise<BBFailure | BBSuccess> {
|
|
338
497
|
const binaryPresent = await fs
|
|
@@ -344,16 +503,145 @@ export async function writeProofAsFields(
|
|
|
344
503
|
}
|
|
345
504
|
|
|
346
505
|
try {
|
|
347
|
-
const args = ['-p', `${proofPath}/${proofFileName}`, '-v'];
|
|
506
|
+
const args = ['-p', `${proofPath}/${proofFileName}`, '-k', vkFilePath, '-v'];
|
|
348
507
|
const timer = new Timer();
|
|
349
508
|
const result = await executeBB(pathToBB, 'proof_as_fields', args, log);
|
|
350
509
|
const duration = timer.ms();
|
|
351
|
-
if (result == BB_RESULT.SUCCESS) {
|
|
510
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
352
511
|
return { status: BB_RESULT.SUCCESS, duration, proofPath: proofPath };
|
|
353
512
|
}
|
|
354
513
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
355
|
-
return {
|
|
514
|
+
return {
|
|
515
|
+
status: BB_RESULT.FAILURE,
|
|
516
|
+
reason: `Failed to create proof as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
517
|
+
};
|
|
356
518
|
} catch (error) {
|
|
357
519
|
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
358
520
|
}
|
|
359
521
|
}
|
|
522
|
+
|
|
523
|
+
export async function generateContractForVerificationKey(
|
|
524
|
+
pathToBB: string,
|
|
525
|
+
vkFilePath: string,
|
|
526
|
+
contractPath: string,
|
|
527
|
+
log: LogFn,
|
|
528
|
+
): Promise<BBFailure | BBSuccess> {
|
|
529
|
+
const binaryPresent = await fs
|
|
530
|
+
.access(pathToBB, fs.constants.R_OK)
|
|
531
|
+
.then(_ => true)
|
|
532
|
+
.catch(_ => false);
|
|
533
|
+
|
|
534
|
+
if (!binaryPresent) {
|
|
535
|
+
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const outputDir = dirname(contractPath);
|
|
539
|
+
const contractName = basename(contractPath);
|
|
540
|
+
// cache contract generation based on vk file and contract name
|
|
541
|
+
const cacheKey = sha256(Buffer.concat([Buffer.from(contractName), await fs.readFile(vkFilePath)]));
|
|
542
|
+
|
|
543
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
544
|
+
|
|
545
|
+
const res = await fsCache<BBSuccess | BBFailure>(outputDir, cacheKey, log, false, async () => {
|
|
546
|
+
try {
|
|
547
|
+
const args = ['-k', vkFilePath, '-o', contractPath, '-v'];
|
|
548
|
+
const timer = new Timer();
|
|
549
|
+
const result = await executeBB(pathToBB, 'contract', args, log);
|
|
550
|
+
const duration = timer.ms();
|
|
551
|
+
if (result.status == BB_RESULT.SUCCESS) {
|
|
552
|
+
return { status: BB_RESULT.SUCCESS, duration, contractPath };
|
|
553
|
+
}
|
|
554
|
+
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
555
|
+
return {
|
|
556
|
+
status: BB_RESULT.FAILURE,
|
|
557
|
+
reason: `Failed to write verifier contract. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
558
|
+
};
|
|
559
|
+
} catch (error) {
|
|
560
|
+
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
if (!res) {
|
|
565
|
+
return {
|
|
566
|
+
status: BB_RESULT.ALREADY_PRESENT,
|
|
567
|
+
duration: 0,
|
|
568
|
+
contractPath,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return res;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export async function generateContractForCircuit(
|
|
576
|
+
pathToBB: string,
|
|
577
|
+
workingDirectory: string,
|
|
578
|
+
circuitName: string,
|
|
579
|
+
compiledCircuit: NoirCompiledCircuit,
|
|
580
|
+
contractName: string,
|
|
581
|
+
log: LogFn,
|
|
582
|
+
force = false,
|
|
583
|
+
) {
|
|
584
|
+
const vkResult = await generateKeyForNoirCircuit(
|
|
585
|
+
pathToBB,
|
|
586
|
+
workingDirectory,
|
|
587
|
+
circuitName,
|
|
588
|
+
compiledCircuit,
|
|
589
|
+
'vk',
|
|
590
|
+
log,
|
|
591
|
+
force,
|
|
592
|
+
);
|
|
593
|
+
if (vkResult.status === BB_RESULT.FAILURE) {
|
|
594
|
+
return vkResult;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return generateContractForVerificationKey(
|
|
598
|
+
pathToBB,
|
|
599
|
+
join(vkResult.vkPath!, VK_FILENAME),
|
|
600
|
+
join(workingDirectory, 'contract', circuitName, contractName),
|
|
601
|
+
log,
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const CACHE_FILENAME = '.cache';
|
|
606
|
+
async function fsCache<T>(
|
|
607
|
+
dir: string,
|
|
608
|
+
expectedCacheKey: Buffer,
|
|
609
|
+
logger: LogFn,
|
|
610
|
+
force: boolean,
|
|
611
|
+
action: () => Promise<T>,
|
|
612
|
+
): Promise<T | undefined> {
|
|
613
|
+
const cacheFilePath = join(dir, CACHE_FILENAME);
|
|
614
|
+
|
|
615
|
+
let run: boolean;
|
|
616
|
+
if (force) {
|
|
617
|
+
run = true;
|
|
618
|
+
} else {
|
|
619
|
+
try {
|
|
620
|
+
run = !expectedCacheKey.equals(await fs.readFile(cacheFilePath));
|
|
621
|
+
} catch (err: any) {
|
|
622
|
+
if (err && 'code' in err && err.code === 'ENOENT') {
|
|
623
|
+
// cache file doesn't exist, swallow error and run
|
|
624
|
+
run = true;
|
|
625
|
+
} else {
|
|
626
|
+
throw err;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
let res: T | undefined;
|
|
632
|
+
if (run) {
|
|
633
|
+
logger(`Cache miss or forced run. Running operation in ${dir}...`);
|
|
634
|
+
res = await action();
|
|
635
|
+
} else {
|
|
636
|
+
logger(`Cache hit. Skipping operation in ${dir}...`);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
try {
|
|
640
|
+
await fs.writeFile(cacheFilePath, expectedCacheKey);
|
|
641
|
+
} catch (err) {
|
|
642
|
+
logger(`Couldn't write cache data to ${cacheFilePath}. Skipping cache...`);
|
|
643
|
+
// ignore
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return res;
|
|
647
|
+
}
|
package/src/config.ts
ADDED
package/src/index.ts
CHANGED
package/src/mappings/mappings.ts
CHANGED
|
@@ -2,12 +2,18 @@ import { PublicKernelType } from '@aztec/circuit-types';
|
|
|
2
2
|
import { type PublicKernelCircuitPrivateInputs, type PublicKernelCircuitPublicInputs } from '@aztec/circuits.js';
|
|
3
3
|
import {
|
|
4
4
|
type ServerProtocolArtifact,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
convertPublicInnerInputsToWitnessMap,
|
|
6
|
+
convertPublicInnerOutputFromWitnessMap,
|
|
7
|
+
convertPublicSetupInputsToWitnessMap,
|
|
8
|
+
convertPublicSetupOutputFromWitnessMap,
|
|
9
|
+
convertPublicTeardownInputsToWitnessMap,
|
|
10
|
+
convertPublicTeardownOutputFromWitnessMap,
|
|
11
|
+
convertSimulatedPublicInnerInputsToWitnessMap,
|
|
12
|
+
convertSimulatedPublicInnerOutputFromWitnessMap,
|
|
13
|
+
convertSimulatedPublicSetupInputsToWitnessMap,
|
|
14
|
+
convertSimulatedPublicSetupOutputFromWitnessMap,
|
|
15
|
+
convertSimulatedPublicTeardownInputsToWitnessMap,
|
|
16
|
+
convertSimulatedPublicTeardownOutputFromWitnessMap,
|
|
11
17
|
} from '@aztec/noir-protocol-circuits-types';
|
|
12
18
|
|
|
13
19
|
import { type WitnessMap } from '@noir-lang/types';
|
|
@@ -20,22 +26,42 @@ export type PublicKernelProvingOps = {
|
|
|
20
26
|
|
|
21
27
|
export type KernelTypeToArtifact = Record<PublicKernelType, PublicKernelProvingOps | undefined>;
|
|
22
28
|
|
|
29
|
+
export const SimulatedPublicKernelArtifactMapping: KernelTypeToArtifact = {
|
|
30
|
+
[PublicKernelType.NON_PUBLIC]: undefined,
|
|
31
|
+
[PublicKernelType.APP_LOGIC]: {
|
|
32
|
+
artifact: 'PublicKernelAppLogicArtifact',
|
|
33
|
+
convertInputs: convertSimulatedPublicInnerInputsToWitnessMap,
|
|
34
|
+
convertOutputs: convertSimulatedPublicInnerOutputFromWitnessMap,
|
|
35
|
+
},
|
|
36
|
+
[PublicKernelType.SETUP]: {
|
|
37
|
+
artifact: 'PublicKernelSetupArtifact',
|
|
38
|
+
convertInputs: convertSimulatedPublicSetupInputsToWitnessMap,
|
|
39
|
+
convertOutputs: convertSimulatedPublicSetupOutputFromWitnessMap,
|
|
40
|
+
},
|
|
41
|
+
[PublicKernelType.TEARDOWN]: {
|
|
42
|
+
artifact: 'PublicKernelTeardownArtifact',
|
|
43
|
+
convertInputs: convertSimulatedPublicTeardownInputsToWitnessMap,
|
|
44
|
+
convertOutputs: convertSimulatedPublicTeardownOutputFromWitnessMap,
|
|
45
|
+
},
|
|
46
|
+
[PublicKernelType.TAIL]: undefined,
|
|
47
|
+
};
|
|
48
|
+
|
|
23
49
|
export const PublicKernelArtifactMapping: KernelTypeToArtifact = {
|
|
24
50
|
[PublicKernelType.NON_PUBLIC]: undefined,
|
|
25
51
|
[PublicKernelType.APP_LOGIC]: {
|
|
26
52
|
artifact: 'PublicKernelAppLogicArtifact',
|
|
27
|
-
convertInputs:
|
|
28
|
-
convertOutputs:
|
|
53
|
+
convertInputs: convertPublicInnerInputsToWitnessMap,
|
|
54
|
+
convertOutputs: convertPublicInnerOutputFromWitnessMap,
|
|
29
55
|
},
|
|
30
56
|
[PublicKernelType.SETUP]: {
|
|
31
57
|
artifact: 'PublicKernelSetupArtifact',
|
|
32
|
-
convertInputs:
|
|
33
|
-
convertOutputs:
|
|
58
|
+
convertInputs: convertPublicSetupInputsToWitnessMap,
|
|
59
|
+
convertOutputs: convertPublicSetupOutputFromWitnessMap,
|
|
34
60
|
},
|
|
35
61
|
[PublicKernelType.TEARDOWN]: {
|
|
36
62
|
artifact: 'PublicKernelTeardownArtifact',
|
|
37
|
-
convertInputs:
|
|
38
|
-
convertOutputs:
|
|
63
|
+
convertInputs: convertPublicTeardownInputsToWitnessMap,
|
|
64
|
+
convertOutputs: convertPublicTeardownOutputFromWitnessMap,
|
|
39
65
|
},
|
|
40
66
|
[PublicKernelType.TAIL]: undefined,
|
|
41
67
|
};
|