@aztec/bb-prover 0.76.4 → 0.77.0-testnet-ignition.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/avm_proving_tests/avm_proving_tester.d.ts +8 -8
- package/dest/avm_proving_tests/avm_proving_tester.d.ts.map +1 -1
- package/dest/avm_proving_tests/avm_proving_tester.js +24 -25
- package/dest/bb/cli.d.ts +3 -3
- package/dest/bb/cli.d.ts.map +1 -1
- package/dest/bb/cli.js +4 -9
- package/dest/bb/execute.d.ts +7 -37
- package/dest/bb/execute.d.ts.map +1 -1
- package/dest/bb/execute.js +359 -357
- package/dest/bb/index.js +4 -6
- package/dest/config.js +1 -2
- package/dest/honk.d.ts +1 -1
- package/dest/honk.d.ts.map +1 -1
- package/dest/honk.js +8 -5
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +0 -1
- package/dest/instrumentation.d.ts +2 -2
- package/dest/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation.js +50 -47
- package/dest/prover/bb_native_private_kernel_prover.d.ts +4 -4
- package/dest/prover/bb_native_private_kernel_prover.d.ts.map +1 -1
- package/dest/prover/bb_native_private_kernel_prover.js +19 -19
- package/dest/prover/bb_private_kernel_prover.d.ts +7 -6
- package/dest/prover/bb_private_kernel_prover.d.ts.map +1 -1
- package/dest/prover/bb_private_kernel_prover.js +14 -15
- package/dest/prover/bb_prover.d.ts +7 -10
- package/dest/prover/bb_prover.d.ts.map +1 -1
- package/dest/prover/bb_prover.js +402 -447
- package/dest/prover/client_ivc_proof_utils.d.ts +3 -3
- package/dest/prover/client_ivc_proof_utils.d.ts.map +1 -1
- package/dest/prover/client_ivc_proof_utils.js +18 -12
- package/dest/prover/index.js +0 -1
- package/dest/stats.d.ts +2 -2
- package/dest/stats.d.ts.map +1 -1
- package/dest/stats.js +15 -14
- package/dest/test/delay_values.d.ts +4 -0
- package/dest/test/delay_values.d.ts.map +1 -0
- package/dest/test/delay_values.js +29 -0
- package/dest/test/index.js +0 -1
- package/dest/test/test_circuit_prover.d.ts +16 -7
- package/dest/test/test_circuit_prover.d.ts.map +1 -1
- package/dest/test/test_circuit_prover.js +165 -161
- package/dest/test/test_verifier.d.ts +2 -1
- package/dest/test/test_verifier.d.ts.map +1 -1
- package/dest/test/test_verifier.js +0 -1
- package/dest/verification_key/verification_key_data.d.ts +1 -1
- package/dest/verification_key/verification_key_data.d.ts.map +1 -1
- package/dest/verification_key/verification_key_data.js +13 -9
- package/dest/verifier/bb_verifier.d.ts +6 -4
- package/dest/verifier/bb_verifier.d.ts.map +1 -1
- package/dest/verifier/bb_verifier.js +18 -18
- package/dest/verifier/index.js +0 -1
- package/dest/wasm/bb_wasm_private_kernel_prover.d.ts +5 -4
- package/dest/wasm/bb_wasm_private_kernel_prover.d.ts.map +1 -1
- package/dest/wasm/bb_wasm_private_kernel_prover.js +26 -11
- package/dest/wasm/bundle.d.ts +1 -1
- package/dest/wasm/bundle.d.ts.map +1 -1
- package/dest/wasm/bundle.js +1 -2
- package/dest/wasm/lazy.d.ts +1 -1
- package/dest/wasm/lazy.d.ts.map +1 -1
- package/dest/wasm/lazy.js +1 -2
- package/package.json +18 -19
- package/src/avm_proving_tests/avm_proving_tester.ts +17 -33
- package/src/bb/cli.ts +3 -3
- package/src/bb/execute.ts +80 -211
- package/src/honk.ts +1 -1
- package/src/index.ts +1 -1
- package/src/instrumentation.ts +10 -10
- package/src/prover/bb_native_private_kernel_prover.ts +5 -4
- package/src/prover/bb_private_kernel_prover.ts +20 -20
- package/src/prover/bb_prover.ts +33 -100
- package/src/prover/client_ivc_proof_utils.ts +3 -3
- package/src/stats.ts +2 -2
- package/src/test/delay_values.ts +31 -0
- package/src/test/test_circuit_prover.ts +149 -120
- package/src/test/test_verifier.ts +2 -1
- package/src/verification_key/verification_key_data.ts +4 -7
- package/src/verifier/bb_verifier.ts +16 -9
- package/src/wasm/bb_wasm_private_kernel_prover.ts +18 -5
- package/src/wasm/bundle.ts +1 -1
- package/src/wasm/lazy.ts +1 -1
package/dest/bb/execute.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { serializeWithMessagePack } from '@aztec/circuits.js';
|
|
2
1
|
import { sha256 } from '@aztec/foundation/crypto';
|
|
3
2
|
import { Timer } from '@aztec/foundation/timer';
|
|
4
3
|
import * as proc from 'child_process';
|
|
@@ -9,15 +8,16 @@ export const VK_FILENAME = 'vk';
|
|
|
9
8
|
export const VK_FIELDS_FILENAME = 'vk_fields.json';
|
|
10
9
|
export const PROOF_FILENAME = 'proof';
|
|
11
10
|
export const PROOF_FIELDS_FILENAME = 'proof_fields.json';
|
|
11
|
+
export const AVM_INPUTS_FILENAME = 'avm_inputs.bin';
|
|
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
|
|
@@ -88,94 +103,66 @@ export async function executeBbClientIvcProof(pathToBB, workingDirectory, byteco
|
|
|
88
103
|
'client_ivc',
|
|
89
104
|
'--input_type',
|
|
90
105
|
'runtime_stack',
|
|
106
|
+
'--write_vk'
|
|
91
107
|
];
|
|
92
108
|
const timer = new Timer();
|
|
93
|
-
const logFunction = (message)
|
|
109
|
+
const logFunction = (message)=>{
|
|
94
110
|
log(`bb - ${message}`);
|
|
95
111
|
};
|
|
96
112
|
const result = await executeBB(pathToBB, 'prove', args, logFunction);
|
|
97
113
|
const durationMs = timer.ms();
|
|
98
|
-
if (result.status ==
|
|
114
|
+
if (result.status == 0) {
|
|
99
115
|
return {
|
|
100
|
-
status:
|
|
116
|
+
status: 0,
|
|
101
117
|
durationMs,
|
|
102
118
|
proofPath: `${outputPath}`,
|
|
103
119
|
pkPath: undefined,
|
|
104
|
-
vkPath: `${outputPath}
|
|
120
|
+
vkPath: `${outputPath}`
|
|
105
121
|
};
|
|
106
122
|
}
|
|
107
123
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
108
124
|
return {
|
|
109
|
-
status:
|
|
125
|
+
status: 1,
|
|
110
126
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
111
|
-
retry: !!result.signal
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Used for generating verification keys of noir circuits.
|
|
120
|
-
* It is assumed that the working directory is a temporary and/or random directory used solely for generating this VK.
|
|
121
|
-
* @param pathToBB - The full path to the bb binary
|
|
122
|
-
* @param workingDirectory - A working directory for use by bb
|
|
123
|
-
* @param circuitName - An identifier for the circuit
|
|
124
|
-
* @param bytecode - The compiled circuit bytecode
|
|
125
|
-
* @param inputWitnessFile - The circuit input witness
|
|
126
|
-
* @param log - A logging function
|
|
127
|
-
* @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) {
|
|
130
|
-
// Check that the working directory exists
|
|
131
|
-
try {
|
|
132
|
-
await fs.access(workingDirectory);
|
|
133
|
-
}
|
|
134
|
-
catch (error) {
|
|
135
|
-
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
|
|
136
|
-
}
|
|
137
|
-
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
138
|
-
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
139
|
-
// The verification key is written to this path
|
|
140
|
-
const outputPath = `${workingDirectory}/vk`;
|
|
141
|
-
const binaryPresent = await fs
|
|
142
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
143
|
-
.then(_ => true)
|
|
144
|
-
.catch(_ => false);
|
|
145
|
-
if (!binaryPresent) {
|
|
146
|
-
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
147
|
-
}
|
|
148
|
-
try {
|
|
149
|
-
// Write the bytecode to the working directory
|
|
150
|
-
await fs.writeFile(bytecodePath, bytecode);
|
|
151
|
-
const timer = new Timer();
|
|
152
|
-
const logFunction = (message) => {
|
|
153
|
-
log(`computeVerificationKey(${circuitName}) BB out - ${message}`);
|
|
127
|
+
retry: !!result.signal
|
|
154
128
|
};
|
|
155
|
-
|
|
156
|
-
let result = await executeBB(pathToBB, `write_vk_${flavor}`, args, logFunction);
|
|
157
|
-
if (result.status == BB_RESULT.FAILURE) {
|
|
158
|
-
return { status: BB_RESULT.FAILURE, reason: 'Failed writing VK.' };
|
|
159
|
-
}
|
|
160
|
-
result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, ['-o', outputPath + '_fields.json', '-k', outputPath, '-v'], logFunction);
|
|
161
|
-
const duration = timer.ms();
|
|
162
|
-
if (result.status == BB_RESULT.SUCCESS) {
|
|
163
|
-
return {
|
|
164
|
-
status: BB_RESULT.SUCCESS,
|
|
165
|
-
durationMs: duration,
|
|
166
|
-
pkPath: undefined,
|
|
167
|
-
vkPath: `${outputPath}`,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
129
|
+
} catch (error) {
|
|
171
130
|
return {
|
|
172
|
-
status:
|
|
173
|
-
reason:
|
|
174
|
-
retry: !!result.signal,
|
|
131
|
+
status: 1,
|
|
132
|
+
reason: `${error}`
|
|
175
133
|
};
|
|
176
134
|
}
|
|
177
|
-
|
|
178
|
-
|
|
135
|
+
}
|
|
136
|
+
function getArgs(flavor) {
|
|
137
|
+
switch(flavor){
|
|
138
|
+
case 'ultra_honk':
|
|
139
|
+
{
|
|
140
|
+
return [
|
|
141
|
+
'--scheme',
|
|
142
|
+
'ultra_honk',
|
|
143
|
+
'--oracle_hash',
|
|
144
|
+
'poseidon2'
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
case 'ultra_keccak_honk':
|
|
148
|
+
{
|
|
149
|
+
return [
|
|
150
|
+
'--scheme',
|
|
151
|
+
'ultra_honk',
|
|
152
|
+
'--oracle_hash',
|
|
153
|
+
'keccak'
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
case 'ultra_rollup_honk':
|
|
157
|
+
{
|
|
158
|
+
return [
|
|
159
|
+
'--scheme',
|
|
160
|
+
'ultra_honk',
|
|
161
|
+
'--oracle_hash',
|
|
162
|
+
'poseidon2',
|
|
163
|
+
'--ipa_accumulation'
|
|
164
|
+
];
|
|
165
|
+
}
|
|
179
166
|
}
|
|
180
167
|
}
|
|
181
168
|
/**
|
|
@@ -188,54 +175,71 @@ export async function computeVerificationKey(pathToBB, workingDirectory, circuit
|
|
|
188
175
|
* @param inputWitnessFile - The circuit input witness
|
|
189
176
|
* @param log - A logging function
|
|
190
177
|
* @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) {
|
|
178
|
+
*/ export async function generateProof(pathToBB, workingDirectory, circuitName, bytecode, recursive, inputWitnessFile, flavor, log) {
|
|
193
179
|
// Check that the working directory exists
|
|
194
180
|
try {
|
|
195
181
|
await fs.access(workingDirectory);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
|
|
182
|
+
} catch (error) {
|
|
183
|
+
return {
|
|
184
|
+
status: 1,
|
|
185
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
186
|
+
};
|
|
199
187
|
}
|
|
200
188
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
201
189
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
202
190
|
// The proof is written to e.g. /workingDirectory/ultra_honk/proof
|
|
203
191
|
const outputPath = `${workingDirectory}`;
|
|
204
|
-
const binaryPresent = await fs
|
|
205
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
206
|
-
.then(_ => true)
|
|
207
|
-
.catch(_ => false);
|
|
192
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
208
193
|
if (!binaryPresent) {
|
|
209
|
-
return {
|
|
194
|
+
return {
|
|
195
|
+
status: 1,
|
|
196
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
197
|
+
};
|
|
210
198
|
}
|
|
211
199
|
try {
|
|
212
200
|
// Write the bytecode to the working directory
|
|
213
201
|
await fs.writeFile(bytecodePath, bytecode);
|
|
214
|
-
const args = [
|
|
202
|
+
const args = getArgs(flavor).concat([
|
|
203
|
+
'--output_format',
|
|
204
|
+
'bytes_and_fields',
|
|
205
|
+
'--write_vk',
|
|
206
|
+
'-o',
|
|
207
|
+
outputPath,
|
|
208
|
+
'-b',
|
|
209
|
+
bytecodePath,
|
|
210
|
+
'-w',
|
|
211
|
+
inputWitnessFile,
|
|
212
|
+
'-v'
|
|
213
|
+
]);
|
|
214
|
+
if (recursive) {
|
|
215
|
+
args.push('--init_kzg_accumulator');
|
|
216
|
+
}
|
|
215
217
|
const timer = new Timer();
|
|
216
|
-
const logFunction = (message)
|
|
218
|
+
const logFunction = (message)=>{
|
|
217
219
|
log(`${circuitName} BB out - ${message}`);
|
|
218
220
|
};
|
|
219
|
-
const result = await executeBB(pathToBB, `
|
|
221
|
+
const result = await executeBB(pathToBB, `prove`, args, logFunction);
|
|
220
222
|
const duration = timer.ms();
|
|
221
|
-
if (result.status ==
|
|
223
|
+
if (result.status == 0) {
|
|
222
224
|
return {
|
|
223
|
-
status:
|
|
225
|
+
status: 0,
|
|
224
226
|
durationMs: duration,
|
|
225
227
|
proofPath: `${outputPath}`,
|
|
226
228
|
pkPath: undefined,
|
|
227
|
-
vkPath: `${outputPath}
|
|
229
|
+
vkPath: `${outputPath}`
|
|
228
230
|
};
|
|
229
231
|
}
|
|
230
232
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
231
233
|
return {
|
|
232
|
-
status:
|
|
234
|
+
status: 1,
|
|
233
235
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
234
|
-
retry: !!result.signal
|
|
236
|
+
retry: !!result.signal
|
|
237
|
+
};
|
|
238
|
+
} catch (error) {
|
|
239
|
+
return {
|
|
240
|
+
status: 1,
|
|
241
|
+
reason: `${error}`
|
|
235
242
|
};
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
239
243
|
}
|
|
240
244
|
}
|
|
241
245
|
/**
|
|
@@ -248,57 +252,67 @@ export async function generateProof(pathToBB, workingDirectory, circuitName, byt
|
|
|
248
252
|
* @param inputWitnessFile - The circuit input witness
|
|
249
253
|
* @param log - A logging function
|
|
250
254
|
* @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) {
|
|
255
|
+
*/ export async function generateTubeProof(pathToBB, workingDirectory, log) {
|
|
253
256
|
// Check that the working directory exists
|
|
254
257
|
try {
|
|
255
258
|
await fs.access(workingDirectory);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
status: 1,
|
|
262
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
263
|
+
};
|
|
259
264
|
}
|
|
260
265
|
// // Paths for the inputs
|
|
261
266
|
const vkPath = join(workingDirectory, CLIENT_IVC_VK_FILE_NAME);
|
|
262
267
|
const proofPath = join(workingDirectory, CLIENT_IVC_PROOF_FILE_NAME);
|
|
263
268
|
// The proof is written to e.g. /workingDirectory/proof
|
|
264
269
|
const outputPath = workingDirectory;
|
|
265
|
-
const filePresent = async (file)
|
|
266
|
-
.access(file, fs.constants.R_OK)
|
|
267
|
-
.then(_ => true)
|
|
268
|
-
.catch(_ => false);
|
|
270
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
269
271
|
const binaryPresent = await filePresent(pathToBB);
|
|
270
272
|
if (!binaryPresent) {
|
|
271
|
-
return {
|
|
273
|
+
return {
|
|
274
|
+
status: 1,
|
|
275
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
276
|
+
};
|
|
272
277
|
}
|
|
273
278
|
try {
|
|
274
|
-
if (!
|
|
275
|
-
return {
|
|
279
|
+
if (!await filePresent(vkPath) || !await filePresent(proofPath)) {
|
|
280
|
+
return {
|
|
281
|
+
status: 1,
|
|
282
|
+
reason: `Client IVC input files not present in ${workingDirectory}`
|
|
283
|
+
};
|
|
276
284
|
}
|
|
277
|
-
const args = [
|
|
285
|
+
const args = [
|
|
286
|
+
'-o',
|
|
287
|
+
outputPath,
|
|
288
|
+
'-v'
|
|
289
|
+
];
|
|
278
290
|
const timer = new Timer();
|
|
279
|
-
const logFunction = (message)
|
|
291
|
+
const logFunction = (message)=>{
|
|
280
292
|
log(`TubeCircuit (prove) BB out - ${message}`);
|
|
281
293
|
};
|
|
282
294
|
const result = await executeBB(pathToBB, 'prove_tube', args, logFunction);
|
|
283
295
|
const durationMs = timer.ms();
|
|
284
|
-
if (result.status ==
|
|
296
|
+
if (result.status == 0) {
|
|
285
297
|
return {
|
|
286
|
-
status:
|
|
298
|
+
status: 0,
|
|
287
299
|
durationMs,
|
|
288
300
|
proofPath: outputPath,
|
|
289
301
|
pkPath: undefined,
|
|
290
|
-
vkPath: outputPath
|
|
302
|
+
vkPath: outputPath
|
|
291
303
|
};
|
|
292
304
|
}
|
|
293
305
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
294
306
|
return {
|
|
295
|
-
status:
|
|
307
|
+
status: 1,
|
|
296
308
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
297
|
-
retry: !!result.signal
|
|
309
|
+
retry: !!result.signal
|
|
310
|
+
};
|
|
311
|
+
} catch (error) {
|
|
312
|
+
return {
|
|
313
|
+
status: 1,
|
|
314
|
+
reason: `${error}`
|
|
298
315
|
};
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
302
316
|
}
|
|
303
317
|
}
|
|
304
318
|
/**
|
|
@@ -309,64 +323,73 @@ export async function generateTubeProof(pathToBB, workingDirectory, log) {
|
|
|
309
323
|
* @param input - The inputs for the public function to be proven
|
|
310
324
|
* @param log - A logging function
|
|
311
325
|
* @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) {
|
|
326
|
+
*/ export async function generateAvmProofV2(pathToBB, workingDirectory, input, logger) {
|
|
314
327
|
// Check that the working directory exists
|
|
315
328
|
try {
|
|
316
329
|
await fs.access(workingDirectory);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
330
|
+
} catch (error) {
|
|
331
|
+
return {
|
|
332
|
+
status: 1,
|
|
333
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
334
|
+
};
|
|
320
335
|
}
|
|
321
336
|
// The proof is written to e.g. /workingDirectory/proof
|
|
322
337
|
const outputPath = workingDirectory;
|
|
323
|
-
const filePresent = async (file)
|
|
324
|
-
.access(file, fs.constants.R_OK)
|
|
325
|
-
.then(_ => true)
|
|
326
|
-
.catch(_ => false);
|
|
338
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
327
339
|
const binaryPresent = await filePresent(pathToBB);
|
|
328
340
|
if (!binaryPresent) {
|
|
329
|
-
return {
|
|
341
|
+
return {
|
|
342
|
+
status: 1,
|
|
343
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
344
|
+
};
|
|
330
345
|
}
|
|
331
|
-
const inputsBuffer = input.
|
|
346
|
+
const inputsBuffer = input.serializeWithMessagePack();
|
|
332
347
|
try {
|
|
333
348
|
// Write the inputs to the working directory.
|
|
334
|
-
const avmInputsPath = join(workingDirectory,
|
|
349
|
+
const avmInputsPath = join(workingDirectory, AVM_INPUTS_FILENAME);
|
|
335
350
|
await fs.writeFile(avmInputsPath, inputsBuffer);
|
|
336
|
-
if (!
|
|
337
|
-
return {
|
|
351
|
+
if (!await filePresent(avmInputsPath)) {
|
|
352
|
+
return {
|
|
353
|
+
status: 1,
|
|
354
|
+
reason: `Could not write avm inputs to ${avmInputsPath}`
|
|
355
|
+
};
|
|
338
356
|
}
|
|
339
357
|
const args = [
|
|
340
358
|
'--avm-inputs',
|
|
341
359
|
avmInputsPath,
|
|
342
360
|
'-o',
|
|
343
|
-
outputPath
|
|
344
|
-
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
361
|
+
outputPath
|
|
345
362
|
];
|
|
363
|
+
const loggingArg = logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '';
|
|
364
|
+
if (loggingArg !== '') {
|
|
365
|
+
args.push(loggingArg);
|
|
366
|
+
}
|
|
346
367
|
const timer = new Timer();
|
|
347
|
-
const logFunction = (message)
|
|
368
|
+
const logFunction = (message)=>{
|
|
348
369
|
logger.verbose(`AvmCircuit (prove) BB out - ${message}`);
|
|
349
370
|
};
|
|
350
371
|
const result = await executeBB(pathToBB, 'avm2_prove', args, logFunction);
|
|
351
372
|
const duration = timer.ms();
|
|
352
|
-
if (result.status ==
|
|
373
|
+
if (result.status == 0) {
|
|
353
374
|
return {
|
|
354
|
-
status:
|
|
375
|
+
status: 0,
|
|
355
376
|
durationMs: duration,
|
|
356
377
|
proofPath: join(outputPath, PROOF_FILENAME),
|
|
357
378
|
pkPath: undefined,
|
|
358
|
-
vkPath: outputPath
|
|
379
|
+
vkPath: outputPath
|
|
359
380
|
};
|
|
360
381
|
}
|
|
361
382
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
362
383
|
return {
|
|
363
|
-
status:
|
|
384
|
+
status: 1,
|
|
364
385
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
365
|
-
retry: !!result.signal
|
|
386
|
+
retry: !!result.signal
|
|
387
|
+
};
|
|
388
|
+
} catch (error) {
|
|
389
|
+
return {
|
|
390
|
+
status: 1,
|
|
391
|
+
reason: `${error}`
|
|
366
392
|
};
|
|
367
|
-
}
|
|
368
|
-
catch (error) {
|
|
369
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
370
393
|
}
|
|
371
394
|
}
|
|
372
395
|
/**
|
|
@@ -377,73 +400,79 @@ export async function generateAvmProofV2(pathToBB, workingDirectory, input, logg
|
|
|
377
400
|
* @param bytecode - The AVM bytecode for the public function to be proven (expected to be decompressed)
|
|
378
401
|
* @param log - A logging function
|
|
379
402
|
* @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) {
|
|
403
|
+
*/ export async function generateAvmProof(pathToBB, workingDirectory, _input, logger, checkCircuitOnly = false) {
|
|
382
404
|
// Check that the working directory exists
|
|
383
405
|
try {
|
|
384
406
|
await fs.access(workingDirectory);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
407
|
+
} catch (error) {
|
|
408
|
+
return {
|
|
409
|
+
status: 1,
|
|
410
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
411
|
+
};
|
|
388
412
|
}
|
|
389
413
|
// Paths for the inputs
|
|
390
414
|
const publicInputsPath = join(workingDirectory, AVM_PUBLIC_INPUTS_FILENAME);
|
|
391
415
|
const avmHintsPath = join(workingDirectory, AVM_HINTS_FILENAME);
|
|
392
416
|
// The proof is written to e.g. /workingDirectory/proof
|
|
393
417
|
const outputPath = workingDirectory;
|
|
394
|
-
const filePresent = async (file)
|
|
395
|
-
.access(file, fs.constants.R_OK)
|
|
396
|
-
.then(_ => true)
|
|
397
|
-
.catch(_ => false);
|
|
418
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
398
419
|
const binaryPresent = await filePresent(pathToBB);
|
|
399
420
|
if (!binaryPresent) {
|
|
400
|
-
return {
|
|
421
|
+
return {
|
|
422
|
+
status: 1,
|
|
423
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
424
|
+
};
|
|
401
425
|
}
|
|
402
426
|
try {
|
|
403
427
|
// Write the inputs to the working directory.
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
428
|
+
// WARNING: Not writing the inputs since VM1 is disabled!
|
|
429
|
+
// await fs.writeFile(publicInputsPath, input.publicInputs.toBuffer());
|
|
430
|
+
// if (!(await filePresent(publicInputsPath))) {
|
|
431
|
+
// return { status: BB_RESULT.FAILURE, reason: `Could not write publicInputs at ${publicInputsPath}` };
|
|
432
|
+
// }
|
|
433
|
+
// await fs.writeFile(avmHintsPath, input.avmHints.toBuffer());
|
|
434
|
+
// if (!(await filePresent(avmHintsPath))) {
|
|
435
|
+
// return { status: BB_RESULT.FAILURE, reason: `Could not write avmHints at ${avmHintsPath}` };
|
|
436
|
+
// }
|
|
412
437
|
const args = [
|
|
413
438
|
'--avm-public-inputs',
|
|
414
439
|
publicInputsPath,
|
|
415
440
|
'--avm-hints',
|
|
416
441
|
avmHintsPath,
|
|
417
442
|
'-o',
|
|
418
|
-
outputPath
|
|
419
|
-
logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '',
|
|
420
|
-
checkCircuitOnly ? '--check-circuit-only' : '',
|
|
443
|
+
outputPath
|
|
421
444
|
];
|
|
445
|
+
const loggingArg = logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '';
|
|
446
|
+
if (loggingArg !== '') {
|
|
447
|
+
args.push(loggingArg);
|
|
448
|
+
}
|
|
422
449
|
const timer = new Timer();
|
|
423
450
|
const cmd = checkCircuitOnly ? 'check_circuit' : 'prove';
|
|
424
|
-
const logFunction = (message)
|
|
451
|
+
const logFunction = (message)=>{
|
|
425
452
|
logger.verbose(`AvmCircuit (${cmd}) BB out - ${message}`);
|
|
426
453
|
};
|
|
427
454
|
const result = await executeBB(pathToBB, `avm_${cmd}`, args, logFunction);
|
|
428
455
|
const duration = timer.ms();
|
|
429
|
-
if (result.status ==
|
|
456
|
+
if (result.status == 0) {
|
|
430
457
|
return {
|
|
431
|
-
status:
|
|
458
|
+
status: 0,
|
|
432
459
|
durationMs: duration,
|
|
433
460
|
proofPath: join(outputPath, PROOF_FILENAME),
|
|
434
461
|
pkPath: undefined,
|
|
435
|
-
vkPath: outputPath
|
|
462
|
+
vkPath: outputPath
|
|
436
463
|
};
|
|
437
464
|
}
|
|
438
465
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
439
466
|
return {
|
|
440
|
-
status:
|
|
467
|
+
status: 1,
|
|
441
468
|
reason: `Failed to generate proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
442
|
-
retry: !!result.signal
|
|
469
|
+
retry: !!result.signal
|
|
470
|
+
};
|
|
471
|
+
} catch (error) {
|
|
472
|
+
return {
|
|
473
|
+
status: 1,
|
|
474
|
+
reason: `${error}`
|
|
443
475
|
};
|
|
444
|
-
}
|
|
445
|
-
catch (error) {
|
|
446
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
447
476
|
}
|
|
448
477
|
}
|
|
449
478
|
/**
|
|
@@ -453,9 +482,8 @@ export async function generateAvmProof(pathToBB, workingDirectory, input, logger
|
|
|
453
482
|
* @param verificationKeyPath - The full path to the circuit verification key
|
|
454
483
|
* @param log - A logging function
|
|
455
484
|
* @returns An object containing a result indication and duration taken
|
|
456
|
-
*/
|
|
457
|
-
|
|
458
|
-
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, `verify_${ultraHonkFlavor}`, log);
|
|
485
|
+
*/ export async function verifyProof(pathToBB, proofFullPath, verificationKeyPath, ultraHonkFlavor, log) {
|
|
486
|
+
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, `verify`, log, getArgs(ultraHonkFlavor));
|
|
459
487
|
}
|
|
460
488
|
/**
|
|
461
489
|
* Used for verifying proofs of the AVM
|
|
@@ -464,25 +492,24 @@ export async function verifyProof(pathToBB, proofFullPath, verificationKeyPath,
|
|
|
464
492
|
* @param verificationKeyPath - The full path to the circuit verification key
|
|
465
493
|
* @param log - A logging function
|
|
466
494
|
* @returns An object containing a result indication and duration taken
|
|
467
|
-
*/
|
|
468
|
-
export async function verifyAvmProof(pathToBB, proofFullPath, verificationKeyPath, logger) {
|
|
495
|
+
*/ export async function verifyAvmProof(pathToBB, proofFullPath, verificationKeyPath, logger) {
|
|
469
496
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm_verify', logger);
|
|
470
497
|
}
|
|
471
498
|
export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath, publicInputs, verificationKeyPath, logger) {
|
|
472
|
-
const inputsBuffer = serializeWithMessagePack(
|
|
499
|
+
const inputsBuffer = publicInputs.serializeWithMessagePack();
|
|
473
500
|
// 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);
|
|
501
|
+
const filePresent = async (file)=>await fs.access(file, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
478
502
|
const avmInputsPath = join(workingDirectory, 'avm_public_inputs.bin');
|
|
479
503
|
await fs.writeFile(avmInputsPath, inputsBuffer);
|
|
480
|
-
if (!
|
|
481
|
-
return {
|
|
504
|
+
if (!await filePresent(avmInputsPath)) {
|
|
505
|
+
return {
|
|
506
|
+
status: 1,
|
|
507
|
+
reason: `Could not write avm inputs to ${avmInputsPath}`
|
|
508
|
+
};
|
|
482
509
|
}
|
|
483
510
|
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm2_verify', logger, [
|
|
484
511
|
'--avm-public-inputs',
|
|
485
|
-
avmInputsPath
|
|
512
|
+
avmInputsPath
|
|
486
513
|
]);
|
|
487
514
|
}
|
|
488
515
|
/**
|
|
@@ -492,33 +519,44 @@ export async function verifyAvmProofV2(pathToBB, workingDirectory, proofFullPath
|
|
|
492
519
|
* @param targetPath - The path to the folder with the proof, accumulator, and verification keys
|
|
493
520
|
* @param log - A logging function
|
|
494
521
|
* @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);
|
|
522
|
+
*/ export async function verifyClientIvcProof(pathToBB, proofPath, keyPath, log) {
|
|
523
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
501
524
|
if (!binaryPresent) {
|
|
502
|
-
return {
|
|
525
|
+
return {
|
|
526
|
+
status: 1,
|
|
527
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
528
|
+
};
|
|
503
529
|
}
|
|
504
530
|
try {
|
|
505
|
-
const args = [
|
|
531
|
+
const args = [
|
|
532
|
+
'--scheme',
|
|
533
|
+
'client_ivc',
|
|
534
|
+
'-p',
|
|
535
|
+
proofPath,
|
|
536
|
+
'-k',
|
|
537
|
+
keyPath
|
|
538
|
+
];
|
|
506
539
|
const timer = new Timer();
|
|
507
540
|
const command = 'verify';
|
|
508
541
|
const result = await executeBB(pathToBB, command, args, log);
|
|
509
542
|
const duration = timer.ms();
|
|
510
|
-
if (result.status ==
|
|
511
|
-
return {
|
|
543
|
+
if (result.status == 0) {
|
|
544
|
+
return {
|
|
545
|
+
status: 0,
|
|
546
|
+
durationMs: duration
|
|
547
|
+
};
|
|
512
548
|
}
|
|
513
549
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
514
550
|
return {
|
|
515
|
-
status:
|
|
551
|
+
status: 1,
|
|
516
552
|
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
517
|
-
retry: !!result.signal
|
|
553
|
+
retry: !!result.signal
|
|
554
|
+
};
|
|
555
|
+
} catch (error) {
|
|
556
|
+
return {
|
|
557
|
+
status: 1,
|
|
558
|
+
reason: `${error}`
|
|
518
559
|
};
|
|
519
|
-
}
|
|
520
|
-
catch (error) {
|
|
521
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
522
560
|
}
|
|
523
561
|
}
|
|
524
562
|
/**
|
|
@@ -529,17 +567,16 @@ export async function verifyClientIvcProof(pathToBB, targetPath, log) {
|
|
|
529
567
|
* @param command - The BB command to execute (verify/avm_verify)
|
|
530
568
|
* @param log - A logging function
|
|
531
569
|
* @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);
|
|
570
|
+
*/ async function verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, command, logger, extraArgs = []) {
|
|
571
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
538
572
|
if (!binaryPresent) {
|
|
539
|
-
return {
|
|
573
|
+
return {
|
|
574
|
+
status: 1,
|
|
575
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
576
|
+
};
|
|
540
577
|
}
|
|
541
|
-
const logFunction = (message)
|
|
542
|
-
logger.verbose(`
|
|
578
|
+
const logFunction = (message)=>{
|
|
579
|
+
logger.verbose(`bb-prover (verify) BB out - ${message}`);
|
|
543
580
|
};
|
|
544
581
|
try {
|
|
545
582
|
const args = [
|
|
@@ -547,135 +584,91 @@ async function verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath,
|
|
|
547
584
|
proofFullPath,
|
|
548
585
|
'-k',
|
|
549
586
|
verificationKeyPath,
|
|
550
|
-
|
|
551
|
-
...extraArgs,
|
|
587
|
+
...extraArgs
|
|
552
588
|
];
|
|
589
|
+
const loggingArg = logger.level === 'debug' || logger.level === 'trace' ? '-d' : logger.level === 'verbose' ? '-v' : '';
|
|
590
|
+
if (loggingArg !== '') {
|
|
591
|
+
args.push(loggingArg);
|
|
592
|
+
}
|
|
553
593
|
const timer = new Timer();
|
|
554
594
|
const result = await executeBB(pathToBB, command, args, logFunction);
|
|
555
595
|
const duration = timer.ms();
|
|
556
|
-
if (result.status ==
|
|
557
|
-
return {
|
|
596
|
+
if (result.status == 0) {
|
|
597
|
+
return {
|
|
598
|
+
status: 0,
|
|
599
|
+
durationMs: duration
|
|
600
|
+
};
|
|
558
601
|
}
|
|
559
602
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
560
603
|
return {
|
|
561
|
-
status:
|
|
604
|
+
status: 1,
|
|
562
605
|
reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
563
|
-
retry: !!result.signal
|
|
606
|
+
retry: !!result.signal
|
|
564
607
|
};
|
|
565
|
-
}
|
|
566
|
-
catch (error) {
|
|
567
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
/**
|
|
571
|
-
* Used for verifying proofs of noir circuits
|
|
572
|
-
* @param pathToBB - The full path to the bb binary
|
|
573
|
-
* @param verificationKeyPath - The directory containing the binary verification key
|
|
574
|
-
* @param verificationKeyFilename - The filename of the verification key
|
|
575
|
-
* @param log - A logging function
|
|
576
|
-
* @returns An object containing a result indication and duration taken
|
|
577
|
-
*/
|
|
578
|
-
export async function writeVkAsFields(pathToBB, verificationKeyPath, verificationKeyFilename, log) {
|
|
579
|
-
const binaryPresent = await fs
|
|
580
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
581
|
-
.then(_ => true)
|
|
582
|
-
.catch(_ => false);
|
|
583
|
-
if (!binaryPresent) {
|
|
584
|
-
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
585
|
-
}
|
|
586
|
-
try {
|
|
587
|
-
const args = ['-k', `${verificationKeyPath}/${verificationKeyFilename}`, '-v'];
|
|
588
|
-
const timer = new Timer();
|
|
589
|
-
const result = await executeBB(pathToBB, 'vk_as_fields_ultra_honk', args, log);
|
|
590
|
-
const duration = timer.ms();
|
|
591
|
-
if (result.status == BB_RESULT.SUCCESS) {
|
|
592
|
-
return { status: BB_RESULT.SUCCESS, durationMs: duration, vkPath: verificationKeyPath };
|
|
593
|
-
}
|
|
594
|
-
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
608
|
+
} catch (error) {
|
|
595
609
|
return {
|
|
596
|
-
status:
|
|
597
|
-
reason:
|
|
598
|
-
retry: !!result.signal,
|
|
610
|
+
status: 1,
|
|
611
|
+
reason: `${error}`
|
|
599
612
|
};
|
|
600
613
|
}
|
|
601
|
-
catch (error) {
|
|
602
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
603
|
-
}
|
|
604
614
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
* @param pathToBB - The full path to the bb binary
|
|
608
|
-
* @param proofPath - The directory containing the binary proof
|
|
609
|
-
* @param proofFileName - The filename of the proof
|
|
610
|
-
* @param vkFileName - The filename of the verification key
|
|
611
|
-
* @param log - A logging function
|
|
612
|
-
* @returns An object containing a result indication and duration taken
|
|
613
|
-
*/
|
|
614
|
-
export async function writeProofAsFields(pathToBB, proofPath, proofFileName, vkFilePath, log) {
|
|
615
|
-
const binaryPresent = await fs
|
|
616
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
617
|
-
.then(_ => true)
|
|
618
|
-
.catch(_ => false);
|
|
615
|
+
export async function generateContractForVerificationKey(pathToBB, vkFilePath, contractPath, log) {
|
|
616
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
619
617
|
if (!binaryPresent) {
|
|
620
|
-
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
621
|
-
}
|
|
622
|
-
try {
|
|
623
|
-
const args = ['-p', `${proofPath}/${proofFileName}`, '-k', vkFilePath, '-v'];
|
|
624
|
-
const timer = new Timer();
|
|
625
|
-
const result = await executeBB(pathToBB, 'proof_as_fields_honk', args, log);
|
|
626
|
-
const duration = timer.ms();
|
|
627
|
-
if (result.status == BB_RESULT.SUCCESS) {
|
|
628
|
-
return { status: BB_RESULT.SUCCESS, durationMs: duration, proofPath: proofPath };
|
|
629
|
-
}
|
|
630
|
-
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
631
618
|
return {
|
|
632
|
-
status:
|
|
633
|
-
reason: `Failed to
|
|
634
|
-
retry: !!result.signal,
|
|
619
|
+
status: 1,
|
|
620
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
635
621
|
};
|
|
636
622
|
}
|
|
637
|
-
catch (error) {
|
|
638
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
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);
|
|
646
|
-
if (!binaryPresent) {
|
|
647
|
-
return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` };
|
|
648
|
-
}
|
|
649
623
|
const outputDir = dirname(contractPath);
|
|
650
624
|
const contractName = basename(contractPath);
|
|
651
625
|
// cache contract generation based on vk file and contract name
|
|
652
|
-
const cacheKey = sha256(Buffer.concat([
|
|
653
|
-
|
|
654
|
-
|
|
626
|
+
const cacheKey = sha256(Buffer.concat([
|
|
627
|
+
Buffer.from(contractName),
|
|
628
|
+
await fs.readFile(vkFilePath)
|
|
629
|
+
]));
|
|
630
|
+
await fs.mkdir(outputDir, {
|
|
631
|
+
recursive: true
|
|
632
|
+
});
|
|
633
|
+
const res = await fsCache(outputDir, cacheKey, log, false, async ()=>{
|
|
655
634
|
try {
|
|
656
|
-
const args = [
|
|
635
|
+
const args = [
|
|
636
|
+
'--scheme',
|
|
637
|
+
'ultra_honk',
|
|
638
|
+
'-k',
|
|
639
|
+
vkFilePath,
|
|
640
|
+
'-o',
|
|
641
|
+
contractPath,
|
|
642
|
+
'-v'
|
|
643
|
+
];
|
|
657
644
|
const timer = new Timer();
|
|
658
|
-
const result = await executeBB(pathToBB, '
|
|
645
|
+
const result = await executeBB(pathToBB, 'contract', args, log);
|
|
659
646
|
const duration = timer.ms();
|
|
660
|
-
if (result.status ==
|
|
661
|
-
return {
|
|
647
|
+
if (result.status == 0) {
|
|
648
|
+
return {
|
|
649
|
+
status: 0,
|
|
650
|
+
durationMs: duration,
|
|
651
|
+
contractPath
|
|
652
|
+
};
|
|
662
653
|
}
|
|
663
654
|
// Not a great error message here but it is difficult to decipher what comes from bb
|
|
664
655
|
return {
|
|
665
|
-
status:
|
|
656
|
+
status: 1,
|
|
666
657
|
reason: `Failed to write verifier contract. Exit code ${result.exitCode}. Signal ${result.signal}.`,
|
|
667
|
-
retry: !!result.signal
|
|
658
|
+
retry: !!result.signal
|
|
659
|
+
};
|
|
660
|
+
} catch (error) {
|
|
661
|
+
return {
|
|
662
|
+
status: 1,
|
|
663
|
+
reason: `${error}`
|
|
668
664
|
};
|
|
669
|
-
}
|
|
670
|
-
catch (error) {
|
|
671
|
-
return { status: BB_RESULT.FAILURE, reason: `${error}` };
|
|
672
665
|
}
|
|
673
666
|
});
|
|
674
667
|
if (!res) {
|
|
675
668
|
return {
|
|
676
|
-
status:
|
|
669
|
+
status: 2,
|
|
677
670
|
durationMs: 0,
|
|
678
|
-
contractPath
|
|
671
|
+
contractPath
|
|
679
672
|
};
|
|
680
673
|
}
|
|
681
674
|
return res;
|
|
@@ -688,27 +681,28 @@ export async function generateContractForVerificationKey(pathToBB, vkFilePath, c
|
|
|
688
681
|
* @param bytecode - The bytecode of the circuit
|
|
689
682
|
* @param flavor - The flavor of the backend - mega_honk or ultra_honk variants
|
|
690
683
|
* @returns An object containing the status, gate count, and time taken
|
|
691
|
-
*/
|
|
692
|
-
export async function computeGateCountForCircuit(pathToBB, workingDirectory, circuitName, bytecode, flavor, log) {
|
|
684
|
+
*/ export async function computeGateCountForCircuit(pathToBB, workingDirectory, circuitName, bytecode, flavor, log) {
|
|
693
685
|
// Check that the working directory exists
|
|
694
686
|
try {
|
|
695
687
|
await fs.access(workingDirectory);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
|
|
688
|
+
} catch (error) {
|
|
689
|
+
return {
|
|
690
|
+
status: 1,
|
|
691
|
+
reason: `Working directory ${workingDirectory} does not exist`
|
|
692
|
+
};
|
|
699
693
|
}
|
|
700
694
|
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
|
|
701
695
|
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;
|
|
702
|
-
const binaryPresent = await fs
|
|
703
|
-
.access(pathToBB, fs.constants.R_OK)
|
|
704
|
-
.then(_ => true)
|
|
705
|
-
.catch(_ => false);
|
|
696
|
+
const binaryPresent = await fs.access(pathToBB, fs.constants.R_OK).then((_)=>true).catch((_)=>false);
|
|
706
697
|
if (!binaryPresent) {
|
|
707
|
-
return {
|
|
698
|
+
return {
|
|
699
|
+
status: 1,
|
|
700
|
+
reason: `Failed to find bb binary at ${pathToBB}`
|
|
701
|
+
};
|
|
708
702
|
}
|
|
709
703
|
// Accumulate the stdout from bb
|
|
710
704
|
let stdout = '';
|
|
711
|
-
const logHandler = (message)
|
|
705
|
+
const logHandler = (message)=>{
|
|
712
706
|
stdout += message;
|
|
713
707
|
log(message);
|
|
714
708
|
};
|
|
@@ -716,25 +710,39 @@ export async function computeGateCountForCircuit(pathToBB, workingDirectory, cir
|
|
|
716
710
|
// Write the bytecode to the working directory
|
|
717
711
|
await fs.writeFile(bytecodePath, bytecode);
|
|
718
712
|
const timer = new Timer();
|
|
719
|
-
const result = await executeBB(pathToBB,
|
|
713
|
+
const result = await executeBB(pathToBB, 'gates', [
|
|
714
|
+
'--scheme',
|
|
715
|
+
flavor === 'mega_honk' ? 'client_ivc' : 'ultra_honk',
|
|
716
|
+
'-b',
|
|
717
|
+
bytecodePath,
|
|
718
|
+
'-v'
|
|
719
|
+
], logHandler);
|
|
720
720
|
const duration = timer.ms();
|
|
721
|
-
if (result.status ==
|
|
721
|
+
if (result.status == 0) {
|
|
722
722
|
// Look for "circuit_size" in the stdout and parse the number
|
|
723
723
|
const circuitSizeMatch = stdout.match(/circuit_size": (\d+)/);
|
|
724
724
|
if (!circuitSizeMatch) {
|
|
725
|
-
return {
|
|
725
|
+
return {
|
|
726
|
+
status: 1,
|
|
727
|
+
reason: 'Failed to parse circuit_size from bb gates stdout.'
|
|
728
|
+
};
|
|
726
729
|
}
|
|
727
730
|
const circuitSize = parseInt(circuitSizeMatch[1]);
|
|
728
731
|
return {
|
|
729
|
-
status:
|
|
732
|
+
status: 0,
|
|
730
733
|
durationMs: duration,
|
|
731
|
-
circuitSize: circuitSize
|
|
734
|
+
circuitSize: circuitSize
|
|
732
735
|
};
|
|
733
736
|
}
|
|
734
|
-
return {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
737
|
+
return {
|
|
738
|
+
status: 1,
|
|
739
|
+
reason: 'Failed getting the gate count.'
|
|
740
|
+
};
|
|
741
|
+
} catch (error) {
|
|
742
|
+
return {
|
|
743
|
+
status: 1,
|
|
744
|
+
reason: `${error}`
|
|
745
|
+
};
|
|
738
746
|
}
|
|
739
747
|
}
|
|
740
748
|
const CACHE_FILENAME = '.cache';
|
|
@@ -743,17 +751,14 @@ async function fsCache(dir, expectedCacheKey, logger, force, action) {
|
|
|
743
751
|
let run;
|
|
744
752
|
if (force) {
|
|
745
753
|
run = true;
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
754
|
+
} else {
|
|
748
755
|
try {
|
|
749
756
|
run = !expectedCacheKey.equals(await fs.readFile(cacheFilePath));
|
|
750
|
-
}
|
|
751
|
-
catch (err) {
|
|
757
|
+
} catch (err) {
|
|
752
758
|
if (err && 'code' in err && err.code === 'ENOENT') {
|
|
753
759
|
// cache file doesn't exist, swallow error and run
|
|
754
760
|
run = true;
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
761
|
+
} else {
|
|
757
762
|
throw err;
|
|
758
763
|
}
|
|
759
764
|
}
|
|
@@ -762,17 +767,14 @@ async function fsCache(dir, expectedCacheKey, logger, force, action) {
|
|
|
762
767
|
if (run) {
|
|
763
768
|
logger(`Cache miss or forced run. Running operation in ${dir}...`);
|
|
764
769
|
res = await action();
|
|
765
|
-
}
|
|
766
|
-
else {
|
|
770
|
+
} else {
|
|
767
771
|
logger(`Cache hit. Skipping operation in ${dir}...`);
|
|
768
772
|
}
|
|
769
773
|
try {
|
|
770
774
|
await fs.writeFile(cacheFilePath, expectedCacheKey);
|
|
771
|
-
}
|
|
772
|
-
catch (err) {
|
|
775
|
+
} catch (err) {
|
|
773
776
|
logger(`Couldn't write cache data to ${cacheFilePath}. Skipping cache...`);
|
|
774
|
-
|
|
777
|
+
// ignore
|
|
775
778
|
}
|
|
776
779
|
return res;
|
|
777
780
|
}
|
|
778
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhlY3V0ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iYi9leGVjdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBeUIsd0JBQXdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFFbEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRWhELE9BQU8sS0FBSyxJQUFJLE1BQU0sZUFBZSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxRQUFRLElBQUksRUFBRSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUcvQyxPQUFPLEVBQUUsMEJBQTBCLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUUxRyxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ2hDLE1BQU0sQ0FBQyxNQUFNLGtCQUFrQixHQUFHLGdCQUFnQixDQUFDO0FBQ25ELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUM7QUFDdEMsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsbUJBQW1CLENBQUM7QUFDekQsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsa0JBQWtCLENBQUM7QUFDeEQsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsdUJBQXVCLENBQUM7QUFDbEUsTUFBTSxDQUFDLE1BQU0sa0JBQWtCLEdBQUcsZUFBZSxDQUFDO0FBRWxELE1BQU0sQ0FBTixJQUFZLFNBSVg7QUFKRCxXQUFZLFNBQVM7SUFDbkIsK0NBQU8sQ0FBQTtJQUNQLCtDQUFPLENBQUE7SUFDUCwrREFBZSxDQUFBO0FBQ2pCLENBQUMsRUFKVyxTQUFTLEtBQVQsU0FBUyxRQUlwQjtBQWlDRDs7Ozs7Ozs7R0FRRztBQUNI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
|