@aztec/bb-prover 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e
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 -110
- 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 +39 -5
- 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 +207 -78
- 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 +6 -5
- package/package.json +19 -17
- package/src/avm_proving_tests/avm_proving_tester.ts +53 -126
- 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 +116 -4
- package/src/prover/proof_utils.ts +41 -1
- package/src/prover/server/bb_prover.ts +132 -137
- 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 +6 -7
- 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,7 +378,7 @@ 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)),
|
|
@@ -286,16 +386,28 @@ export abstract class BBPrivateKernelProver implements PrivateKernelProver {
|
|
|
286
386
|
executionSteps.map(step => step.functionName),
|
|
287
387
|
);
|
|
288
388
|
|
|
289
|
-
|
|
389
|
+
// Use compressed prove path to get both proof fields and compressed proof bytes
|
|
390
|
+
const result = await backend.prove(
|
|
290
391
|
executionSteps.map(step => ungzip(serializeWitness(step.witness))),
|
|
291
392
|
executionSteps.map(step => step.vk),
|
|
393
|
+
{ compress: true },
|
|
292
394
|
);
|
|
293
395
|
this.log.info(`Generated ClientIVC proof`, {
|
|
294
396
|
eventName: 'client-ivc-proof-generation',
|
|
295
397
|
duration: timer.ms(),
|
|
296
|
-
proofSize:
|
|
398
|
+
proofSize: result.proofFields.length,
|
|
399
|
+
compressedSize: result.compressedProof?.length,
|
|
297
400
|
});
|
|
298
|
-
|
|
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;
|
|
299
411
|
}
|
|
300
412
|
|
|
301
413
|
public async computeGateCountForCircuit(_bytecode: Buffer, _circuitName: string): Promise<number> {
|
|
@@ -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,12 +444,11 @@ 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,
|
|
@@ -471,7 +461,7 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
471
461
|
logger.debug(`Generating witness data for ${circuitType}`);
|
|
472
462
|
|
|
473
463
|
const inputWitness = convertInput(input);
|
|
474
|
-
const foreignCallHandler = undefined;
|
|
464
|
+
const foreignCallHandler = undefined;
|
|
475
465
|
const witnessResult = await simulator.executeProtocolCircuit(inputWitness, artifact, foreignCallHandler);
|
|
476
466
|
const output = convertOutput(witnessResult.witness);
|
|
477
467
|
|
|
@@ -488,75 +478,80 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
488
478
|
eventName: 'circuit-witness-generation',
|
|
489
479
|
} satisfies CircuitWitnessGenerationStats);
|
|
490
480
|
|
|
491
|
-
//
|
|
492
|
-
|
|
481
|
+
// Read and decompress the witness for bb.js
|
|
482
|
+
const witnessGz = await fs.readFile(outputWitnessFile);
|
|
483
|
+
const witness = ungzip(witnessGz);
|
|
493
484
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
workingDirectory,
|
|
497
|
-
circuitType,
|
|
498
|
-
Buffer.from(artifact.bytecode, 'base64'),
|
|
499
|
-
this.getVerificationKeyDataForCircuit(circuitType).keyAsBytes,
|
|
500
|
-
outputWitnessFile,
|
|
501
|
-
getUltraHonkFlavorForCircuit(circuitType),
|
|
502
|
-
logger,
|
|
503
|
-
);
|
|
485
|
+
// Decompress bytecode for bb.js
|
|
486
|
+
const bytecode = ungzip(Buffer.from(artifact.bytecode, 'base64'));
|
|
504
487
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
488
|
+
// Prove the circuit via bb.js API
|
|
489
|
+
logger.debug(`Proving ${circuitType} via bb.js...`);
|
|
490
|
+
|
|
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}`);
|
|
508
503
|
}
|
|
509
504
|
|
|
510
505
|
return {
|
|
511
506
|
circuitOutput: output,
|
|
512
|
-
|
|
507
|
+
proofResult,
|
|
513
508
|
};
|
|
514
509
|
}
|
|
515
510
|
|
|
516
|
-
private async generateAvmProofWithBB(input: AvmCircuitInputs, workingDirectory: string): Promise<BBSuccess> {
|
|
517
|
-
logger.info(`Proving avm-circuit for TX ${input.hints.tx.hash}...`);
|
|
518
|
-
|
|
519
|
-
const provingResult = await generateAvmProof(this.config.bbBinaryPath, workingDirectory, input, logger);
|
|
520
|
-
|
|
521
|
-
if (provingResult.status === BB_RESULT.FAILURE) {
|
|
522
|
-
logger.error(`Failed to generate AVM proof for TX ${input.hints.tx.hash}: ${provingResult.reason}`);
|
|
523
|
-
throw new ProvingError(provingResult.reason, provingResult, provingResult.retry);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return provingResult;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
511
|
private async createAvmProof(
|
|
530
512
|
input: AvmCircuitInputs,
|
|
531
513
|
): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED>> {
|
|
532
|
-
|
|
533
|
-
const provingResult = await this.generateAvmProofWithBB(input, bbWorkingDirectory);
|
|
514
|
+
logger.info(`Proving avm-circuit for TX ${input.hints.tx.hash}...`);
|
|
534
515
|
|
|
535
|
-
|
|
516
|
+
const inputsBuffer = input.serializeWithMessagePack();
|
|
517
|
+
await using instance = await this.bbJsFactory.getInstance();
|
|
518
|
+
const { proof: proofFieldArrays, durationMs } = await instance.generateAvmProof(inputsBuffer);
|
|
536
519
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
this.instrumentation.recordAvmDuration('provingDuration', appCircuitName, provingResult.durationMs);
|
|
540
|
-
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)));
|
|
541
522
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
appCircuitName: input.hints.tx.hash,
|
|
547
|
-
// does not include reading the proof from disk
|
|
548
|
-
duration: provingResult.durationMs,
|
|
549
|
-
proofSize: avmProof.binaryProof.buffer.length,
|
|
550
|
-
eventName: 'circuit-proving',
|
|
551
|
-
inputSize: input.serializeWithMessagePack().length,
|
|
552
|
-
circuitSize: 1 << 21,
|
|
553
|
-
numPublicInputs: 0,
|
|
554
|
-
} 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}.`,
|
|
555
527
|
);
|
|
528
|
+
}
|
|
529
|
+
const proofFieldsPadded = proofFields.concat(
|
|
530
|
+
Array(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED - proofFields.length).fill(new Fr(0)),
|
|
531
|
+
);
|
|
556
532
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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;
|
|
560
555
|
}
|
|
561
556
|
|
|
562
557
|
/**
|
|
@@ -579,33 +574,38 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
579
574
|
convertInput: (input: CircuitInputType) => WitnessMap,
|
|
580
575
|
convertOutput: (outputWitness: WitnessMap) => CircuitOutputType,
|
|
581
576
|
): Promise<{ circuitOutput: CircuitOutputType; proof: RecursiveProof<PROOF_LENGTH> }> {
|
|
582
|
-
//
|
|
583
|
-
const operation = async (
|
|
584
|
-
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(
|
|
585
580
|
input,
|
|
586
581
|
circuitType,
|
|
587
582
|
convertInput,
|
|
588
583
|
convertOutput,
|
|
589
|
-
|
|
584
|
+
workingDirectory,
|
|
590
585
|
);
|
|
591
586
|
|
|
592
587
|
const vkData = this.getVerificationKeyDataForCircuit(circuitType);
|
|
593
|
-
//
|
|
594
|
-
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
|
+
);
|
|
595
595
|
|
|
596
596
|
const circuitName = mapProtocolArtifactNameToCircuitName(circuitType);
|
|
597
|
-
this.instrumentation.recordDuration('provingDuration', circuitName,
|
|
597
|
+
this.instrumentation.recordDuration('provingDuration', circuitName, proofResult.durationMs);
|
|
598
598
|
this.instrumentation.recordSize('proofSize', circuitName, proof.binaryProof.buffer.length);
|
|
599
599
|
this.instrumentation.recordSize('circuitPublicInputCount', circuitName, vkData.numPublicInputs);
|
|
600
600
|
this.instrumentation.recordSize('circuitSize', circuitName, vkData.circuitSize);
|
|
601
601
|
logger.info(
|
|
602
|
-
`Generated proof for ${circuitType} in ${Math.ceil(
|
|
602
|
+
`Generated proof for ${circuitType} in ${Math.ceil(proofResult.durationMs)} ms, size: ${
|
|
603
603
|
proof.proof.length
|
|
604
604
|
} fields`,
|
|
605
605
|
{
|
|
606
606
|
circuitName,
|
|
607
607
|
circuitSize: vkData.circuitSize,
|
|
608
|
-
duration:
|
|
608
|
+
duration: proofResult.durationMs,
|
|
609
609
|
inputSize: output.toBuffer().length,
|
|
610
610
|
proofSize: proof.binaryProof.buffer.length,
|
|
611
611
|
eventName: 'circuit-proving',
|
|
@@ -622,49 +622,58 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
/**
|
|
625
|
-
* Verifies a proof
|
|
625
|
+
* Verifies a proof via bb.js API (no temp files needed).
|
|
626
626
|
* @param circuitType - The type of circuit whose proof is to be verified
|
|
627
627
|
* @param proof - The proof to be verified
|
|
628
628
|
*/
|
|
629
629
|
public async verifyProof(circuitType: ServerProtocolArtifact, proof: Proof) {
|
|
630
630
|
const verificationKey = this.getVerificationKeyDataForCircuit(circuitType);
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
)
|
|
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`);
|
|
634
656
|
}
|
|
635
657
|
|
|
658
|
+
/** Verify an AVM proof via bb.js API. */
|
|
636
659
|
public async verifyAvmProof(proof: Proof, publicInputs: AvmCircuitPublicInputs) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
const proofFileName = path.join(bbWorkingDirectory, PROOF_FILENAME);
|
|
649
|
-
const verificationKeyPath = path.join(bbWorkingDirectory, VK_FILENAME);
|
|
650
|
-
// TODO(https://github.com/AztecProtocol/aztec-packages/issues/13189): Put this proof parsing logic in the proof class.
|
|
651
|
-
await fs.writeFile(publicInputsFileName, proof.buffer.subarray(0, proof.numPublicInputs * 32));
|
|
652
|
-
await fs.writeFile(proofFileName, proof.buffer.subarray(proof.numPublicInputs * 32));
|
|
653
|
-
if (verificationKey !== undefined) {
|
|
654
|
-
await fs.writeFile(verificationKeyPath, verificationKey.keyAsBytes);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const result = await verificationFunction(proofFileName, verificationKeyPath);
|
|
658
|
-
|
|
659
|
-
if (result.status === BB_RESULT.FAILURE) {
|
|
660
|
-
const errorMessage = `Failed to verify proof from key!`;
|
|
661
|
-
throw new ProvingError(errorMessage, result, result.retry);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
logger.info(`Successfully verified proof from key in ${result.durationMs} ms`);
|
|
665
|
-
};
|
|
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();
|
|
668
|
+
|
|
669
|
+
await using instance = await this.bbJsFactory.getInstance();
|
|
670
|
+
const { verified, durationMs } = await instance.verifyAvmProof(proofFields, piBuffer);
|
|
666
671
|
|
|
667
|
-
|
|
672
|
+
if (!verified) {
|
|
673
|
+
throw new ProvingError('Failed to verify AVM proof!');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
logger.info(`Successfully verified AVM proof in ${durationMs} ms`);
|
|
668
677
|
}
|
|
669
678
|
|
|
670
679
|
/**
|
|
@@ -680,29 +689,6 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
680
689
|
return vk;
|
|
681
690
|
}
|
|
682
691
|
|
|
683
|
-
private async readAvmProofAsFields(
|
|
684
|
-
proofFilename: string,
|
|
685
|
-
): Promise<RecursiveProof<typeof AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED>> {
|
|
686
|
-
const rawProofBuffer = await fs.readFile(proofFilename);
|
|
687
|
-
const reader = BufferReader.asReader(rawProofBuffer);
|
|
688
|
-
const proofFields = reader.readArray(rawProofBuffer.length / Fr.SIZE_IN_BYTES, Fr);
|
|
689
|
-
|
|
690
|
-
// We extend to a fixed-size padded proof as during development any new AVM circuit column changes the
|
|
691
|
-
// proof length and we do not have a mechanism to feedback a cpp constant to noir/TS.
|
|
692
|
-
// TODO(#13390): Revive a non-padded AVM proof
|
|
693
|
-
if (proofFields.length > AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED) {
|
|
694
|
-
throw new Error(
|
|
695
|
-
`Proof has ${proofFields.length} fields, expected no more than ${AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED}.`,
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
const proofFieldsPadded = proofFields.concat(
|
|
699
|
-
Array(AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED - proofFields.length).fill(new Fr(0)),
|
|
700
|
-
);
|
|
701
|
-
|
|
702
|
-
const proof = new Proof(rawProofBuffer, /*numPublicInputs=*/ 0);
|
|
703
|
-
return new RecursiveProof(proofFieldsPadded, proof, true, AVM_V2_PROOF_LENGTH_IN_FIELDS_PADDED);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
692
|
private runInDirectory<T>(fn: (dir: string) => Promise<T>) {
|
|
707
693
|
return runInDirectory(
|
|
708
694
|
this.config.bbWorkingDirectory,
|
|
@@ -716,3 +702,12 @@ export class BBNativeRollupProver implements ServerCircuitProver {
|
|
|
716
702
|
);
|
|
717
703
|
}
|
|
718
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
|
+
}
|
|
@@ -5,7 +5,7 @@ import { VerificationKeyAsFields, VerificationKeyData } from '@aztec/stdlib/vks'
|
|
|
5
5
|
import { promises as fs } from 'fs';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
|
|
8
|
-
import { VK_FILENAME } from '../bb/
|
|
8
|
+
import { VK_FILENAME } from '../bb/file_names.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Reads the verification key data stored at the specified location and parses into a VerificationKeyData
|