@aztec/bb-prover 0.0.1-commit.2ed92850 → 0.0.1-commit.2f68f620
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 +13 -8
- package/dest/avm_proving_tests/avm_proving_tester.d.ts.map +1 -1
- package/dest/avm_proving_tests/avm_proving_tester.js +152 -107
- package/dest/bb/bb_js_backend.d.ts +196 -0
- package/dest/bb/bb_js_backend.d.ts.map +1 -0
- package/dest/bb/bb_js_backend.js +379 -0
- package/dest/bb/bb_js_debug.d.ts +52 -0
- package/dest/bb/bb_js_debug.d.ts.map +1 -0
- package/dest/bb/bb_js_debug.js +176 -0
- package/dest/bb/file_names.d.ts +4 -0
- package/dest/bb/file_names.d.ts.map +1 -0
- package/dest/bb/file_names.js +5 -0
- package/dest/config.d.ts +17 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/index.d.ts +3 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/instrumentation.d.ts +1 -1
- package/dest/instrumentation.d.ts.map +1 -1
- package/dest/instrumentation.js +12 -4
- package/dest/prover/client/bb_private_kernel_prover.d.ts +10 -2
- package/dest/prover/client/bb_private_kernel_prover.d.ts.map +1 -1
- package/dest/prover/client/bb_private_kernel_prover.js +43 -7
- package/dest/prover/proof_utils.d.ts +11 -1
- package/dest/prover/proof_utils.d.ts.map +1 -1
- package/dest/prover/proof_utils.js +24 -1
- package/dest/prover/server/bb_prover.d.ts +4 -5
- package/dest/prover/server/bb_prover.d.ts.map +1 -1
- package/dest/prover/server/bb_prover.js +208 -79
- package/dest/test/delay_values.js +1 -1
- package/dest/verification_key/verification_key_data.js +1 -1
- package/dest/verifier/batch_chonk_verifier.d.ts +56 -0
- package/dest/verifier/batch_chonk_verifier.d.ts.map +1 -0
- package/dest/verifier/batch_chonk_verifier.js +384 -0
- package/dest/verifier/bb_verifier.d.ts +4 -1
- package/dest/verifier/bb_verifier.d.ts.map +1 -1
- package/dest/verifier/bb_verifier.js +134 -45
- package/dest/verifier/index.d.ts +2 -1
- package/dest/verifier/index.d.ts.map +1 -1
- package/dest/verifier/index.js +1 -0
- package/dest/verifier/queued_chonk_verifier.d.ts +2 -3
- package/dest/verifier/queued_chonk_verifier.d.ts.map +1 -1
- package/dest/verifier/queued_chonk_verifier.js +8 -7
- package/package.json +19 -17
- package/src/avm_proving_tests/avm_proving_tester.ts +53 -122
- package/src/bb/bb_js_backend.ts +435 -0
- package/src/bb/bb_js_debug.ts +227 -0
- package/src/bb/file_names.ts +6 -0
- package/src/config.ts +16 -0
- package/src/index.ts +2 -1
- package/src/instrumentation.ts +12 -4
- package/src/prover/client/bb_private_kernel_prover.ts +118 -5
- package/src/prover/proof_utils.ts +41 -1
- package/src/prover/server/bb_prover.ts +133 -137
- package/src/test/delay_values.ts +1 -1
- package/src/verification_key/verification_key_data.ts +1 -1
- package/src/verifier/batch_chonk_verifier.ts +415 -0
- package/src/verifier/bb_verifier.ts +66 -76
- package/src/verifier/index.ts +1 -0
- package/src/verifier/queued_chonk_verifier.ts +8 -8
- package/dest/bb/execute.d.ts +0 -107
- package/dest/bb/execute.d.ts.map +0 -1
- package/dest/bb/execute.js +0 -647
- package/src/bb/execute.ts +0 -678
package/src/index.ts
CHANGED
|
@@ -2,7 +2,8 @@ export * from './prover/index.js';
|
|
|
2
2
|
export * from './test/index.js';
|
|
3
3
|
export * from './verifier/index.js';
|
|
4
4
|
export * from './config.js';
|
|
5
|
-
export * from './bb/
|
|
5
|
+
export * from './bb/file_names.js';
|
|
6
|
+
export * from './bb/bb_js_backend.js';
|
|
6
7
|
export * from './honk.js';
|
|
7
8
|
export * from './verification_key/verification_key_data.js';
|
|
8
9
|
|
package/src/instrumentation.ts
CHANGED
|
@@ -58,10 +58,18 @@ export class ProverInstrumentation {
|
|
|
58
58
|
circuitName: CircuitName,
|
|
59
59
|
timerOrMS: Timer | number,
|
|
60
60
|
) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
// Simulation duration is stored in ms, while the others are stored in seconds
|
|
62
|
+
if (metric === 'simulationDuration') {
|
|
63
|
+
const ms = typeof timerOrMS === 'number' ? timerOrMS : timerOrMS.ms();
|
|
64
|
+
this[metric].record(Math.trunc(ms), {
|
|
65
|
+
[Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName,
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
const s = typeof timerOrMS === 'number' ? timerOrMS / 1000 : timerOrMS.s();
|
|
69
|
+
this[metric].record(s, {
|
|
70
|
+
[Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
/**
|
|
@@ -5,8 +5,16 @@ import { serializeWitness } from '@aztec/noir-noirc_abi';
|
|
|
5
5
|
import {
|
|
6
6
|
convertHidingKernelPublicInputsToWitnessMapWithAbi,
|
|
7
7
|
convertHidingKernelToRollupInputsToWitnessMapWithAbi,
|
|
8
|
+
convertPrivateKernelInit2InputsToWitnessMapWithAbi,
|
|
9
|
+
convertPrivateKernelInit2OutputsFromWitnessMapWithAbi,
|
|
10
|
+
convertPrivateKernelInit3InputsToWitnessMapWithAbi,
|
|
11
|
+
convertPrivateKernelInit3OutputsFromWitnessMapWithAbi,
|
|
8
12
|
convertPrivateKernelInitInputsToWitnessMapWithAbi,
|
|
9
13
|
convertPrivateKernelInitOutputsFromWitnessMapWithAbi,
|
|
14
|
+
convertPrivateKernelInner2InputsToWitnessMapWithAbi,
|
|
15
|
+
convertPrivateKernelInner2OutputsFromWitnessMapWithAbi,
|
|
16
|
+
convertPrivateKernelInner3InputsToWitnessMapWithAbi,
|
|
17
|
+
convertPrivateKernelInner3OutputsFromWitnessMapWithAbi,
|
|
10
18
|
convertPrivateKernelInnerInputsToWitnessMapWithAbi,
|
|
11
19
|
convertPrivateKernelInnerOutputsFromWitnessMapWithAbi,
|
|
12
20
|
convertPrivateKernelResetInputsToWitnessMapWithAbi,
|
|
@@ -32,7 +40,11 @@ import type {
|
|
|
32
40
|
HidingKernelToRollupPrivateInputs,
|
|
33
41
|
PrivateExecutionStep,
|
|
34
42
|
PrivateKernelCircuitPublicInputs,
|
|
43
|
+
PrivateKernelInit2CircuitPrivateInputs,
|
|
44
|
+
PrivateKernelInit3CircuitPrivateInputs,
|
|
35
45
|
PrivateKernelInitCircuitPrivateInputs,
|
|
46
|
+
PrivateKernelInner2CircuitPrivateInputs,
|
|
47
|
+
PrivateKernelInner3CircuitPrivateInputs,
|
|
36
48
|
PrivateKernelInnerCircuitPrivateInputs,
|
|
37
49
|
PrivateKernelResetCircuitPrivateInputs,
|
|
38
50
|
PrivateKernelSimulateOutput,
|
|
@@ -79,6 +91,50 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
|
|
|
79
91
|
);
|
|
80
92
|
}
|
|
81
93
|
|
|
94
|
+
public async generateInit2Output(
|
|
95
|
+
inputs: PrivateKernelInit2CircuitPrivateInputs,
|
|
96
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
97
|
+
return await this.generateCircuitOutput(
|
|
98
|
+
inputs,
|
|
99
|
+
'PrivateKernelInit2Artifact',
|
|
100
|
+
convertPrivateKernelInit2InputsToWitnessMapWithAbi,
|
|
101
|
+
convertPrivateKernelInit2OutputsFromWitnessMapWithAbi,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public async simulateInit2(
|
|
106
|
+
inputs: PrivateKernelInit2CircuitPrivateInputs,
|
|
107
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
108
|
+
return await this.simulateCircuitOutput(
|
|
109
|
+
inputs,
|
|
110
|
+
'PrivateKernelInit2Artifact',
|
|
111
|
+
convertPrivateKernelInit2InputsToWitnessMapWithAbi,
|
|
112
|
+
convertPrivateKernelInit2OutputsFromWitnessMapWithAbi,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public async generateInit3Output(
|
|
117
|
+
inputs: PrivateKernelInit3CircuitPrivateInputs,
|
|
118
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
119
|
+
return await this.generateCircuitOutput(
|
|
120
|
+
inputs,
|
|
121
|
+
'PrivateKernelInit3Artifact',
|
|
122
|
+
convertPrivateKernelInit3InputsToWitnessMapWithAbi,
|
|
123
|
+
convertPrivateKernelInit3OutputsFromWitnessMapWithAbi,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public async simulateInit3(
|
|
128
|
+
inputs: PrivateKernelInit3CircuitPrivateInputs,
|
|
129
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
130
|
+
return await this.simulateCircuitOutput(
|
|
131
|
+
inputs,
|
|
132
|
+
'PrivateKernelInit3Artifact',
|
|
133
|
+
convertPrivateKernelInit3InputsToWitnessMapWithAbi,
|
|
134
|
+
convertPrivateKernelInit3OutputsFromWitnessMapWithAbi,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
82
138
|
public async generateInnerOutput(
|
|
83
139
|
inputs: PrivateKernelInnerCircuitPrivateInputs,
|
|
84
140
|
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
@@ -101,6 +157,50 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
|
|
|
101
157
|
);
|
|
102
158
|
}
|
|
103
159
|
|
|
160
|
+
public async generateInner2Output(
|
|
161
|
+
inputs: PrivateKernelInner2CircuitPrivateInputs,
|
|
162
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
163
|
+
return await this.generateCircuitOutput(
|
|
164
|
+
inputs,
|
|
165
|
+
'PrivateKernelInner2Artifact',
|
|
166
|
+
convertPrivateKernelInner2InputsToWitnessMapWithAbi,
|
|
167
|
+
convertPrivateKernelInner2OutputsFromWitnessMapWithAbi,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public async simulateInner2(
|
|
172
|
+
inputs: PrivateKernelInner2CircuitPrivateInputs,
|
|
173
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
174
|
+
return await this.simulateCircuitOutput(
|
|
175
|
+
inputs,
|
|
176
|
+
'PrivateKernelInner2Artifact',
|
|
177
|
+
convertPrivateKernelInner2InputsToWitnessMapWithAbi,
|
|
178
|
+
convertPrivateKernelInner2OutputsFromWitnessMapWithAbi,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
public async generateInner3Output(
|
|
183
|
+
inputs: PrivateKernelInner3CircuitPrivateInputs,
|
|
184
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
185
|
+
return await this.generateCircuitOutput(
|
|
186
|
+
inputs,
|
|
187
|
+
'PrivateKernelInner3Artifact',
|
|
188
|
+
convertPrivateKernelInner3InputsToWitnessMapWithAbi,
|
|
189
|
+
convertPrivateKernelInner3OutputsFromWitnessMapWithAbi,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public async simulateInner3(
|
|
194
|
+
inputs: PrivateKernelInner3CircuitPrivateInputs,
|
|
195
|
+
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
196
|
+
return await this.simulateCircuitOutput(
|
|
197
|
+
inputs,
|
|
198
|
+
'PrivateKernelInner3Artifact',
|
|
199
|
+
convertPrivateKernelInner3InputsToWitnessMapWithAbi,
|
|
200
|
+
convertPrivateKernelInner3OutputsFromWitnessMapWithAbi,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
104
204
|
public async generateResetOutput(
|
|
105
205
|
inputs: PrivateKernelResetCircuitPrivateInputs,
|
|
106
206
|
): Promise<PrivateKernelSimulateOutput<PrivateKernelCircuitPublicInputs>> {
|
|
@@ -278,23 +378,36 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
|
|
|
278
378
|
this.log.info(`Generating ClientIVC proof...`);
|
|
279
379
|
const barretenberg = await Barretenberg.initSingleton({
|
|
280
380
|
...this.options,
|
|
281
|
-
logger: this.options.logger?.
|
|
381
|
+
logger: this.options.logger?.verbose,
|
|
282
382
|
});
|
|
283
383
|
const backend = new AztecClientBackend(
|
|
284
384
|
executionSteps.map(step => ungzip(step.bytecode)),
|
|
285
385
|
barretenberg,
|
|
386
|
+
executionSteps.map(step => step.functionName),
|
|
286
387
|
);
|
|
287
388
|
|
|
288
|
-
|
|
389
|
+
// Use compressed prove path to get both proof fields and compressed proof bytes
|
|
390
|
+
const result = await backend.prove(
|
|
289
391
|
executionSteps.map(step => ungzip(serializeWitness(step.witness))),
|
|
290
392
|
executionSteps.map(step => step.vk),
|
|
393
|
+
{ compress: true },
|
|
291
394
|
);
|
|
292
395
|
this.log.info(`Generated ClientIVC proof`, {
|
|
293
396
|
eventName: 'client-ivc-proof-generation',
|
|
294
397
|
duration: timer.ms(),
|
|
295
|
-
proofSize:
|
|
398
|
+
proofSize: result.proofFields.length,
|
|
399
|
+
compressedSize: result.compressedProof?.length,
|
|
296
400
|
});
|
|
297
|
-
|
|
401
|
+
|
|
402
|
+
// Create ChonkProofWithPublicInputs from the flat field elements
|
|
403
|
+
const proofWithPubInputs = ChonkProofWithPublicInputs.fromBufferArray(result.proofFields);
|
|
404
|
+
|
|
405
|
+
// Attach compressed proof bytes to the ChonkProof (without public inputs).
|
|
406
|
+
// The compressed bytes are for the full proof WITH public inputs from bb;
|
|
407
|
+
// when deserializing, the decompressor will strip them to match CHONK_PROOF_LENGTH.
|
|
408
|
+
proofWithPubInputs.compressedProof = result.compressedProof ? Buffer.from(result.compressedProof) : undefined;
|
|
409
|
+
|
|
410
|
+
return proofWithPubInputs;
|
|
298
411
|
}
|
|
299
412
|
|
|
300
413
|
public async computeGateCountForCircuit(_bytecode: Buffer, _circuitName: string): Promise<number> {
|
|
@@ -303,7 +416,7 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
|
|
|
303
416
|
...this.options,
|
|
304
417
|
logger: this.options.logger?.[(process.env.LOG_LEVEL as LogLevel) || 'verbose'],
|
|
305
418
|
});
|
|
306
|
-
const backend = new AztecClientBackend([ungzip(_bytecode)], barretenberg);
|
|
419
|
+
const backend = new AztecClientBackend([ungzip(_bytecode)], barretenberg, [_circuitName]);
|
|
307
420
|
const gateCount = await backend.gates();
|
|
308
421
|
return gateCount[0];
|
|
309
422
|
}
|
|
@@ -16,7 +16,7 @@ import assert from 'assert';
|
|
|
16
16
|
import { promises as fs } from 'fs';
|
|
17
17
|
import * as path from 'path';
|
|
18
18
|
|
|
19
|
-
import { PROOF_FILENAME, PUBLIC_INPUTS_FILENAME } from '../bb/
|
|
19
|
+
import { PROOF_FILENAME, PUBLIC_INPUTS_FILENAME } from '../bb/file_names.js';
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Create a ChonkProof proof file.
|
|
@@ -113,3 +113,43 @@ export async function readProofsFromOutputDirectory<PROOF_LENGTH extends number>
|
|
|
113
113
|
proofLength,
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Construct a RecursiveProof from in-memory proof and public input field arrays
|
|
119
|
+
* returned by the bb.js circuitProve API, without reading from files.
|
|
120
|
+
*
|
|
121
|
+
* @param proofFields - Proof fields as 32-byte Uint8Arrays from circuitProve.
|
|
122
|
+
* @param publicInputFields - Public input fields as 32-byte Uint8Arrays from circuitProve.
|
|
123
|
+
* @param vkData - Verification key data for the circuit.
|
|
124
|
+
* @param proofLength - Expected proof field count.
|
|
125
|
+
*/
|
|
126
|
+
export function constructRecursiveProofFromBuffers<PROOF_LENGTH extends number>(
|
|
127
|
+
proofFields: Uint8Array[],
|
|
128
|
+
publicInputFields: Uint8Array[],
|
|
129
|
+
vkData: VerificationKeyData,
|
|
130
|
+
proofLength: PROOF_LENGTH,
|
|
131
|
+
): RecursiveProof<PROOF_LENGTH> {
|
|
132
|
+
assert(
|
|
133
|
+
proofLength == NESTED_RECURSIVE_PROOF_LENGTH ||
|
|
134
|
+
proofLength == NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH ||
|
|
135
|
+
proofLength == ULTRA_KECCAK_PROOF_LENGTH,
|
|
136
|
+
`Proof length must be one of the expected proof lengths, received ${proofLength}`,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Convert Uint8Array fields to Fr instances
|
|
140
|
+
const proofFieldsFr = proofFields.map(f => Fr.fromBuffer(Buffer.from(f)));
|
|
141
|
+
|
|
142
|
+
assert(
|
|
143
|
+
proofFieldsFr.length == proofLength,
|
|
144
|
+
`Proof fields length mismatch: ${proofFieldsFr.length} != ${proofLength}`,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Reconstruct the binary proof with public inputs prepended (same format as file-based path)
|
|
148
|
+
const binaryPublicInputs = Buffer.concat(publicInputFields.map(f => Buffer.from(f)));
|
|
149
|
+
const binaryProof = Buffer.concat(proofFields.map(f => Buffer.from(f)));
|
|
150
|
+
const binaryProofWithPublicInputs = Buffer.concat([binaryPublicInputs, binaryProof]);
|
|
151
|
+
|
|
152
|
+
const numPublicInputs = getNumCustomPublicInputs(proofLength, vkData);
|
|
153
|
+
|
|
154
|
+
return new RecursiveProof(proofFieldsFr, new Proof(binaryProofWithPublicInputs, numPublicInputs), true, proofLength);
|
|
155
|
+
}
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
10
10
|
import { runInDirectory } from '@aztec/foundation/fs';
|
|
11
11
|
import { createLogger } from '@aztec/foundation/log';
|
|
12
|
-
import { BufferReader } from '@aztec/foundation/serialize';
|
|
13
12
|
import {
|
|
14
13
|
type ServerProtocolArtifact,
|
|
15
14
|
convertBlockMergeRollupOutputsFromWitnessMap,
|
|
@@ -88,24 +87,14 @@ import { VerificationKeyData } from '@aztec/stdlib/vks';
|
|
|
88
87
|
import { Attributes, type TelemetryClient, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
89
88
|
|
|
90
89
|
import { promises as fs } from 'fs';
|
|
90
|
+
import { ungzip } from 'pako';
|
|
91
91
|
import * as path from 'path';
|
|
92
92
|
|
|
93
|
-
import {
|
|
94
|
-
type BBFailure,
|
|
95
|
-
type BBSuccess,
|
|
96
|
-
BB_RESULT,
|
|
97
|
-
PROOF_FILENAME,
|
|
98
|
-
PUBLIC_INPUTS_FILENAME,
|
|
99
|
-
VK_FILENAME,
|
|
100
|
-
generateAvmProof,
|
|
101
|
-
generateProof,
|
|
102
|
-
verifyAvmProof,
|
|
103
|
-
verifyProof,
|
|
104
|
-
} from '../../bb/execute.js';
|
|
93
|
+
import { BBJsFactory, type BBJsProofResult } from '../../bb/bb_js_backend.js';
|
|
105
94
|
import type { ACVMConfig, BBConfig } from '../../config.js';
|
|
106
95
|
import { getUltraHonkFlavorForCircuit } from '../../honk.js';
|
|
107
96
|
import { ProverInstrumentation } from '../../instrumentation.js';
|
|
108
|
-
import {
|
|
97
|
+
import { constructRecursiveProofFromBuffers } from '../proof_utils.js';
|
|
109
98
|
|
|
110
99
|
const logger = createLogger('bb-prover');
|
|
111
100
|
|
|
@@ -119,12 +108,14 @@ export interface BBProverConfig extends BBConfig, ACVMConfig {
|
|
|
119
108
|
*/
|
|
120
109
|
export class BBNativeRollupProver implements ServerCircuitProver {
|
|
121
110
|
private instrumentation: ProverInstrumentation;
|
|
111
|
+
private bbJsFactory: BBJsFactory;
|
|
122
112
|
|
|
123
113
|
constructor(
|
|
124
114
|
private config: BBProverConfig,
|
|
125
115
|
telemetry: TelemetryClient,
|
|
126
116
|
) {
|
|
127
117
|
this.instrumentation = new ProverInstrumentation(telemetry, 'BBNativeRollupProver');
|
|
118
|
+
this.bbJsFactory = new BBJsFactory(config.bbBinaryPath, { logger, debugDir: config.bbDebugOutputDir });
|
|
128
119
|
}
|
|
129
120
|
|
|
130
121
|
get tracer() {
|
|
@@ -136,7 +127,7 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
136
127
|
await fs.mkdir(config.acvmWorkingDirectory, { recursive: true });
|
|
137
128
|
await fs.access(config.bbBinaryPath, fs.constants.R_OK);
|
|
138
129
|
await fs.mkdir(config.bbWorkingDirectory, { recursive: true });
|
|
139
|
-
logger.info(`Using
|
|
130
|
+
logger.info(`Using bb.js API with binary at ${config.bbBinaryPath}`);
|
|
140
131
|
logger.info(`Using native ACVM at ${config.acvmBinaryPath} and working directory ${config.acvmWorkingDirectory}`);
|
|
141
132
|
|
|
142
133
|
return new BBNativeRollupProver(config, telemetry);
|
|
@@ -453,16 +444,16 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
453
444
|
convertInput: (input: Input) => WitnessMap,
|
|
454
445
|
convertOutput: (outputWitness: WitnessMap) => Output,
|
|
455
446
|
workingDirectory: string,
|
|
456
|
-
): Promise<{ circuitOutput: Output;
|
|
457
|
-
// Have the ACVM write the partial witness here
|
|
447
|
+
): Promise<{ circuitOutput: Output; proofResult: BBJsProofResult }> {
|
|
448
|
+
// Have the ACVM write the partial witness here (still needs a temp directory)
|
|
458
449
|
const outputWitnessFile = path.join(workingDirectory, 'partial-witness.gz');
|
|
459
450
|
|
|
460
451
|
// Generate the partial witness using the ACVM
|
|
461
|
-
// A further temp directory will be created beneath ours and then cleaned up after the partial witness has been copied to our specified location
|
|
462
452
|
const simulator = new NativeACVMSimulator(
|
|
463
453
|
this.config.acvmWorkingDirectory,
|
|
464
454
|
this.config.acvmBinaryPath,
|
|
465
455
|
outputWitnessFile,
|
|
456
|
+
logger,
|
|
466
457
|
);
|
|
467
458
|
|
|
468
459
|
const artifact = getServerCircuitArtifact(circuitType);
|
|
@@ -470,7 +461,7 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
470
461
|
logger.debug(`Generating witness data for ${circuitType}`);
|
|
471
462
|
|
|
472
463
|
const inputWitness = convertInput(input);
|
|
473
|
-
const foreignCallHandler = undefined;
|
|
464
|
+
const foreignCallHandler = undefined;
|
|
474
465
|
const witnessResult = await simulator.executeProtocolCircuit(inputWitness, artifact, foreignCallHandler);
|
|
475
466
|
const output = convertOutput(witnessResult.witness);
|
|
476
467
|
|
|
@@ -487,75 +478,80 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
487
478
|
eventName: 'circuit-witness-generation',
|
|
488
479
|
} satisfies CircuitWitnessGenerationStats);
|
|
489
480
|
|
|
490
|
-
//
|
|
491
|
-
|
|
481
|
+
// Read and decompress the witness for bb.js
|
|
482
|
+
const witnessGz = await fs.readFile(outputWitnessFile);
|
|
483
|
+
const witness = ungzip(witnessGz);
|
|
492
484
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
this.getVerificationKeyDataForCircuit(circuitType).keyAsBytes,
|
|
499
|
-
outputWitnessFile,
|
|
500
|
-
getUltraHonkFlavorForCircuit(circuitType),
|
|
501
|
-
logger,
|
|
502
|
-
);
|
|
485
|
+
// Decompress bytecode for bb.js
|
|
486
|
+
const bytecode = ungzip(Buffer.from(artifact.bytecode, 'base64'));
|
|
487
|
+
|
|
488
|
+
// Prove the circuit via bb.js API
|
|
489
|
+
logger.debug(`Proving ${circuitType} via bb.js...`);
|
|
503
490
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
491
|
+
let proofResult: BBJsProofResult;
|
|
492
|
+
try {
|
|
493
|
+
await using instance = await this.bbJsFactory.getInstance();
|
|
494
|
+
proofResult = await instance.generateProof(
|
|
495
|
+
circuitType,
|
|
496
|
+
bytecode,
|
|
497
|
+
this.getVerificationKeyDataForCircuit(circuitType).keyAsBytes,
|
|
498
|
+
witness,
|
|
499
|
+
getUltraHonkFlavorForCircuit(circuitType),
|
|
500
|
+
);
|
|
501
|
+
} catch (error) {
|
|
502
|
+
throw new ProvingError(`Failed to generate proof for ${circuitType}: ${error}`);
|
|
507
503
|
}
|
|
508
504
|
|
|
509
505
|
return {
|
|
510
506
|
circuitOutput: output,
|
|
511
|
-
|
|
507
|
+
proofResult,
|
|
512
508
|
};
|
|
513
509
|
}
|
|
514
510
|
|
|
515
|
-
private async generateAvmProofWithBB(input: AvmCircuitInputs, workingDirectory: string): Promise<BBSuccess> {
|
|
516
|
-
logger.info(`Proving avm-circuit for TX ${input.hints.tx.hash}...`);
|
|
517
|
-
|
|
518
|
-
const provingResult = await generateAvmProof(this.config.bbBinaryPath, workingDirectory, input, logger);
|
|
519
|
-
|
|
520
|
-
if (provingResult.status === BB_RESULT.FAILURE) {
|
|
521
|
-
logger.error(`Failed to generate AVM proof for TX ${input.hints.tx.hash}: ${provingResult.reason}`);
|
|
522
|
-
throw new ProvingError(provingResult.reason, provingResult, provingResult.retry);
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
return provingResult;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
511
|
private async createAvmProof(
|
|
529
512
|
input: AvmCircuitInputs,
|
|
530
513
|
): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED>> {
|
|
531
|
-
|
|
532
|
-
const provingResult = await this.generateAvmProofWithBB(input, bbWorkingDirectory);
|
|
514
|
+
logger.info(`Proving avm-circuit for TX ${input.hints.tx.hash}...`);
|
|
533
515
|
|
|
534
|
-
|
|
516
|
+
const inputsBuffer = input.serializeWithMessagePack();
|
|
517
|
+
await using instance = await this.bbJsFactory.getInstance();
|
|
518
|
+
const { proof: proofFieldArrays, durationMs } = await instance.generateAvmProof(inputsBuffer);
|
|
535
519
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
this.instrumentation.recordAvmDuration('provingDuration', appCircuitName, provingResult.durationMs);
|
|
539
|
-
this.instrumentation.recordAvmSize('proofSize', appCircuitName, avmProof.binaryProof.buffer.length);
|
|
520
|
+
// Convert Uint8Array[] (32-byte field elements) to Fr[]
|
|
521
|
+
const proofFields = proofFieldArrays.map(f => Fr.fromBuffer(Buffer.from(f)));
|
|
540
522
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
appCircuitName: input.hints.tx.hash,
|
|
546
|
-
// does not include reading the proof from disk
|
|
547
|
-
duration: provingResult.durationMs,
|
|
548
|
-
proofSize: avmProof.binaryProof.buffer.length,
|
|
549
|
-
eventName: 'circuit-proving',
|
|
550
|
-
inputSize: input.serializeWithMessagePack().length,
|
|
551
|
-
circuitSize: 1 << 21,
|
|
552
|
-
numPublicInputs: 0,
|
|
553
|
-
} satisfies CircuitProvingStats,
|
|
523
|
+
// Pad to fixed size (during development the proof length may vary)
|
|
524
|
+
if (proofFields.length > AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED) {
|
|
525
|
+
throw new Error(
|
|
526
|
+
`Proof has ${proofFields.length} fields, expected no more than ${AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED}.`,
|
|
554
527
|
);
|
|
528
|
+
}
|
|
529
|
+
const proofFieldsPadded = proofFields.concat(
|
|
530
|
+
Array(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED - proofFields.length).fill(new Fr(0)),
|
|
531
|
+
);
|
|
555
532
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
533
|
+
// Build the binary proof from the raw field data
|
|
534
|
+
const rawProofBuffer = Buffer.concat(proofFieldArrays.map(f => Buffer.from(f)));
|
|
535
|
+
const binaryProof = new Proof(rawProofBuffer, /*numPublicInputs=*/ 0);
|
|
536
|
+
const avmProof = new RecursiveProof(proofFieldsPadded, binaryProof, true, AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
|
|
537
|
+
|
|
538
|
+
const circuitType = 'avm-circuit' as const;
|
|
539
|
+
const appCircuitName = 'unknown' as const;
|
|
540
|
+
this.instrumentation.recordAvmDuration('provingDuration', appCircuitName, durationMs);
|
|
541
|
+
this.instrumentation.recordAvmSize('proofSize', appCircuitName, avmProof.binaryProof.buffer.length);
|
|
542
|
+
|
|
543
|
+
logger.info(`Generated proof for ${circuitType}(${input.hints.tx.hash}) in ${Math.ceil(durationMs)} ms`, {
|
|
544
|
+
circuitName: circuitType,
|
|
545
|
+
appCircuitName: input.hints.tx.hash,
|
|
546
|
+
duration: durationMs,
|
|
547
|
+
proofSize: avmProof.binaryProof.buffer.length,
|
|
548
|
+
eventName: 'circuit-proving',
|
|
549
|
+
inputSize: inputsBuffer.length,
|
|
550
|
+
circuitSize: 1 << 21,
|
|
551
|
+
numPublicInputs: 0,
|
|
552
|
+
} satisfies CircuitProvingStats);
|
|
553
|
+
|
|
554
|
+
return avmProof;
|
|
559
555
|
}
|
|
560
556
|
|
|
561
557
|
/**
|
|
@@ -578,33 +574,38 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
578
574
|
convertInput: (input: CircuitInputType) => WitnessMap,
|
|
579
575
|
convertOutput: (outputWitness: WitnessMap) => CircuitOutputType,
|
|
580
576
|
): Promise<{ circuitOutput: CircuitOutputType; proof: RecursiveProof<PROOF_LENGTH> }> {
|
|
581
|
-
//
|
|
582
|
-
const operation = async (
|
|
583
|
-
const {
|
|
577
|
+
// Still need runInDirectory for ACVM witness generation temp files
|
|
578
|
+
const operation = async (workingDirectory: string) => {
|
|
579
|
+
const { proofResult, circuitOutput: output } = await this.generateProofWithBB(
|
|
584
580
|
input,
|
|
585
581
|
circuitType,
|
|
586
582
|
convertInput,
|
|
587
583
|
convertOutput,
|
|
588
|
-
|
|
584
|
+
workingDirectory,
|
|
589
585
|
);
|
|
590
586
|
|
|
591
587
|
const vkData = this.getVerificationKeyDataForCircuit(circuitType);
|
|
592
|
-
//
|
|
593
|
-
const proof =
|
|
588
|
+
// Construct proof from in-memory buffers (no file I/O needed)
|
|
589
|
+
const proof = constructRecursiveProofFromBuffers(
|
|
590
|
+
proofResult.proofFields,
|
|
591
|
+
proofResult.publicInputFields,
|
|
592
|
+
vkData,
|
|
593
|
+
proofLength,
|
|
594
|
+
);
|
|
594
595
|
|
|
595
596
|
const circuitName = mapProtocolArtifactNameToCircuitName(circuitType);
|
|
596
|
-
this.instrumentation.recordDuration('provingDuration', circuitName,
|
|
597
|
+
this.instrumentation.recordDuration('provingDuration', circuitName, proofResult.durationMs);
|
|
597
598
|
this.instrumentation.recordSize('proofSize', circuitName, proof.binaryProof.buffer.length);
|
|
598
599
|
this.instrumentation.recordSize('circuitPublicInputCount', circuitName, vkData.numPublicInputs);
|
|
599
600
|
this.instrumentation.recordSize('circuitSize', circuitName, vkData.circuitSize);
|
|
600
601
|
logger.info(
|
|
601
|
-
`Generated proof for ${circuitType} in ${Math.ceil(
|
|
602
|
+
`Generated proof for ${circuitType} in ${Math.ceil(proofResult.durationMs)} ms, size: ${
|
|
602
603
|
proof.proof.length
|
|
603
604
|
} fields`,
|
|
604
605
|
{
|
|
605
606
|
circuitName,
|
|
606
607
|
circuitSize: vkData.circuitSize,
|
|
607
|
-
duration:
|
|
608
|
+
duration: proofResult.durationMs,
|
|
608
609
|
inputSize: output.toBuffer().length,
|
|
609
610
|
proofSize: proof.binaryProof.buffer.length,
|
|
610
611
|
eventName: 'circuit-proving',
|
|
@@ -621,49 +622,58 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
621
622
|
}
|
|
622
623
|
|
|
623
624
|
/**
|
|
624
|
-
* Verifies a proof
|
|
625
|
+
* Verifies a proof via bb.js API (no temp files needed).
|
|
625
626
|
* @param circuitType - The type of circuit whose proof is to be verified
|
|
626
627
|
* @param proof - The proof to be verified
|
|
627
628
|
*/
|
|
628
629
|
public async verifyProof(circuitType: ServerProtocolArtifact, proof: Proof) {
|
|
629
630
|
const verificationKey = this.getVerificationKeyDataForCircuit(circuitType);
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
)
|
|
631
|
+
const flavor = getUltraHonkFlavorForCircuit(circuitType);
|
|
632
|
+
|
|
633
|
+
// Split proof buffer into public input fields and proof fields (32-byte each)
|
|
634
|
+
const publicInputFields = splitBufferToFieldArrays(proof.buffer.subarray(0, proof.numPublicInputs * 32));
|
|
635
|
+
const proofFields = splitBufferToFieldArrays(proof.buffer.subarray(proof.numPublicInputs * 32));
|
|
636
|
+
|
|
637
|
+
let verified: boolean;
|
|
638
|
+
let durationMs: number;
|
|
639
|
+
try {
|
|
640
|
+
await using instance = await this.bbJsFactory.getInstance();
|
|
641
|
+
({ verified, durationMs } = await instance.verifyProof(
|
|
642
|
+
proofFields,
|
|
643
|
+
verificationKey.keyAsBytes,
|
|
644
|
+
publicInputFields,
|
|
645
|
+
flavor,
|
|
646
|
+
));
|
|
647
|
+
} catch (error) {
|
|
648
|
+
throw new ProvingError(`Failed to verify proof for ${circuitType}: ${error}`);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (!verified) {
|
|
652
|
+
throw new ProvingError('Failed to verify proof from key!');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
logger.info(`Successfully verified proof from key in ${durationMs} ms`);
|
|
633
656
|
}
|
|
634
657
|
|
|
658
|
+
/** Verify an AVM proof via bb.js API. */
|
|
635
659
|
public async verifyAvmProof(proof: Proof, publicInputs: AvmCircuitPublicInputs) {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
) {
|
|
645
|
-
const operation = async (bbWorkingDirectory: string) => {
|
|
646
|
-
const publicInputsFileName = path.join(bbWorkingDirectory, PUBLIC_INPUTS_FILENAME);
|
|
647
|
-
const proofFileName = path.join(bbWorkingDirectory, PROOF_FILENAME);
|
|
648
|
-
const verificationKeyPath = path.join(bbWorkingDirectory, VK_FILENAME);
|
|
649
|
-
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/13189): Put this proof parsing logic in the proof class.
|
|
650
|
-
await fs.writeFile(publicInputsFileName, proof.buffer.subarray(0, proof.numPublicInputs * 32));
|
|
651
|
-
await fs.writeFile(proofFileName, proof.buffer.subarray(proof.numPublicInputs * 32));
|
|
652
|
-
if (verificationKey !== undefined) {
|
|
653
|
-
await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
const result = await verificationFunction(proofFileName, verificationKeyPath);
|
|
657
|
-
|
|
658
|
-
if (result.status === BB_RESULT.FAILURE) {
|
|
659
|
-
const errorMessage = `Failed to verify proof from key!`;
|
|
660
|
-
throw new ProvingError(errorMessage, result, result.retry);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
logger.info(`Successfully verified proof from key in ${result.durationMs} ms`);
|
|
664
|
-
};
|
|
660
|
+
// For AVM proofs, numPublicInputs is 0, so the full buffer is the proof.
|
|
661
|
+
const proofBuffer = proof.buffer.subarray(proof.numPublicInputs * 32);
|
|
662
|
+
// Split the raw proof buffer into 32-byte field element arrays
|
|
663
|
+
const proofFields: Uint8Array[] = [];
|
|
664
|
+
for (let i = 0; i < proofBuffer.length; i += Fr.SIZE_IN_BYTES) {
|
|
665
|
+
proofFields.push(new Uint8Array(proofBuffer.subarray(i, i + Fr.SIZE_IN_BYTES)));
|
|
666
|
+
}
|
|
667
|
+
const piBuffer = publicInputs.serializeWithMessagePack();
|
|
665
668
|
|
|
666
|
-
await this.
|
|
669
|
+
await using instance = await this.bbJsFactory.getInstance();
|
|
670
|
+
const { verified, durationMs } = await instance.verifyAvmProof(proofFields, piBuffer);
|
|
671
|
+
|
|
672
|
+
if (!verified) {
|
|
673
|
+
throw new ProvingError('Failed to verify AVM proof!');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
logger.info(`Successfully verified AVM proof in ${durationMs} ms`);
|
|
667
677
|
}
|
|
668
678
|
|
|
669
679
|
/**
|
|
@@ -679,29 +689,6 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
679
689
|
return vk;
|
|
680
690
|
}
|
|
681
691
|
|
|
682
|
-
private async readAvmProofAsFields(
|
|
683
|
-
proofFilename: string,
|
|
684
|
-
): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED>> {
|
|
685
|
-
const rawProofBuffer = await fs.readFile(proofFilename);
|
|
686
|
-
const reader = BufferReader.asReader(rawProofBuffer);
|
|
687
|
-
const proofFields = reader.readArray(rawProofBuffer.length / Fr.SIZE_IN_BYTES, Fr);
|
|
688
|
-
|
|
689
|
-
// We extend to a fixed-size padded proof as during development any new AVM circuit column changes the
|
|
690
|
-
// proof length and we do not have a mechanism to feedback a cpp constant to noir/TS.
|
|
691
|
-
// TODO(#13390): Revive a non-padded AVM proof
|
|
692
|
-
if (proofFields.length > AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED) {
|
|
693
|
-
throw new Error(
|
|
694
|
-
`Proof has ${proofFields.length} fields, expected no more than ${AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED}.`,
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
const proofFieldsPadded = proofFields.concat(
|
|
698
|
-
Array(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED - proofFields.length).fill(new Fr(0)),
|
|
699
|
-
);
|
|
700
|
-
|
|
701
|
-
const proof = new Proof(rawProofBuffer, /*numPublicInputs=*/ 0);
|
|
702
|
-
return new RecursiveProof(proofFieldsPadded, proof, true, AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
|
|
703
|
-
}
|
|
704
|
-
|
|
705
692
|
private runInDirectory<T>(fn: (dir: string) => Promise<T>) {
|
|
706
693
|
return runInDirectory(
|
|
707
694
|
this.config.bbWorkingDirectory,
|
|
@@ -715,3 +702,12 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
715
702
|
);
|
|
716
703
|
}
|
|
717
704
|
}
|
|
705
|
+
|
|
706
|
+
/** Split a buffer into 32-byte Uint8Array field elements. */
|
|
707
|
+
function splitBufferToFieldArrays(buffer: Buffer): Uint8Array[] {
|
|
708
|
+
const fields: Uint8Array[] = [];
|
|
709
|
+
for (let i = 0; i < buffer.length; i += 32) {
|
|
710
|
+
fields.push(new Uint8Array(buffer.subarray(i, i + 32)));
|
|
711
|
+
}
|
|
712
|
+
return fields;
|
|
713
|
+
}
|