@aztec/bb-prover 0.46.2 → 0.46.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/bb/cli.d.ts.map +1 -1
- package/dest/bb/cli.js +22 -1
- package/dest/bb/execute.d.ts +25 -0
- package/dest/bb/execute.d.ts.map +1 -1
- package/dest/bb/execute.js +185 -7
- package/dest/prover/bb_private_kernel_prover.d.ts +49 -0
- package/dest/prover/bb_private_kernel_prover.d.ts.map +1 -0
- package/dest/prover/bb_private_kernel_prover.js +201 -0
- package/dest/prover/bb_prover.d.ts +20 -5
- package/dest/prover/bb_prover.d.ts.map +1 -1
- package/dest/prover/bb_prover.js +118 -15
- package/dest/prover/index.d.ts +1 -1
- package/dest/prover/index.d.ts.map +1 -1
- package/dest/prover/index.js +2 -2
- package/dest/stats.d.ts.map +1 -1
- package/dest/stats.js +3 -1
- package/dest/test/test_circuit_prover.d.ts +6 -1
- package/dest/test/test_circuit_prover.d.ts.map +1 -1
- package/dest/test/test_circuit_prover.js +17 -3
- package/dest/verification_key/verification_key_data.d.ts.map +1 -1
- package/dest/verification_key/verification_key_data.js +2 -3
- package/dest/verifier/bb_verifier.d.ts.map +1 -1
- package/dest/verifier/bb_verifier.js +8 -7
- package/package.json +8 -7
- package/src/bb/cli.ts +32 -0
- package/src/bb/execute.ts +227 -7
- package/src/prover/{bb_native_proof_creator.ts → bb_private_kernel_prover.ts} +114 -114
- package/src/prover/bb_prover.ts +167 -17
- package/src/prover/index.ts +1 -1
- package/src/stats.ts +2 -0
- package/src/test/test_circuit_prover.ts +42 -0
- package/src/verification_key/verification_key_data.ts +1 -5
- package/src/verifier/bb_verifier.ts +7 -6
- package/dest/prover/bb_native_proof_creator.d.ts +0 -47
- package/dest/prover/bb_native_proof_creator.d.ts.map +0 -1
- package/dest/prover/bb_native_proof_creator.js +0 -198
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { AGGREGATION_OBJECT_LENGTH, Fr, NESTED_RECURSIVE_PROOF_LENGTH, Proof, RECURSIVE_PROOF_LENGTH, RecursiveProof, } from '@aztec/circuits.js';
|
|
2
|
-
import { siloNoteHash } from '@aztec/circuits.js/hash';
|
|
3
|
-
import { runInDirectory } from '@aztec/foundation/fs';
|
|
4
|
-
import { createDebugLogger } from '@aztec/foundation/log';
|
|
5
|
-
import { Timer } from '@aztec/foundation/timer';
|
|
6
|
-
import { ClientCircuitArtifacts, PrivateResetTagToArtifactName, ProtocolCircuitVks, convertPrivateKernelInitInputsToWitnessMap, convertPrivateKernelInitOutputsFromWitnessMap, convertPrivateKernelInnerInputsToWitnessMap, convertPrivateKernelInnerOutputsFromWitnessMap, convertPrivateKernelResetInputsToWitnessMap, convertPrivateKernelResetOutputsFromWitnessMap, convertPrivateKernelTailForPublicOutputsFromWitnessMap, convertPrivateKernelTailInputsToWitnessMap, convertPrivateKernelTailOutputsFromWitnessMap, convertPrivateKernelTailToPublicInputsToWitnessMap, } from '@aztec/noir-protocol-circuits-types';
|
|
7
|
-
import { WASMSimulator } from '@aztec/simulator';
|
|
8
|
-
import { serializeWitness } from '@noir-lang/noirc_abi';
|
|
9
|
-
import * as fs from 'fs/promises';
|
|
10
|
-
import { join } from 'path';
|
|
11
|
-
import { BB_RESULT, PROOF_FIELDS_FILENAME, PROOF_FILENAME, generateProof, verifyProof } from '../bb/execute.js';
|
|
12
|
-
import { mapProtocolArtifactNameToCircuitName } from '../stats.js';
|
|
13
|
-
import { extractVkData } from '../verification_key/verification_key_data.js';
|
|
14
|
-
/**
|
|
15
|
-
* This proof creator implementation uses the native bb binary.
|
|
16
|
-
* This is a temporary implementation until we make the WASM version work.
|
|
17
|
-
*/
|
|
18
|
-
export class BBNativeProofCreator {
|
|
19
|
-
constructor(bbBinaryPath, bbWorkingDirectory, log = createDebugLogger('aztec:bb-native-prover')) {
|
|
20
|
-
this.bbBinaryPath = bbBinaryPath;
|
|
21
|
-
this.bbWorkingDirectory = bbWorkingDirectory;
|
|
22
|
-
this.log = log;
|
|
23
|
-
this.simulator = new WASMSimulator();
|
|
24
|
-
this.verificationKeys = new Map();
|
|
25
|
-
}
|
|
26
|
-
getSiloedCommitments(publicInputs) {
|
|
27
|
-
const contractAddress = publicInputs.callContext.storageContractAddress;
|
|
28
|
-
return Promise.resolve(publicInputs.noteHashes.map(commitment => siloNoteHash(contractAddress, commitment.value)));
|
|
29
|
-
}
|
|
30
|
-
async createProofInit(inputs) {
|
|
31
|
-
return await this.createSafeProof(inputs, 'PrivateKernelInitArtifact', convertPrivateKernelInitInputsToWitnessMap, convertPrivateKernelInitOutputsFromWitnessMap);
|
|
32
|
-
}
|
|
33
|
-
async createProofInner(inputs) {
|
|
34
|
-
return await this.createSafeProof(inputs, 'PrivateKernelInnerArtifact', convertPrivateKernelInnerInputsToWitnessMap, convertPrivateKernelInnerOutputsFromWitnessMap);
|
|
35
|
-
}
|
|
36
|
-
async createProofReset(inputs) {
|
|
37
|
-
return await this.createSafeProof(inputs, PrivateResetTagToArtifactName[inputs.sizeTag], convertPrivateKernelResetInputsToWitnessMap, output => convertPrivateKernelResetOutputsFromWitnessMap(output, inputs.sizeTag));
|
|
38
|
-
}
|
|
39
|
-
async createProofTail(inputs) {
|
|
40
|
-
if (!inputs.isForPublic()) {
|
|
41
|
-
return await this.createSafeProof(inputs, 'PrivateKernelTailArtifact', convertPrivateKernelTailInputsToWitnessMap, convertPrivateKernelTailOutputsFromWitnessMap);
|
|
42
|
-
}
|
|
43
|
-
return await this.createSafeProof(inputs, 'PrivateKernelTailToPublicArtifact', convertPrivateKernelTailToPublicInputsToWitnessMap, convertPrivateKernelTailForPublicOutputsFromWitnessMap);
|
|
44
|
-
}
|
|
45
|
-
async createAppCircuitProof(partialWitness, bytecode, appCircuitName) {
|
|
46
|
-
const operation = async (directory) => {
|
|
47
|
-
this.log.debug(`Proving app circuit`);
|
|
48
|
-
const proofOutput = await this.createProof(directory, partialWitness, bytecode, 'App', appCircuitName);
|
|
49
|
-
if (proofOutput.proof.proof.length != RECURSIVE_PROOF_LENGTH) {
|
|
50
|
-
throw new Error(`Incorrect proof length`);
|
|
51
|
-
}
|
|
52
|
-
const proof = proofOutput.proof;
|
|
53
|
-
const output = {
|
|
54
|
-
proof,
|
|
55
|
-
verificationKey: proofOutput.verificationKey,
|
|
56
|
-
};
|
|
57
|
-
return output;
|
|
58
|
-
};
|
|
59
|
-
return await runInDirectory(this.bbWorkingDirectory, operation);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Verifies a proof, will generate the verification key if one is not cached internally
|
|
63
|
-
* @param circuitType - The type of circuit whose proof is to be verified
|
|
64
|
-
* @param proof - The proof to be verified
|
|
65
|
-
*/
|
|
66
|
-
async verifyProofForProtocolCircuit(circuitType, proof) {
|
|
67
|
-
const verificationKey = ProtocolCircuitVks[circuitType];
|
|
68
|
-
this.log.debug(`Verifying with key: ${verificationKey.keyAsFields.hash.toString()}`);
|
|
69
|
-
const logFunction = (message) => {
|
|
70
|
-
this.log.debug(`${circuitType} BB out - ${message}`);
|
|
71
|
-
};
|
|
72
|
-
const result = await this.verifyProofFromKey(verificationKey.keyAsBytes, proof, logFunction);
|
|
73
|
-
if (result.status === BB_RESULT.FAILURE) {
|
|
74
|
-
const errorMessage = `Failed to verify ${circuitType} proof!`;
|
|
75
|
-
throw new Error(errorMessage);
|
|
76
|
-
}
|
|
77
|
-
this.log.info(`Successfully verified ${circuitType} proof in ${Math.ceil(result.durationMs)} ms`);
|
|
78
|
-
}
|
|
79
|
-
async verifyProofFromKey(verificationKey, proof, logFunction = () => { }) {
|
|
80
|
-
const operation = async (bbWorkingDirectory) => {
|
|
81
|
-
const proofFileName = `${bbWorkingDirectory}/proof`;
|
|
82
|
-
const verificationKeyPath = `${bbWorkingDirectory}/vk`;
|
|
83
|
-
await fs.writeFile(proofFileName, proof.buffer);
|
|
84
|
-
await fs.writeFile(verificationKeyPath, verificationKey);
|
|
85
|
-
return await verifyProof(this.bbBinaryPath, proofFileName, verificationKeyPath, logFunction);
|
|
86
|
-
};
|
|
87
|
-
return await runInDirectory(this.bbWorkingDirectory, operation);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Ensures our verification key cache includes the key data located at the specified directory
|
|
91
|
-
* @param filePath - The directory containing the verification key data files
|
|
92
|
-
* @param circuitType - The type of circuit to which the verification key corresponds
|
|
93
|
-
*/
|
|
94
|
-
async updateVerificationKeyAfterProof(filePath, circuitType) {
|
|
95
|
-
let promise = this.verificationKeys.get(circuitType);
|
|
96
|
-
if (!promise) {
|
|
97
|
-
promise = extractVkData(filePath);
|
|
98
|
-
this.log.debug(`Updated verification key for circuit: ${circuitType}`);
|
|
99
|
-
this.verificationKeys.set(circuitType, promise);
|
|
100
|
-
}
|
|
101
|
-
return await promise;
|
|
102
|
-
}
|
|
103
|
-
async createSafeProof(inputs, circuitType, convertInputs, convertOutputs) {
|
|
104
|
-
const operation = async (directory) => {
|
|
105
|
-
return await this.generateWitnessAndCreateProof(inputs, circuitType, directory, convertInputs, convertOutputs);
|
|
106
|
-
};
|
|
107
|
-
return await runInDirectory(this.bbWorkingDirectory, operation);
|
|
108
|
-
}
|
|
109
|
-
async generateWitnessAndCreateProof(inputs, circuitType, directory, convertInputs, convertOutputs) {
|
|
110
|
-
this.log.debug(`Generating witness for ${circuitType}`);
|
|
111
|
-
const compiledCircuit = ClientCircuitArtifacts[circuitType];
|
|
112
|
-
const witnessMap = convertInputs(inputs);
|
|
113
|
-
const timer = new Timer();
|
|
114
|
-
const outputWitness = await this.simulator.simulateCircuit(witnessMap, compiledCircuit);
|
|
115
|
-
const output = convertOutputs(outputWitness);
|
|
116
|
-
this.log.debug(`Generated witness for ${circuitType}`, {
|
|
117
|
-
eventName: 'circuit-witness-generation',
|
|
118
|
-
circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
|
|
119
|
-
duration: timer.ms(),
|
|
120
|
-
inputSize: inputs.toBuffer().length,
|
|
121
|
-
outputSize: output.toBuffer().length,
|
|
122
|
-
});
|
|
123
|
-
const proofOutput = await this.createProof(directory, outputWitness, Buffer.from(compiledCircuit.bytecode, 'base64'), circuitType);
|
|
124
|
-
if (proofOutput.proof.proof.length != NESTED_RECURSIVE_PROOF_LENGTH) {
|
|
125
|
-
throw new Error(`Incorrect proof length`);
|
|
126
|
-
}
|
|
127
|
-
const nestedProof = proofOutput.proof;
|
|
128
|
-
const kernelOutput = {
|
|
129
|
-
publicInputs: output,
|
|
130
|
-
proof: nestedProof,
|
|
131
|
-
verificationKey: proofOutput.verificationKey,
|
|
132
|
-
};
|
|
133
|
-
return kernelOutput;
|
|
134
|
-
}
|
|
135
|
-
async createProof(directory, partialWitness, bytecode, circuitType, appCircuitName) {
|
|
136
|
-
const compressedBincodedWitness = serializeWitness(partialWitness);
|
|
137
|
-
const inputsWitnessFile = join(directory, 'witness.gz');
|
|
138
|
-
await fs.writeFile(inputsWitnessFile, compressedBincodedWitness);
|
|
139
|
-
this.log.debug(`Written ${inputsWitnessFile}`);
|
|
140
|
-
const dbgCircuitName = appCircuitName ? `(${appCircuitName})` : '';
|
|
141
|
-
this.log.info(`Proving ${circuitType}${dbgCircuitName} circuit...`);
|
|
142
|
-
const timer = new Timer();
|
|
143
|
-
const provingResult = await generateProof(this.bbBinaryPath, directory, circuitType, bytecode, inputsWitnessFile, this.log.debug);
|
|
144
|
-
if (provingResult.status === BB_RESULT.FAILURE) {
|
|
145
|
-
this.log.error(`Failed to generate proof for ${circuitType}${dbgCircuitName}: ${provingResult.reason}`);
|
|
146
|
-
throw new Error(provingResult.reason);
|
|
147
|
-
}
|
|
148
|
-
this.log.info(`Generated ${circuitType}${dbgCircuitName} circuit proof in ${Math.ceil(timer.ms())} ms`);
|
|
149
|
-
if (circuitType === 'App') {
|
|
150
|
-
const vkData = await extractVkData(directory);
|
|
151
|
-
const proof = await this.readProofAsFields(directory, circuitType, vkData);
|
|
152
|
-
this.log.debug(`Generated proof`, {
|
|
153
|
-
eventName: 'circuit-proving',
|
|
154
|
-
circuitName: 'app-circuit',
|
|
155
|
-
duration: provingResult.durationMs,
|
|
156
|
-
inputSize: compressedBincodedWitness.length,
|
|
157
|
-
proofSize: proof.binaryProof.buffer.length,
|
|
158
|
-
appCircuitName,
|
|
159
|
-
circuitSize: vkData.circuitSize,
|
|
160
|
-
numPublicInputs: vkData.numPublicInputs,
|
|
161
|
-
});
|
|
162
|
-
return { proof, verificationKey: vkData.keyAsFields };
|
|
163
|
-
}
|
|
164
|
-
const vkData = await this.updateVerificationKeyAfterProof(directory, circuitType);
|
|
165
|
-
const proof = await this.readProofAsFields(directory, circuitType, vkData);
|
|
166
|
-
await this.verifyProofForProtocolCircuit(circuitType, proof.binaryProof);
|
|
167
|
-
this.log.debug(`Generated proof`, {
|
|
168
|
-
circuitName: mapProtocolArtifactNameToCircuitName(circuitType),
|
|
169
|
-
duration: provingResult.durationMs,
|
|
170
|
-
eventName: 'circuit-proving',
|
|
171
|
-
inputSize: compressedBincodedWitness.length,
|
|
172
|
-
proofSize: proof.binaryProof.buffer.length,
|
|
173
|
-
circuitSize: vkData.circuitSize,
|
|
174
|
-
numPublicInputs: vkData.numPublicInputs,
|
|
175
|
-
});
|
|
176
|
-
return { proof, verificationKey: vkData.keyAsFields };
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Parses and returns the proof data stored at the specified directory
|
|
180
|
-
* @param filePath - The directory containing the proof data
|
|
181
|
-
* @param circuitType - The type of circuit proven
|
|
182
|
-
* @returns The proof
|
|
183
|
-
*/
|
|
184
|
-
async readProofAsFields(filePath, circuitType, vkData) {
|
|
185
|
-
const [binaryProof, proofString] = await Promise.all([
|
|
186
|
-
fs.readFile(`${filePath}/${PROOF_FILENAME}`),
|
|
187
|
-
fs.readFile(`${filePath}/${PROOF_FIELDS_FILENAME}`, { encoding: 'utf-8' }),
|
|
188
|
-
]);
|
|
189
|
-
const json = JSON.parse(proofString);
|
|
190
|
-
const fields = json.map(Fr.fromString);
|
|
191
|
-
const numPublicInputs = circuitType === 'App' ? vkData.numPublicInputs : vkData.numPublicInputs - AGGREGATION_OBJECT_LENGTH;
|
|
192
|
-
const fieldsWithoutPublicInputs = fields.slice(numPublicInputs);
|
|
193
|
-
this.log.debug(`Circuit type: ${circuitType}, complete proof length: ${fields.length}, without public inputs: ${fieldsWithoutPublicInputs.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProof.length}`);
|
|
194
|
-
const proof = new RecursiveProof(fieldsWithoutPublicInputs, new Proof(binaryProof, vkData.numPublicInputs), true);
|
|
195
|
-
return proof;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmJfbmF0aXZlX3Byb29mX2NyZWF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmVyL2JiX25hdGl2ZV9wcm9vZl9jcmVhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsRUFBRSxFQUNGLDZCQUE2QixFQVE3QixLQUFLLEVBQ0wsc0JBQXNCLEVBQ3RCLGNBQWMsR0FHZixNQUFNLG9CQUFvQixDQUFDO0FBQzVCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDMUQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ2hELE9BQU8sRUFDTCxzQkFBc0IsRUFFdEIsNkJBQTZCLEVBQzdCLGtCQUFrQixFQUNsQiwwQ0FBMEMsRUFDMUMsNkNBQTZDLEVBQzdDLDJDQUEyQyxFQUMzQyw4Q0FBOEMsRUFDOUMsMkNBQTJDLEVBQzNDLDhDQUE4QyxFQUM5QyxzREFBc0QsRUFDdEQsMENBQTBDLEVBQzFDLDZDQUE2QyxFQUM3QyxrREFBa0QsR0FDbkQsTUFBTSxxQ0FBcUMsQ0FBQztBQUM3QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFHakQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFFeEQsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUU1QixPQUFPLEVBQUUsU0FBUyxFQUFFLHFCQUFxQixFQUFFLGNBQWMsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDaEgsT0FBTyxFQUFFLG9DQUFvQyxFQUFF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
|