@aztec/bb-prover 0.74.0 → 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2
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/avm_proving_tests/avm_proving_tester.js +12 -11
- package/dest/bb/cli.js +4 -9
- package/dest/bb/execute.js +395 -267
- package/dest/bb/index.js +4 -6
- package/dest/config.js +1 -2
- package/dest/honk.js +8 -5
- package/dest/index.js +0 -1
- package/dest/instrumentation.js +44 -41
- package/dest/prover/bb_native_private_kernel_prover.js +19 -19
- package/dest/prover/bb_private_kernel_prover.js +11 -11
- package/dest/prover/bb_prover.js +431 -445
- package/dest/prover/client_ivc_proof_utils.js +15 -9
- package/dest/prover/index.js +0 -1
- package/dest/stats.js +15 -14
- package/dest/test/index.js +0 -1
- package/dest/test/test_circuit_prover.js +156 -160
- package/dest/test/test_verifier.js +0 -1
- package/dest/verification_key/verification_key_data.js +10 -8
- package/dest/verifier/bb_verifier.js +13 -13
- package/dest/verifier/index.js +0 -1
- package/dest/wasm/bb_wasm_private_kernel_prover.js +11 -10
- package/dest/wasm/bundle.js +1 -2
- package/dest/wasm/lazy.js +1 -2
- package/package.json +10 -10
- package/dest/avm_proving_tests/avm_proving_tester.d.ts +0 -24
- package/dest/avm_proving_tests/avm_proving_tester.d.ts.map +0 -1
- package/dest/bb/cli.d.ts +0 -12
- package/dest/bb/cli.d.ts.map +0 -1
- package/dest/bb/execute.d.ts +0 -170
- package/dest/bb/execute.d.ts.map +0 -1
- package/dest/bb/index.d.ts +0 -3
- package/dest/bb/index.d.ts.map +0 -1
- package/dest/config.d.ts +0 -13
- package/dest/config.d.ts.map +0 -1
- package/dest/honk.d.ts +0 -13
- package/dest/honk.d.ts.map +0 -1
- package/dest/index.d.ts +0 -8
- package/dest/index.d.ts.map +0 -1
- package/dest/instrumentation.d.ts +0 -47
- package/dest/instrumentation.d.ts.map +0 -1
- package/dest/prover/bb_native_private_kernel_prover.d.ts +0 -25
- package/dest/prover/bb_native_private_kernel_prover.d.ts.map +0 -1
- package/dest/prover/bb_private_kernel_prover.d.ts +0 -31
- package/dest/prover/bb_private_kernel_prover.d.ts.map +0 -1
- package/dest/prover/bb_prover.d.ts +0 -123
- package/dest/prover/bb_prover.d.ts.map +0 -1
- package/dest/prover/client_ivc_proof_utils.d.ts +0 -25
- package/dest/prover/client_ivc_proof_utils.d.ts.map +0 -1
- package/dest/prover/index.d.ts +0 -4
- package/dest/prover/index.d.ts.map +0 -1
- package/dest/stats.d.ts +0 -5
- package/dest/stats.d.ts.map +0 -1
- package/dest/test/index.d.ts +0 -3
- package/dest/test/index.d.ts.map +0 -1
- package/dest/test/test_circuit_prover.d.ts +0 -72
- package/dest/test/test_circuit_prover.d.ts.map +0 -1
- package/dest/test/test_verifier.d.ts +0 -5
- package/dest/test/test_verifier.d.ts.map +0 -1
- package/dest/verification_key/verification_key_data.d.ts +0 -9
- package/dest/verification_key/verification_key_data.d.ts.map +0 -1
- package/dest/verifier/bb_verifier.d.ts +0 -15
- package/dest/verifier/bb_verifier.d.ts.map +0 -1
- package/dest/verifier/index.d.ts +0 -2
- package/dest/verifier/index.d.ts.map +0 -1
- package/dest/wasm/bb_wasm_private_kernel_prover.d.ts +0 -16
- package/dest/wasm/bb_wasm_private_kernel_prover.d.ts.map +0 -1
- package/dest/wasm/bundle.d.ts +0 -6
- package/dest/wasm/bundle.d.ts.map +0 -1
- package/dest/wasm/lazy.d.ts +0 -6
- package/dest/wasm/lazy.d.ts.map +0 -1
package/dest/bb/execute.js
CHANGED
|
@@ -12,12 +12,12 @@ export const PROOF_FIELDS_FILENAME = 'proof_fields.json';
|
|
|
12
12
|
export const AVM_BYTECODE_FILENAME = 'avm_bytecode.bin';
|
|
13
13
|
export const AVM_PUBLIC_INPUTS_FILENAME = 'avm_public_inputs.bin';
|
|
14
14
|
export const AVM_HINTS_FILENAME = 'avm_hints.bin';
|
|
15
|
-
export var BB_RESULT
|
|
16
|
-
(function (BB_RESULT) {
|
|
15
|
+
export var BB_RESULT = /*#__PURE__*/ function(BB_RESULT) {
|
|
17
16
|
BB_RESULT[BB_RESULT["SUCCESS"] = 0] = "SUCCESS";
|
|
18
17
|
BB_RESULT[BB_RESULT["FAILURE"] = 1] = "FAILURE";
|
|
19
18
|
BB_RESULT[BB_RESULT["ALREADY_PRESENT"] = 2] = "ALREADY_PRESENT";
|
|
20
|
-
|
|
19
|
+
return BB_RESULT;
|
|
20
|
+
}({});
|
|
21
21
|
/**
|
|
22
22
|
* Invokes the Barretenberg binary with the provided command and args
|
|
23
23
|
* @param pathToBB - The path to the BB binary
|
|
@@ -26,51 +26,66 @@ export var BB_RESULT;
|
|
|
26
26
|
* @param logger - A log function
|
|
27
27
|
* @param resultParser - An optional handler for detecting success or failure
|
|
28
28
|
* @returns The completed partial witness outputted from the circuit
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
return new Promise(resolve => {
|
|
29
|
+
*/ export function executeBB(pathToBB, command, args, logger, resultParser = (code)=>code === 0) {
|
|
30
|
+
return new Promise((resolve)=>{
|
|
32
31
|
// spawn the bb process
|
|
33
32
|
const { HARDWARE_CONCURRENCY: _, ...envWithoutConcurrency } = process.env;
|
|
34
33
|
const env = process.env.HARDWARE_CONCURRENCY ? process.env : envWithoutConcurrency;
|
|
35
34
|
logger(`Executing BB with: ${pathToBB} ${command} ${args.join(' ')}`);
|
|
36
|
-
const bb = proc.spawn(pathToBB, [
|
|
37
|
-
|
|
35
|
+
const bb = proc.spawn(pathToBB, [
|
|
36
|
+
command,
|
|
37
|
+
...args
|
|
38
|
+
], {
|
|
39
|
+
env
|
|
38
40
|
});
|
|
39
|
-
bb.stdout.on('data', data
|
|
41
|
+
bb.stdout.on('data', (data)=>{
|
|
40
42
|
const message = data.toString('utf-8').replace(/\n$/, '');
|
|
41
43
|
logger(message);
|
|
42
44
|
});
|
|
43
|
-
bb.stderr.on('data', data
|
|
45
|
+
bb.stderr.on('data', (data)=>{
|
|
44
46
|
const message = data.toString('utf-8').replace(/\n$/, '');
|
|
45
47
|
logger(message);
|
|
46
48
|
});
|
|
47
|
-
bb.on('close', (exitCode, signal)
|
|
49
|
+
bb.on('close', (exitCode, signal)=>{
|
|
48
50
|
if (resultParser(exitCode)) {
|
|
49
|
-
resolve({
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
resolve({
|
|
52
|
+
status: 0,
|
|
53
|
+
exitCode,
|
|
54
|
+
signal
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
resolve({
|
|
58
|
+
status: 1,
|
|
59
|
+
exitCode,
|
|
60
|
+
signal
|
|
61
|
+
});
|
|
53
62
|
}
|
|
54
63
|
});
|
|
55
|
-
}).catch(_
|
|
64
|
+
}).catch((_)=>({
|
|
65
|
+
status: 1,
|
|
66
|
+
exitCode: -1,
|
|
67
|
+
signal: undefined
|
|
68
|
+
}));
|
|
56
69
|
}
|
|
57
70
|
// TODO(#7369) comment this etc (really just take inspiration from this and rewrite it all O:))
|
|
58
71
|
export async function executeBbClientIvcProof(pathToBB, workingDirectory, bytecodeStackPath, witnessStackPath, log) {
|
|
59
72
|
// Check that the working directory exists
|
|
60
73
|
try {
|
|
61
74
|
await fs.access(workingDirectory);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return {
|
|
77
|
+
status: 1,
|
|
78
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
79
|
+
};
|
|
65
80
|
}
|
|
66
81
|
// The proof is written to e.g. /workingDirectory/proof
|
|
67
82
|
const outputPath = `${workingDirectory}`;
|
|
68
|
-
const binaryPresent = await fs
|
|
69
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
70
|
-
.then(_ => true)
|
|
71
|
-
.catch(_ => false);
|
|
83
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
72
84
|
if (!binaryPresent) {
|
|
73
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
status: 1,
|
|
87
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
88
|
+
};
|
|
74
89
|
}
|
|
75
90
|
try {
|
|
76
91
|
// Write the bytecode to the working directory
|
|
@@ -87,32 +102,34 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
87
102
|
'--scheme',
|
|
88
103
|
'client_ivc',
|
|
89
104
|
'--input_type',
|
|
90
|
-
'runtime_stack'
|
|
105
|
+
'runtime_stack'
|
|
91
106
|
];
|
|
92
107
|
const timer = new Timer();
|
|
93
|
-
const logFunction = (message)
|
|
108
|
+
const logFunction = (message)=>{
|
|
94
109
|
log(`bb - ${message}`);
|
|
95
110
|
};
|
|
96
111
|
const result = await executeBB(pathToBB, 'prove', args, logFunction);
|
|
97
112
|
const durationMs = timer.ms();
|
|
98
|
-
if (result.status ==
|
|
113
|
+
if (result.status == 0) {
|
|
99
114
|
return {
|
|
100
|
-
status:
|
|
115
|
+
status: 0,
|
|
101
116
|
durationMs,
|
|
102
117
|
proofPath: `${outputPath}`,
|
|
103
118
|
pkPath: undefined,
|
|
104
|
-
vkPath: `${outputPath}
|
|
119
|
+
vkPath: `${outputPath}`
|
|
105
120
|
};
|
|
106
121
|
}
|
|
107
122
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
108
123
|
return {
|
|
109
|
-
status:
|
|
124
|
+
status: 1,
|
|
110
125
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
111
|
-
retry: !!result.signal
|
|
126
|
+
retry: !!result.signal
|
|
127
|
+
};
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return {
|
|
130
|
+
status: 1,
|
|
131
|
+
reason: `${error}`
|
|
112
132
|
};
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
116
133
|
}
|
|
117
134
|
}
|
|
118
135
|
/**
|
|
@@ -125,57 +142,76 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
125
142
|
* @param inputWitnessFile - The circuit input witness
|
|
126
143
|
* @param log - A logging function
|
|
127
144
|
* @returns An object containing a result indication, the location of the VK and the duration taken
|
|
128
|
-
*/
|
|
129
|
-
export async function computeVerificationKey(pathToBB, workingDirectory, circuitName, bytecode, recursive, flavor, log) {
|
|
145
|
+
*/ export async function computeVerificationKey(pathToBB, workingDirectory, circuitName, bytecode, recursive, flavor, log) {
|
|
130
146
|
// Check that the working directory exists
|
|
131
147
|
try {
|
|
132
148
|
await fs.access(workingDirectory);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return {
|
|
151
|
+
status: 1,
|
|
152
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
153
|
+
};
|
|
136
154
|
}
|
|
137
155
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
138
156
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
139
157
|
// The verification key is written to this path
|
|
140
158
|
const outputPath = `${workingDirectory}/vk`;
|
|
141
|
-
const binaryPresent = await fs
|
|
142
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
143
|
-
.then(_ => true)
|
|
144
|
-
.catch(_ => false);
|
|
159
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
145
160
|
if (!binaryPresent) {
|
|
146
|
-
return {
|
|
161
|
+
return {
|
|
162
|
+
status: 1,
|
|
163
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
164
|
+
};
|
|
147
165
|
}
|
|
148
166
|
try {
|
|
149
167
|
// Write the bytecode to the working directory
|
|
150
168
|
await fs.writeFile(bytecodePath, bytecode);
|
|
151
169
|
const timer = new Timer();
|
|
152
|
-
const logFunction = (message)
|
|
170
|
+
const logFunction = (message)=>{
|
|
153
171
|
log(`computeVerificationKey(${circuitName}) BB out - ${message}`);
|
|
154
172
|
};
|
|
155
|
-
const args = [
|
|
173
|
+
const args = [
|
|
174
|
+
'-o',
|
|
175
|
+
outputPath,
|
|
176
|
+
'-b',
|
|
177
|
+
bytecodePath,
|
|
178
|
+
'-v',
|
|
179
|
+
recursive ? '--recursive' : ''
|
|
180
|
+
];
|
|
156
181
|
let result = await executeBB(pathToBB, `write_vk_${flavor}`, args, logFunction);
|
|
157
|
-
if (result.status ==
|
|
158
|
-
return {
|
|
182
|
+
if (result.status == 1) {
|
|
183
|
+
return {
|
|
184
|
+
status: 1,
|
|
185
|
+
reason: 'Failed writing VK.'
|
|
186
|
+
};
|
|
159
187
|
}
|
|
160
|
-
result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, [
|
|
188
|
+
result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, [
|
|
189
|
+
'-o',
|
|
190
|
+
outputPath + '_fields.json',
|
|
191
|
+
'-k',
|
|
192
|
+
outputPath,
|
|
193
|
+
'-v'
|
|
194
|
+
], logFunction);
|
|
161
195
|
const duration = timer.ms();
|
|
162
|
-
if (result.status ==
|
|
196
|
+
if (result.status == 0) {
|
|
163
197
|
return {
|
|
164
|
-
status:
|
|
198
|
+
status: 0,
|
|
165
199
|
durationMs: duration,
|
|
166
200
|
pkPath: undefined,
|
|
167
|
-
vkPath: `${outputPath}
|
|
201
|
+
vkPath: `${outputPath}`
|
|
168
202
|
};
|
|
169
203
|
}
|
|
170
204
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
171
205
|
return {
|
|
172
|
-
status:
|
|
206
|
+
status: 1,
|
|
173
207
|
reason: `Failed to write VK. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
174
|
-
retry: !!result.signal
|
|
208
|
+
retry: !!result.signal
|
|
209
|
+
};
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
status: 1,
|
|
213
|
+
reason: `${error}`
|
|
175
214
|
};
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
179
215
|
}
|
|
180
216
|
}
|
|
181
217
|
/**
|
|
@@ -188,54 +224,66 @@ export async function computeVerificationKey(pathToBB, workingDirectory, circuit
|
|
|
188
224
|
* @param inputWitnessFile - The circuit input witness
|
|
189
225
|
* @param log - A logging function
|
|
190
226
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
191
|
-
*/
|
|
192
|
-
export async function generateProof(pathToBB, workingDirectory, circuitName, bytecode, recursive, inputWitnessFile, flavor, log) {
|
|
227
|
+
*/ export async function generateProof(pathToBB, workingDirectory, circuitName, bytecode, recursive, inputWitnessFile, flavor, log) {
|
|
193
228
|
// Check that the working directory exists
|
|
194
229
|
try {
|
|
195
230
|
await fs.access(workingDirectory);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
231
|
+
} catch (error) {
|
|
232
|
+
return {
|
|
233
|
+
status: 1,
|
|
234
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
235
|
+
};
|
|
199
236
|
}
|
|
200
237
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
201
238
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
202
239
|
// The proof is written to e.g. /workingDirectory/ultra_honk/proof
|
|
203
240
|
const outputPath = `${workingDirectory}`;
|
|
204
|
-
const binaryPresent = await fs
|
|
205
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
206
|
-
.then(_ => true)
|
|
207
|
-
.catch(_ => false);
|
|
241
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
208
242
|
if (!binaryPresent) {
|
|
209
|
-
return {
|
|
243
|
+
return {
|
|
244
|
+
status: 1,
|
|
245
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
246
|
+
};
|
|
210
247
|
}
|
|
211
248
|
try {
|
|
212
249
|
// Write the bytecode to the working directory
|
|
213
250
|
await fs.writeFile(bytecodePath, bytecode);
|
|
214
|
-
const args = [
|
|
251
|
+
const args = [
|
|
252
|
+
'-o',
|
|
253
|
+
outputPath,
|
|
254
|
+
'-b',
|
|
255
|
+
bytecodePath,
|
|
256
|
+
'-w',
|
|
257
|
+
inputWitnessFile,
|
|
258
|
+
'-v',
|
|
259
|
+
recursive ? '--recursive' : ''
|
|
260
|
+
];
|
|
215
261
|
const timer = new Timer();
|
|
216
|
-
const logFunction = (message)
|
|
262
|
+
const logFunction = (message)=>{
|
|
217
263
|
log(`${circuitName} BB out - ${message}`);
|
|
218
264
|
};
|
|
219
265
|
const result = await executeBB(pathToBB, `prove_${flavor}_output_all`, args, logFunction);
|
|
220
266
|
const duration = timer.ms();
|
|
221
|
-
if (result.status ==
|
|
267
|
+
if (result.status == 0) {
|
|
222
268
|
return {
|
|
223
|
-
status:
|
|
269
|
+
status: 0,
|
|
224
270
|
durationMs: duration,
|
|
225
271
|
proofPath: `${outputPath}`,
|
|
226
272
|
pkPath: undefined,
|
|
227
|
-
vkPath: `${outputPath}
|
|
273
|
+
vkPath: `${outputPath}`
|
|
228
274
|
};
|
|
229
275
|
}
|
|
230
276
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
231
277
|
return {
|
|
232
|
-
status:
|
|
278
|
+
status: 1,
|
|
233
279
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
234
|
-
retry: !!result.signal
|
|
280
|
+
retry: !!result.signal
|
|
281
|
+
};
|
|
282
|
+
} catch (error) {
|
|
283
|
+
return {
|
|
284
|
+
status: 1,
|
|
285
|
+
reason: `${error}`
|
|
235
286
|
};
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
239
287
|
}
|
|
240
288
|
}
|
|
241
289
|
/**
|
|
@@ -248,57 +296,67 @@ export async function generateProof(pathToBB, workingDirectory, circuitName, byt
|
|
|
248
296
|
* @param inputWitnessFile - The circuit input witness
|
|
249
297
|
* @param log - A logging function
|
|
250
298
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
251
|
-
*/
|
|
252
|
-
export async function generateTubeProof(pathToBB, workingDirectory, log) {
|
|
299
|
+
*/ export async function generateTubeProof(pathToBB, workingDirectory, log) {
|
|
253
300
|
// Check that the working directory exists
|
|
254
301
|
try {
|
|
255
302
|
await fs.access(workingDirectory);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return {
|
|
305
|
+
status: 1,
|
|
306
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
307
|
+
};
|
|
259
308
|
}
|
|
260
309
|
// // Paths for the inputs
|
|
261
310
|
const vkPath = join(workingDirectory, CLIENT_IVC_VK_FILE_NAME);
|
|
262
311
|
const proofPath = join(workingDirectory, CLIENT_IVC_PROOF_FILE_NAME);
|
|
263
312
|
// The proof is written to e.g. /workingDirectory/proof
|
|
264
313
|
const outputPath = workingDirectory;
|
|
265
|
-
const filePresent = async (file)
|
|
266
|
-
.access(file, fs.constants.R_OK)
|
|
267
|
-
.then(_ => true)
|
|
268
|
-
.catch(_ => false);
|
|
314
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
269
315
|
const binaryPresent = await filePresent(pathToBB);
|
|
270
316
|
if (!binaryPresent) {
|
|
271
|
-
return {
|
|
317
|
+
return {
|
|
318
|
+
status: 1,
|
|
319
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
320
|
+
};
|
|
272
321
|
}
|
|
273
322
|
try {
|
|
274
|
-
if (!
|
|
275
|
-
return {
|
|
323
|
+
if (!await filePresent(vkPath) || !await filePresent(proofPath)) {
|
|
324
|
+
return {
|
|
325
|
+
status: 1,
|
|
326
|
+
reason: `Client IVC input files not present in ${workingDirectory}`
|
|
327
|
+
};
|
|
276
328
|
}
|
|
277
|
-
const args = [
|
|
329
|
+
const args = [
|
|
330
|
+
'-o',
|
|
331
|
+
outputPath,
|
|
332
|
+
'-v'
|
|
333
|
+
];
|
|
278
334
|
const timer = new Timer();
|
|
279
|
-
const logFunction = (message)
|
|
335
|
+
const logFunction = (message)=>{
|
|
280
336
|
log(`TubeCircuit (prove) BB out - ${message}`);
|
|
281
337
|
};
|
|
282
338
|
const result = await executeBB(pathToBB, 'prove_tube', args, logFunction);
|
|
283
339
|
const durationMs = timer.ms();
|
|
284
|
-
if (result.status ==
|
|
340
|
+
if (result.status == 0) {
|
|
285
341
|
return {
|
|
286
|
-
status:
|
|
342
|
+
status: 0,
|
|
287
343
|
durationMs,
|
|
288
344
|
proofPath: outputPath,
|
|
289
345
|
pkPath: undefined,
|
|
290
|
-
vkPath: outputPath
|
|
346
|
+
vkPath: outputPath
|
|
291
347
|
};
|
|
292
348
|
}
|
|
293
349
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
294
350
|
return {
|
|
295
|
-
status:
|
|
351
|
+
status: 1,
|
|
296
352
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
297
|
-
retry: !!result.signal
|
|
353
|
+
retry: !!result.signal
|
|
354
|
+
};
|
|
355
|
+
} catch (error) {
|
|
356
|
+
return {
|
|
357
|
+
status: 1,
|
|
358
|
+
reason: `${error}`
|
|
298
359
|
};
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
302
360
|
}
|
|
303
361
|
}
|
|
304
362
|
/**
|
|
@@ -309,64 +367,70 @@ export async function generateTubeProof(pathToBB, workingDirectory, log) {
|
|
|
309
367
|
* @param input - The inputs for the public function to be proven
|
|
310
368
|
* @param log - A logging function
|
|
311
369
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
312
|
-
*/
|
|
313
|
-
export async function generateAvmProofV2(pathToBB, workingDirectory, input, logger) {
|
|
370
|
+
*/ export async function generateAvmProofV2(pathToBB, workingDirectory, input, logger) {
|
|
314
371
|
// Check that the working directory exists
|
|
315
372
|
try {
|
|
316
373
|
await fs.access(workingDirectory);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
374
|
+
} catch (error) {
|
|
375
|
+
return {
|
|
376
|
+
status: 1,
|
|
377
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
378
|
+
};
|
|
320
379
|
}
|
|
321
380
|
// The proof is written to e.g. /workingDirectory/proof
|
|
322
381
|
const outputPath = workingDirectory;
|
|
323
|
-
const filePresent = async (file)
|
|
324
|
-
.access(file, fs.constants.R_OK)
|
|
325
|
-
.then(_ => true)
|
|
326
|
-
.catch(_ => false);
|
|
382
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
327
383
|
const binaryPresent = await filePresent(pathToBB);
|
|
328
384
|
if (!binaryPresent) {
|
|
329
|
-
return {
|
|
385
|
+
return {
|
|
386
|
+
status: 1,
|
|
387
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
388
|
+
};
|
|
330
389
|
}
|
|
331
390
|
const inputsBuffer = input.serializeForAvm2();
|
|
332
391
|
try {
|
|
333
392
|
// Write the inputs to the working directory.
|
|
334
393
|
const avmInputsPath = join(workingDirectory, 'avm_inputs.bin');
|
|
335
394
|
await fs.writeFile(avmInputsPath, inputsBuffer);
|
|
336
|
-
if (!
|
|
337
|
-
return {
|
|
395
|
+
if (!await filePresent(avmInputsPath)) {
|
|
396
|
+
return {
|
|
397
|
+
status: 1,
|
|
398
|
+
reason: `Could not write avm inputs to ${avmInputsPath}`
|
|
399
|
+
};
|
|
338
400
|
}
|
|
339
401
|
const args = [
|
|
340
402
|
'--avm-inputs',
|
|
341
403
|
avmInputsPath,
|
|
342
404
|
'-o',
|
|
343
405
|
outputPath,
|
|
344
|
-
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : ''
|
|
406
|
+
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : ''
|
|
345
407
|
];
|
|
346
408
|
const timer = new Timer();
|
|
347
|
-
const logFunction = (message)
|
|
409
|
+
const logFunction = (message)=>{
|
|
348
410
|
logger.verbose(`AvmCircuit (prove) BB out - ${message}`);
|
|
349
411
|
};
|
|
350
412
|
const result = await executeBB(pathToBB, 'avm2_prove', args, logFunction);
|
|
351
413
|
const duration = timer.ms();
|
|
352
|
-
if (result.status ==
|
|
414
|
+
if (result.status == 0) {
|
|
353
415
|
return {
|
|
354
|
-
status:
|
|
416
|
+
status: 0,
|
|
355
417
|
durationMs: duration,
|
|
356
418
|
proofPath: join(outputPath, PROOF_FILENAME),
|
|
357
419
|
pkPath: undefined,
|
|
358
|
-
vkPath: outputPath
|
|
420
|
+
vkPath: outputPath
|
|
359
421
|
};
|
|
360
422
|
}
|
|
361
423
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
362
424
|
return {
|
|
363
|
-
status:
|
|
425
|
+
status: 1,
|
|
364
426
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
365
|
-
retry: !!result.signal
|
|
427
|
+
retry: !!result.signal
|
|
428
|
+
};
|
|
429
|
+
} catch (error) {
|
|
430
|
+
return {
|
|
431
|
+
status: 1,
|
|
432
|
+
reason: `${error}`
|
|
366
433
|
};
|
|
367
|
-
}
|
|
368
|
-
catch (error) {
|
|
369
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
370
434
|
}
|
|
371
435
|
}
|
|
372
436
|
/**
|
|
@@ -377,37 +441,44 @@ export async function generateAvmProofV2(pathToBB, workingDirectory, input, logg
|
|
|
377
441
|
* @param bytecode - The AVM bytecode for the public function to be proven (expected to be decompressed)
|
|
378
442
|
* @param log - A logging function
|
|
379
443
|
* @returns An object containing a result indication, the location of the proof and the duration taken
|
|
380
|
-
*/
|
|
381
|
-
export async function generateAvmProof(pathToBB, workingDirectory, input, logger, checkCircuitOnly = false) {
|
|
444
|
+
*/ export async function generateAvmProof(pathToBB, workingDirectory, input, logger, checkCircuitOnly = false) {
|
|
382
445
|
// Check that the working directory exists
|
|
383
446
|
try {
|
|
384
447
|
await fs.access(workingDirectory);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
448
|
+
} catch (error) {
|
|
449
|
+
return {
|
|
450
|
+
status: 1,
|
|
451
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
452
|
+
};
|
|
388
453
|
}
|
|
389
454
|
// Paths for the inputs
|
|
390
455
|
const publicInputsPath = join(workingDirectory, AVM_PUBLIC_INPUTS_FILENAME);
|
|
391
456
|
const avmHintsPath = join(workingDirectory, AVM_HINTS_FILENAME);
|
|
392
457
|
// The proof is written to e.g. /workingDirectory/proof
|
|
393
458
|
const outputPath = workingDirectory;
|
|
394
|
-
const filePresent = async (file)
|
|
395
|
-
.access(file, fs.constants.R_OK)
|
|
396
|
-
.then(_ => true)
|
|
397
|
-
.catch(_ => false);
|
|
459
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
398
460
|
const binaryPresent = await filePresent(pathToBB);
|
|
399
461
|
if (!binaryPresent) {
|
|
400
|
-
return {
|
|
462
|
+
return {
|
|
463
|
+
status: 1,
|
|
464
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
465
|
+
};
|
|
401
466
|
}
|
|
402
467
|
try {
|
|
403
468
|
// Write the inputs to the working directory.
|
|
404
469
|
await fs.writeFile(publicInputsPath, input.publicInputs.toBuffer());
|
|
405
|
-
if (!
|
|
406
|
-
return {
|
|
470
|
+
if (!await filePresent(publicInputsPath)) {
|
|
471
|
+
return {
|
|
472
|
+
status: 1,
|
|
473
|
+
reason: `Could not write publicInputs at ${publicInputsPath}`
|
|
474
|
+
};
|
|
407
475
|
}
|
|
408
476
|
await fs.writeFile(avmHintsPath, input.avmHints.toBuffer());
|
|
409
|
-
if (!
|
|
410
|
-
return {
|
|
477
|
+
if (!await filePresent(avmHintsPath)) {
|
|
478
|
+
return {
|
|
479
|
+
status: 1,
|
|
480
|
+
reason: `Could not write avmHints at ${avmHintsPath}`
|
|
481
|
+
};
|
|
411
482
|
}
|
|
412
483
|
const args = [
|
|
413
484
|
'--avm-public-inputs',
|
|
@@ -417,33 +488,35 @@ export async function generateAvmProof(pathToBB, workingDirectory, input, logger
|
|
|
417
488
|
'-o',
|
|
418
489
|
outputPath,
|
|
419
490
|
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
420
|
-
checkCircuitOnly ? '--check-circuit-only' : ''
|
|
491
|
+
checkCircuitOnly ? '--check-circuit-only' : ''
|
|
421
492
|
];
|
|
422
493
|
const timer = new Timer();
|
|
423
494
|
const cmd = checkCircuitOnly ? 'check_circuit' : 'prove';
|
|
424
|
-
const logFunction = (message)
|
|
495
|
+
const logFunction = (message)=>{
|
|
425
496
|
logger.verbose(`AvmCircuit (${cmd}) BB out - ${message}`);
|
|
426
497
|
};
|
|
427
498
|
const result = await executeBB(pathToBB, `avm_${cmd}`, args, logFunction);
|
|
428
499
|
const duration = timer.ms();
|
|
429
|
-
if (result.status ==
|
|
500
|
+
if (result.status == 0) {
|
|
430
501
|
return {
|
|
431
|
-
status:
|
|
502
|
+
status: 0,
|
|
432
503
|
durationMs: duration,
|
|
433
504
|
proofPath: join(outputPath, PROOF_FILENAME),
|
|
434
505
|
pkPath: undefined,
|
|
435
|
-
vkPath: outputPath
|
|
506
|
+
vkPath: outputPath
|
|
436
507
|
};
|
|
437
508
|
}
|
|
438
509
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
439
510
|
return {
|
|
440
|
-
status:
|
|
511
|
+
status: 1,
|
|
441
512
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
442
|
-
retry: !!result.signal
|
|
513
|
+
retry: !!result.signal
|
|
514
|
+
};
|
|
515
|
+
} catch (error) {
|
|
516
|
+
return {
|
|
517
|
+
status: 1,
|
|
518
|
+
reason: `${error}`
|
|
443
519
|
};
|
|
444
|
-
}
|
|
445
|
-
catch (error) {
|
|
446
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
447
520
|
}
|
|
448
521
|
}
|
|
449
522
|
/**
|
|
@@ -453,8 +526,7 @@ export async function generateAvmProof(pathToBB, workingDirectory, input, logger
|
|
|
453
526
|
* @param verificationKeyPath - The full path to the circuit verification key
|
|
454
527
|
* @param log - A logging function
|
|
455
528
|
* @returns An object containing a result indication and duration taken
|
|
456
|
-
*/
|
|
457
|
-
export async function verifyProof(pathToBB, proofFullPath, verificationKeyPath, ultraHonkFlavor, log) {
|
|
529
|
+
*/ export async function verifyProof(pathToBB, proofFullPath, verificationKeyPath, ultraHonkFlavor, log) {
|
|
458
530
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, `verify_${ultraHonkFlavor}`, log);
|
|
459
531
|
}
|
|
460
532
|
/**
|
|
@@ -464,25 +536,24 @@ export async function verifyProof(pathToBB, proofFullPath, verificationKeyPath,
|
|
|
464
536
|
* @param verificationKeyPath - The full path to the circuit verification key
|
|
465
537
|
* @param log - A logging function
|
|
466
538
|
* @returns An object containing a result indication and duration taken
|
|
467
|
-
*/
|
|
468
|
-
export async function verifyAvmProof(pathToBB, proofFullPath, verificationKeyPath, logger) {
|
|
539
|
+
*/ export async function verifyAvmProof(pathToBB, proofFullPath, verificationKeyPath, logger) {
|
|
469
540
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm_verify', logger);
|
|
470
541
|
}
|
|
471
542
|
export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath, publicInputs, verificationKeyPath, logger) {
|
|
472
543
|
const inputsBuffer = serializeWithMessagePack(publicInputs);
|
|
473
544
|
// Write the inputs to the working directory.
|
|
474
|
-
const filePresent = async (file)
|
|
475
|
-
.access(file, fs.constants.R_OK)
|
|
476
|
-
.then(_ => true)
|
|
477
|
-
.catch(_ => false);
|
|
545
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
478
546
|
const avmInputsPath = join(workingDirectory, 'avm_public_inputs.bin');
|
|
479
547
|
await fs.writeFile(avmInputsPath, inputsBuffer);
|
|
480
|
-
if (!
|
|
481
|
-
return {
|
|
548
|
+
if (!await filePresent(avmInputsPath)) {
|
|
549
|
+
return {
|
|
550
|
+
status: 1,
|
|
551
|
+
reason: `Could not write avm inputs to ${avmInputsPath}`
|
|
552
|
+
};
|
|
482
553
|
}
|
|
483
554
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm2_verify', logger, [
|
|
484
555
|
'--avm-public-inputs',
|
|
485
|
-
avmInputsPath
|
|
556
|
+
avmInputsPath
|
|
486
557
|
]);
|
|
487
558
|
}
|
|
488
559
|
/**
|
|
@@ -492,33 +563,42 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
492
563
|
* @param targetPath - The path to the folder with the proof, accumulator, and verification keys
|
|
493
564
|
* @param log - A logging function
|
|
494
565
|
* @returns An object containing a result indication and duration taken
|
|
495
|
-
*/
|
|
496
|
-
|
|
497
|
-
const binaryPresent = await fs
|
|
498
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
499
|
-
.then(_ => true)
|
|
500
|
-
.catch(_ => false);
|
|
566
|
+
*/ export async function verifyClientIvcProof(pathToBB, targetPath, log) {
|
|
567
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
501
568
|
if (!binaryPresent) {
|
|
502
|
-
return {
|
|
569
|
+
return {
|
|
570
|
+
status: 1,
|
|
571
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
572
|
+
};
|
|
503
573
|
}
|
|
504
574
|
try {
|
|
505
|
-
const args = [
|
|
575
|
+
const args = [
|
|
576
|
+
'-o',
|
|
577
|
+
targetPath,
|
|
578
|
+
'--scheme',
|
|
579
|
+
'client_ivc'
|
|
580
|
+
];
|
|
506
581
|
const timer = new Timer();
|
|
507
582
|
const command = 'verify';
|
|
508
583
|
const result = await executeBB(pathToBB, command, args, log);
|
|
509
584
|
const duration = timer.ms();
|
|
510
|
-
if (result.status ==
|
|
511
|
-
return {
|
|
585
|
+
if (result.status == 0) {
|
|
586
|
+
return {
|
|
587
|
+
status: 0,
|
|
588
|
+
durationMs: duration
|
|
589
|
+
};
|
|
512
590
|
}
|
|
513
591
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
514
592
|
return {
|
|
515
|
-
status:
|
|
593
|
+
status: 1,
|
|
516
594
|
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
517
|
-
retry: !!result.signal
|
|
595
|
+
retry: !!result.signal
|
|
596
|
+
};
|
|
597
|
+
} catch (error) {
|
|
598
|
+
return {
|
|
599
|
+
status: 1,
|
|
600
|
+
reason: `${error}`
|
|
518
601
|
};
|
|
519
|
-
}
|
|
520
|
-
catch (error) {
|
|
521
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
522
602
|
}
|
|
523
603
|
}
|
|
524
604
|
/**
|
|
@@ -529,16 +609,15 @@ export async function verifyClientIvcProof(pathToBB, targetPath, log) {
|
|
|
529
609
|
* @param command - The BB command to execute (verify/avm_verify)
|
|
530
610
|
* @param log - A logging function
|
|
531
611
|
* @returns An object containing a result indication and duration taken
|
|
532
|
-
*/
|
|
533
|
-
|
|
534
|
-
const binaryPresent = await fs
|
|
535
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
536
|
-
.then(_ => true)
|
|
537
|
-
.catch(_ => false);
|
|
612
|
+
*/ async function verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, command, logger, extraArgs = []) {
|
|
613
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
538
614
|
if (!binaryPresent) {
|
|
539
|
-
return {
|
|
615
|
+
return {
|
|
616
|
+
status: 1,
|
|
617
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
618
|
+
};
|
|
540
619
|
}
|
|
541
|
-
const logFunction = (message)
|
|
620
|
+
const logFunction = (message)=>{
|
|
542
621
|
logger.verbose(`AvmCircuit (verify) BB out - ${message}`);
|
|
543
622
|
};
|
|
544
623
|
try {
|
|
@@ -548,23 +627,28 @@ async function verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath,
|
|
|
548
627
|
'-k',
|
|
549
628
|
verificationKeyPath,
|
|
550
629
|
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
551
|
-
...extraArgs
|
|
630
|
+
...extraArgs
|
|
552
631
|
];
|
|
553
632
|
const timer = new Timer();
|
|
554
633
|
const result = await executeBB(pathToBB, command, args, logFunction);
|
|
555
634
|
const duration = timer.ms();
|
|
556
|
-
if (result.status ==
|
|
557
|
-
return {
|
|
635
|
+
if (result.status == 0) {
|
|
636
|
+
return {
|
|
637
|
+
status: 0,
|
|
638
|
+
durationMs: duration
|
|
639
|
+
};
|
|
558
640
|
}
|
|
559
641
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
560
642
|
return {
|
|
561
|
-
status:
|
|
643
|
+
status: 1,
|
|
562
644
|
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
563
|
-
retry: !!result.signal
|
|
645
|
+
retry: !!result.signal
|
|
646
|
+
};
|
|
647
|
+
} catch (error) {
|
|
648
|
+
return {
|
|
649
|
+
status: 1,
|
|
650
|
+
reason: `${error}`
|
|
564
651
|
};
|
|
565
|
-
}
|
|
566
|
-
catch (error) {
|
|
567
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
568
652
|
}
|
|
569
653
|
}
|
|
570
654
|
/**
|
|
@@ -574,32 +658,41 @@ async function verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath,
|
|
|
574
658
|
* @param verificationKeyFilename - The filename of the verification key
|
|
575
659
|
* @param log - A logging function
|
|
576
660
|
* @returns An object containing a result indication and duration taken
|
|
577
|
-
*/
|
|
578
|
-
|
|
579
|
-
const binaryPresent = await fs
|
|
580
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
581
|
-
.then(_ => true)
|
|
582
|
-
.catch(_ => false);
|
|
661
|
+
*/ export async function writeVkAsFields(pathToBB, verificationKeyPath, verificationKeyFilename, log) {
|
|
662
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
583
663
|
if (!binaryPresent) {
|
|
584
|
-
return {
|
|
664
|
+
return {
|
|
665
|
+
status: 1,
|
|
666
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
667
|
+
};
|
|
585
668
|
}
|
|
586
669
|
try {
|
|
587
|
-
const args = [
|
|
670
|
+
const args = [
|
|
671
|
+
'-k',
|
|
672
|
+
`${verificationKeyPath}/${verificationKeyFilename}`,
|
|
673
|
+
'-v'
|
|
674
|
+
];
|
|
588
675
|
const timer = new Timer();
|
|
589
676
|
const result = await executeBB(pathToBB, 'vk_as_fields_ultra_honk', args, log);
|
|
590
677
|
const duration = timer.ms();
|
|
591
|
-
if (result.status ==
|
|
592
|
-
return {
|
|
678
|
+
if (result.status == 0) {
|
|
679
|
+
return {
|
|
680
|
+
status: 0,
|
|
681
|
+
durationMs: duration,
|
|
682
|
+
vkPath: verificationKeyPath
|
|
683
|
+
};
|
|
593
684
|
}
|
|
594
685
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
595
686
|
return {
|
|
596
|
-
status:
|
|
687
|
+
status: 1,
|
|
597
688
|
reason: `Failed to create vk as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
598
|
-
retry: !!result.signal
|
|
689
|
+
retry: !!result.signal
|
|
690
|
+
};
|
|
691
|
+
} catch (error) {
|
|
692
|
+
return {
|
|
693
|
+
status: 1,
|
|
694
|
+
reason: `${error}`
|
|
599
695
|
};
|
|
600
|
-
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
603
696
|
}
|
|
604
697
|
}
|
|
605
698
|
/**
|
|
@@ -610,72 +703,100 @@ export async function writeVkAsFields(pathToBB, verificationKeyPath, verificatio
|
|
|
610
703
|
* @param vkFileName - The filename of the verification key
|
|
611
704
|
* @param log - A logging function
|
|
612
705
|
* @returns An object containing a result indication and duration taken
|
|
613
|
-
*/
|
|
614
|
-
|
|
615
|
-
const binaryPresent = await fs
|
|
616
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
617
|
-
.then(_ => true)
|
|
618
|
-
.catch(_ => false);
|
|
706
|
+
*/ export async function writeProofAsFields(pathToBB, proofPath, proofFileName, vkFilePath, log) {
|
|
707
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
619
708
|
if (!binaryPresent) {
|
|
620
|
-
return {
|
|
709
|
+
return {
|
|
710
|
+
status: 1,
|
|
711
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
712
|
+
};
|
|
621
713
|
}
|
|
622
714
|
try {
|
|
623
|
-
const args = [
|
|
715
|
+
const args = [
|
|
716
|
+
'-p',
|
|
717
|
+
`${proofPath}/${proofFileName}`,
|
|
718
|
+
'-k',
|
|
719
|
+
vkFilePath,
|
|
720
|
+
'-v'
|
|
721
|
+
];
|
|
624
722
|
const timer = new Timer();
|
|
625
723
|
const result = await executeBB(pathToBB, 'proof_as_fields_honk', args, log);
|
|
626
724
|
const duration = timer.ms();
|
|
627
|
-
if (result.status ==
|
|
628
|
-
return {
|
|
725
|
+
if (result.status == 0) {
|
|
726
|
+
return {
|
|
727
|
+
status: 0,
|
|
728
|
+
durationMs: duration,
|
|
729
|
+
proofPath: proofPath
|
|
730
|
+
};
|
|
629
731
|
}
|
|
630
732
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
631
733
|
return {
|
|
632
|
-
status:
|
|
734
|
+
status: 1,
|
|
633
735
|
reason: `Failed to create proof as fields. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
634
|
-
retry: !!result.signal
|
|
736
|
+
retry: !!result.signal
|
|
737
|
+
};
|
|
738
|
+
} catch (error) {
|
|
739
|
+
return {
|
|
740
|
+
status: 1,
|
|
741
|
+
reason: `${error}`
|
|
635
742
|
};
|
|
636
|
-
}
|
|
637
|
-
catch (error) {
|
|
638
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
639
743
|
}
|
|
640
744
|
}
|
|
641
745
|
export async function generateContractForVerificationKey(pathToBB, vkFilePath, contractPath, log) {
|
|
642
|
-
const binaryPresent = await fs
|
|
643
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
644
|
-
.then(_ => true)
|
|
645
|
-
.catch(_ => false);
|
|
746
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
646
747
|
if (!binaryPresent) {
|
|
647
|
-
return {
|
|
748
|
+
return {
|
|
749
|
+
status: 1,
|
|
750
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
751
|
+
};
|
|
648
752
|
}
|
|
649
753
|
const outputDir = dirname(contractPath);
|
|
650
754
|
const contractName = basename(contractPath);
|
|
651
755
|
// cache contract generation based on vk file and contract name
|
|
652
|
-
const cacheKey = sha256(Buffer.concat([
|
|
653
|
-
|
|
654
|
-
|
|
756
|
+
const cacheKey = sha256(Buffer.concat([
|
|
757
|
+
Buffer.from(contractName),
|
|
758
|
+
await fs.readFile(vkFilePath)
|
|
759
|
+
]));
|
|
760
|
+
await fs.mkdir(outputDir, {
|
|
761
|
+
recursive: true
|
|
762
|
+
});
|
|
763
|
+
const res = await fsCache(outputDir, cacheKey, log, false, async ()=>{
|
|
655
764
|
try {
|
|
656
|
-
const args = [
|
|
765
|
+
const args = [
|
|
766
|
+
'-k',
|
|
767
|
+
vkFilePath,
|
|
768
|
+
'-o',
|
|
769
|
+
contractPath,
|
|
770
|
+
'-v'
|
|
771
|
+
];
|
|
657
772
|
const timer = new Timer();
|
|
658
773
|
const result = await executeBB(pathToBB, 'contract_ultra_honk', args, log);
|
|
659
774
|
const duration = timer.ms();
|
|
660
|
-
if (result.status ==
|
|
661
|
-
return {
|
|
775
|
+
if (result.status == 0) {
|
|
776
|
+
return {
|
|
777
|
+
status: 0,
|
|
778
|
+
durationMs: duration,
|
|
779
|
+
contractPath
|
|
780
|
+
};
|
|
662
781
|
}
|
|
663
782
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
664
783
|
return {
|
|
665
|
-
status:
|
|
784
|
+
status: 1,
|
|
666
785
|
reason: `Failed to write verifier contract. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
667
|
-
retry: !!result.signal
|
|
786
|
+
retry: !!result.signal
|
|
787
|
+
};
|
|
788
|
+
} catch (error) {
|
|
789
|
+
return {
|
|
790
|
+
status: 1,
|
|
791
|
+
reason: `${error}`
|
|
668
792
|
};
|
|
669
|
-
}
|
|
670
|
-
catch (error) {
|
|
671
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
672
793
|
}
|
|
673
794
|
});
|
|
674
795
|
if (!res) {
|
|
675
796
|
return {
|
|
676
|
-
status:
|
|
797
|
+
status: 2,
|
|
677
798
|
durationMs: 0,
|
|
678
|
-
contractPath
|
|
799
|
+
contractPath
|
|
679
800
|
};
|
|
680
801
|
}
|
|
681
802
|
return res;
|
|
@@ -688,27 +809,28 @@ export async function generateContractForVerificationKey(pathToBB, vkFilePath, c
|
|
|
688
809
|
* @param bytecode - The bytecode of the circuit
|
|
689
810
|
* @param flavor - The flavor of the backend - mega_honk or ultra_honk variants
|
|
690
811
|
* @returns An object containing the status, gate count, and time taken
|
|
691
|
-
*/
|
|
692
|
-
export async function computeGateCountForCircuit(pathToBB, workingDirectory, circuitName, bytecode, flavor, log) {
|
|
812
|
+
*/ export async function computeGateCountForCircuit(pathToBB, workingDirectory, circuitName, bytecode, flavor, log) {
|
|
693
813
|
// Check that the working directory exists
|
|
694
814
|
try {
|
|
695
815
|
await fs.access(workingDirectory);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
|
|
816
|
+
} catch (error) {
|
|
817
|
+
return {
|
|
818
|
+
status: 1,
|
|
819
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
820
|
+
};
|
|
699
821
|
}
|
|
700
822
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
701
823
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
702
|
-
const binaryPresent = await fs
|
|
703
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
704
|
-
.then(_ => true)
|
|
705
|
-
.catch(_ => false);
|
|
824
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
706
825
|
if (!binaryPresent) {
|
|
707
|
-
return {
|
|
826
|
+
return {
|
|
827
|
+
status: 1,
|
|
828
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
829
|
+
};
|
|
708
830
|
}
|
|
709
831
|
// Accumulate the stdout from bb
|
|
710
832
|
let stdout = '';
|
|
711
|
-
const logHandler = (message)
|
|
833
|
+
const logHandler = (message)=>{
|
|
712
834
|
stdout += message;
|
|
713
835
|
log(message);
|
|
714
836
|
};
|
|
@@ -716,25 +838,37 @@ export async function computeGateCountForCircuit(pathToBB, workingDirectory, cir
|
|
|
716
838
|
// Write the bytecode to the working directory
|
|
717
839
|
await fs.writeFile(bytecodePath, bytecode);
|
|
718
840
|
const timer = new Timer();
|
|
719
|
-
const result = await executeBB(pathToBB, flavor === 'mega_honk' ? `gates_for_ivc` : `gates`, [
|
|
841
|
+
const result = await executeBB(pathToBB, flavor === 'mega_honk' ? `gates_for_ivc` : `gates`, [
|
|
842
|
+
'-b',
|
|
843
|
+
bytecodePath,
|
|
844
|
+
'-v'
|
|
845
|
+
], logHandler);
|
|
720
846
|
const duration = timer.ms();
|
|
721
|
-
if (result.status ==
|
|
847
|
+
if (result.status == 0) {
|
|
722
848
|
// Look for "circuit_size" in the stdout and parse the number
|
|
723
849
|
const circuitSizeMatch = stdout.match(/circuit_size": (\d+)/);
|
|
724
850
|
if (!circuitSizeMatch) {
|
|
725
|
-
return {
|
|
851
|
+
return {
|
|
852
|
+
status: 1,
|
|
853
|
+
reason: 'Failed to parse circuit_size from bb gates stdout.'
|
|
854
|
+
};
|
|
726
855
|
}
|
|
727
856
|
const circuitSize = parseInt(circuitSizeMatch[1]);
|
|
728
857
|
return {
|
|
729
|
-
status:
|
|
858
|
+
status: 0,
|
|
730
859
|
durationMs: duration,
|
|
731
|
-
circuitSize: circuitSize
|
|
860
|
+
circuitSize: circuitSize
|
|
732
861
|
};
|
|
733
862
|
}
|
|
734
|
-
return {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
863
|
+
return {
|
|
864
|
+
status: 1,
|
|
865
|
+
reason: 'Failed getting the gate count.'
|
|
866
|
+
};
|
|
867
|
+
} catch (error) {
|
|
868
|
+
return {
|
|
869
|
+
status: 1,
|
|
870
|
+
reason: `${error}`
|
|
871
|
+
};
|
|
738
872
|
}
|
|
739
873
|
}
|
|
740
874
|
const CACHE_FILENAME = '.cache';
|
|
@@ -743,17 +877,14 @@ async function fsCache(dir, expectedCacheKey, logger, force, action) {
|
|
|
743
877
|
let run;
|
|
744
878
|
if (force) {
|
|
745
879
|
run = true;
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
880
|
+
} else {
|
|
748
881
|
try {
|
|
749
882
|
run = !expectedCacheKey.equals(await fs.readFile(cacheFilePath));
|
|
750
|
-
}
|
|
751
|
-
catch (err) {
|
|
883
|
+
} catch (err) {
|
|
752
884
|
if (err && 'code' in err && err.code === 'ENOENT') {
|
|
753
885
|
// cache file doesn't exist, swallow error and run
|
|
754
886
|
run = true;
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
887
|
+
} else {
|
|
757
888
|
throw err;
|
|
758
889
|
}
|
|
759
890
|
}
|
|
@@ -762,17 +893,14 @@ async function fsCache(dir, expectedCacheKey, logger, force, action) {
|
|
|
762
893
|
if (run) {
|
|
763
894
|
logger(`Cache miss or forced run. Running operation in ${dir}...`);
|
|
764
895
|
res = await action();
|
|
765
|
-
}
|
|
766
|
-
else {
|
|
896
|
+
} else {
|
|
767
897
|
logger(`Cache hit. Skipping operation in ${dir}...`);
|
|
768
898
|
}
|
|
769
899
|
try {
|
|
770
900
|
await fs.writeFile(cacheFilePath, expectedCacheKey);
|
|
771
|
-
}
|
|
772
|
-
catch (err) {
|
|
901
|
+
} catch (err) {
|
|
773
902
|
logger(`Couldn't write cache data to ${cacheFilePath}. Skipping cache...`);
|
|
774
|
-
|
|
903
|
+
// ignore
|
|
775
904
|
}
|
|
776
905
|
return res;
|
|
777
906
|
}
|
|
778
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhlY3V0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iYi9leGVjdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBeUIsd0JBQXdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFbEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWhELE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxRQUFRLElBQUksRUFBRSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUcvQyxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUUxRyxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ2hDLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLGdCQUFnQixDQUFDO0FBQ25ELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUM7QUFDdEMsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsbUJBQW1CLENBQUM7QUFDekQsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLENBQUM7QUFDeEQsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsdUJBQXVCLENBQUM7QUFDbEUsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDO0FBRWxELE1BQU0sQ0FBTixJQUFZLFNBSVg7QUFKRCxXQUFZLFNBQVM7SUFDbkIsK0NBQU8sQ0FBQTtJQUNQLCtDQUFPLENBQUE7SUFDUCwrREFBZSxDQUFBO0FBQ2pCLENBQUMsRUFKVyxTQUFTLEtBQVQsU0FBUyxRQUlwQjtBQWlDRDs7Ozs7Ozs7R0FRRztBQUNI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
|