Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ultra keccak honk verifier #8261

Merged
merged 7 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have someone from crypto team check that it's ok to change these names. cc @ludamad ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks okay to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the names to be consistent and so that I could do prove_${flavor} in typescript where flavor="ultra_honk" | "ultra_keccak_honk"

Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,7 @@ void prove_honk_output_all(const std::string& bytecodePath,
using VerificationKey = Flavor::VerificationKey;

bool honk_recursion = false;
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
if constexpr (IsAnyOf<Flavor, UltraFlavor, UltraKeccakFlavor>) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pointing this change out! Without it the proofs generated by prove_ultra_keccak_honk_output_all couldn't be verified.

honk_recursion = true;
}

Expand Down Expand Up @@ -1478,12 +1478,12 @@ int main(int argc, char* argv[])
} else if (command == "prove_keccak_ultra_honk") {
std::string output_path = get_option(args, "-o", "./proofs/proof");
prove_honk<UltraKeccakFlavor>(bytecode_path, witness_path, output_path);
} else if (command == "prove_keccak_ultra_honk_output_all") {
} else if (command == "prove_ultra_keccak_honk_output_all") {
std::string output_path = get_option(args, "-o", "./proofs/proof");
prove_honk_output_all<UltraKeccakFlavor>(bytecode_path, witness_path, output_path);
} else if (command == "verify_ultra_honk") {
return verify_honk<UltraFlavor>(proof_path, vk_path) ? 0 : 1;
} else if (command == "verify_keccak_ultra_honk") {
} else if (command == "verify_ultra_keccak_honk") {
return verify_honk<UltraKeccakFlavor>(proof_path, vk_path) ? 0 : 1;
} else if (command == "write_vk_ultra_honk") {
std::string output_path = get_option(args, "-o", "./target/vk");
Expand All @@ -1508,6 +1508,9 @@ int main(int argc, char* argv[])
} else if (command == "vk_as_fields_mega_honk") {
std::string output_path = get_option(args, "-o", vk_path + "_fields.json");
vk_as_fields_honk<MegaFlavor>(vk_path, output_path);
} else if (command == "vk_as_fields_ultra_keccak_honk") {
std::string output_path = get_option(args, "-o", vk_path + "_fields.json");
vk_as_fields_honk<UltraKeccakFlavor>(vk_path, output_path);
} else {
std::cerr << "Unknown command: " << command << "\n";
return 1;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ protocol-verification-keys:
rollup-verifier-contract:
FROM +bb-cli
COPY --dir +protocol-verification-keys/usr/src/bb /usr/src
RUN --entrypoint write-contract -c RootRollupArtifact -n UltraHonkVerifier.sol
RUN --entrypoint write-contract -c BlockRootRollupArtifact -n UltraHonkVerifier.sol
SAVE ARTIFACT /usr/src/bb /usr/src/bb

txe:
Expand Down
4 changes: 3 additions & 1 deletion yarn-project/bb-prover/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"tslib": "^2.4.0"
},
"devDependencies": {
"@aztec/ethereum": "workspace:^",
"@jest/globals": "^29.5.0",
"@types/jest": "^29.5.0",
"@types/memdown": "^3.0.0",
Expand All @@ -81,7 +82,8 @@
"jest": "^29.5.0",
"jest-mock-extended": "^3.0.3",
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
"typescript": "^5.0.4",
"viem": "^2.7.15"
},
"files": [
"dest",
Expand Down
36 changes: 3 additions & 33 deletions yarn-project/bb-prover/src/bb/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,6 @@ export function getProgram(log: LogFn): Command {
log(Object.keys(ProtocolCircuitArtifacts).reduce((prev: string, x: string) => prev.concat(`\n${x}`)));
});

program
.command('write-pk')
.description('Generates the proving key for the specified circuit')
.requiredOption(
'-w, --working-directory <string>',
'A directory to use for storing input/output files',
BB_WORKING_DIRECTORY,
)
.requiredOption('-b, --bb-path <string>', 'The path to the BB binary', BB_BINARY_PATH)
.requiredOption('-c, --circuit <string>', 'The name of a protocol circuit')
.action(async options => {
const compiledCircuit = ProtocolCircuitArtifacts[options.circuit as ProtocolArtifact];
if (!compiledCircuit) {
log(`Failed to find circuit ${options.circuit}`);
return;
}
try {
await fs.access(options.workingDirectory, fs.constants.W_OK);
} catch (error) {
log(`Working directory does not exist`);
return;
}
await generateKeyForNoirCircuit(
options.bbPath,
options.workingDirectory,
options.circuit,
compiledCircuit,
'pk',
log,
);
});

program
.command('write-vk')
.description('Generates the verification key for the specified circuit')
Expand All @@ -67,6 +35,7 @@ export function getProgram(log: LogFn): Command {
)
.requiredOption('-b, --bb-path <string>', 'The path to the BB binary', BB_BINARY_PATH)
.requiredOption('-c, --circuit <string>', 'The name of a protocol circuit')
.requiredOption('-f, --flavor <string>', 'The name of the verification key flavor', 'ultra_honk')
.action(async options => {
const compiledCircuit = ProtocolCircuitArtifacts[options.circuit as ProtocolArtifact];
if (!compiledCircuit) {
Expand All @@ -84,7 +53,8 @@ export function getProgram(log: LogFn): Command {
options.workingDirectory,
options.circuit,
compiledCircuit,
'vk',
options.flavor,
// (options.circuit as ServerProtocolArtifact) === 'RootRollupArtifact' ? 'ultra_keccak_honk' : 'ultra_honk',
log,
);
});
Expand Down
37 changes: 21 additions & 16 deletions yarn-project/bb-prover/src/bb/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import * as proc from 'child_process';
import * as fs from 'fs/promises';
import { basename, dirname, join } from 'path';

import { type UltraHonkFlavor } from '../honk.js';

export const VK_FILENAME = 'vk';
export const VK_FIELDS_FILENAME = 'vk_fields.json';
export const PROOF_FILENAME = 'proof';
Expand Down Expand Up @@ -113,7 +115,7 @@ export async function generateKeyForNoirCircuit(
workingDirectory: string,
circuitName: string,
compiledCircuit: NoirCompiledCircuit,
key: 'vk' | 'pk',
flavor: UltraHonkFlavor,
log: LogFn,
force = false,
): Promise<BBSuccess | BBFailure> {
Expand All @@ -123,7 +125,7 @@ export async function generateKeyForNoirCircuit(
// The bytecode hash file is also written here as /workingDirectory/pk/BaseParityArtifact/bytecode-hash
// The bytecode is written to e.g. /workingDirectory/pk/BaseParityArtifact/bytecode
// The bytecode is removed after the key is generated, leaving just the hash file
const circuitOutputDirectory = `${workingDirectory}/${key}/${circuitName}`;
const circuitOutputDirectory = `${workingDirectory}/vk/${circuitName}`;
const outputPath = `${circuitOutputDirectory}`;
const bytecodeHash = sha256(bytecode);

Expand All @@ -148,20 +150,20 @@ export async function generateKeyForNoirCircuit(
// args are the output path and the input bytecode path
const args = ['-o', `${outputPath}/${VK_FILENAME}`, '-b', bytecodePath];
const timer = new Timer();
let result = await executeBB(pathToBB, `write_${key}_ultra_honk`, args, log);
let result = await executeBB(pathToBB, `write_vk_${flavor}`, args, log);
// If we succeeded and the type of key if verification, have bb write the 'fields' version too
if (result.status == BB_RESULT.SUCCESS && key === 'vk') {
if (result.status == BB_RESULT.SUCCESS) {
const asFieldsArgs = ['-k', `${outputPath}/${VK_FILENAME}`, '-o', `${outputPath}/${VK_FIELDS_FILENAME}`, '-v'];
result = await executeBB(pathToBB, `vk_as_fields_ultra_honk`, asFieldsArgs, log);
result = await executeBB(pathToBB, `vk_as_fields_${flavor}`, asFieldsArgs, log);
}
const duration = timer.ms();

if (result.status == BB_RESULT.SUCCESS) {
return {
status: BB_RESULT.SUCCESS,
durationMs: duration,
pkPath: key === 'pk' ? outputPath : undefined,
vkPath: key === 'vk' ? outputPath : undefined,
pkPath: undefined,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we could drop pks altogether, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't generate the proving keys ahead of time so I just removed the code, yep.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did we even need this functionality before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucasxia01 I think it was leftover from the time we were integrating recursive proofs into the orchestrator :)

vkPath: outputPath,
proofPath: undefined,
};
}
Expand All @@ -179,8 +181,8 @@ export async function generateKeyForNoirCircuit(
return {
status: BB_RESULT.ALREADY_PRESENT,
durationMs: 0,
pkPath: key === 'pk' ? outputPath : undefined,
vkPath: key === 'vk' ? outputPath : undefined,
pkPath: undefined,
vkPath: outputPath,
};
}

Expand Down Expand Up @@ -261,6 +263,7 @@ export async function computeVerificationKey(
workingDirectory: string,
circuitName: string,
bytecode: Buffer,
flavor: UltraHonkFlavor,
log: LogFn,
): Promise<BBFailure | BBSuccess> {
// Check that the working directory exists
Expand Down Expand Up @@ -293,7 +296,7 @@ export async function computeVerificationKey(
};
let result = await executeBB(
pathToBB,
'write_vk_ultra_honk',
`write_vk_${flavor}`,
['-o', outputPath, '-b', bytecodePath, '-v'],
logFunction,
);
Expand All @@ -302,7 +305,7 @@ export async function computeVerificationKey(
}
result = await executeBB(
pathToBB,
'vk_as_fields_ultra_honk',
`vk_as_fields_${flavor}`,
['-o', outputPath + '_fields.json', '-k', outputPath, '-v'],
logFunction,
);
Expand Down Expand Up @@ -343,6 +346,7 @@ export async function generateProof(
circuitName: string,
bytecode: Buffer,
inputWitnessFile: string,
flavor: UltraHonkFlavor,
log: LogFn,
): Promise<BBFailure | BBSuccess> {
// Check that the working directory exists
Expand All @@ -355,7 +359,7 @@ export async function generateProof(
// The bytecode is written to e.g. /workingDirectory/BaseParityArtifact-bytecode
const bytecodePath = `${workingDirectory}/${circuitName}-bytecode`;

// The proof is written to e.g. /workingDirectory/proof
// The proof is written to e.g. /workingDirectory/ultra_honk/proof
const outputPath = `${workingDirectory}`;

const binaryPresent = await fs
Expand All @@ -374,7 +378,7 @@ export async function generateProof(
const logFunction = (message: string) => {
log(`${circuitName} BB out - ${message}`);
};
const result = await executeBB(pathToBB, 'prove_ultra_honk_output_all', args, logFunction);
const result = await executeBB(pathToBB, `prove_${flavor}_output_all`, args, logFunction);
const duration = timer.ms();

if (result.status == BB_RESULT.SUCCESS) {
Expand Down Expand Up @@ -599,9 +603,10 @@ export async function verifyProof(
pathToBB: string,
proofFullPath: string,
verificationKeyPath: string,
ultraHonkFlavor: UltraHonkFlavor,
log: LogFn,
): Promise<BBFailure | BBSuccess> {
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'verify_ultra_honk', log);
return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, `verify_${ultraHonkFlavor}`, log);
}

/**
Expand Down Expand Up @@ -674,7 +679,7 @@ async function verifyProofInternal(
pathToBB: string,
proofFullPath: string,
verificationKeyPath: string,
command: 'verify_ultra_honk' | 'avm_verify',
command: 'verify_ultra_honk' | 'verify_ultra_keccak_honk' | 'avm_verify',
log: LogFn,
): Promise<BBFailure | BBSuccess> {
const binaryPresent = await fs
Expand Down Expand Up @@ -851,7 +856,7 @@ export async function generateContractForCircuit(
workingDirectory,
circuitName,
compiledCircuit,
'vk',
'ultra_keccak_honk',
log,
force,
);
Expand Down
32 changes: 32 additions & 0 deletions yarn-project/bb-prover/src/honk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { RECURSIVE_PROOF_LENGTH, VERIFICATION_KEY_LENGTH_IN_FIELDS } from '@aztec/circuits.js';
import { type ProtocolArtifact } from '@aztec/noir-protocol-circuits-types';

export type UltraHonkFlavor = 'ultra_honk' | 'ultra_keccak_honk';

const UltraKeccakHonkCircuits = ['BlockRootRollupArtifact'] as const;
type UltraKeccakHonkCircuits = (typeof UltraKeccakHonkCircuits)[number];
type UltraHonkCircuits = Exclude<ProtocolArtifact, UltraKeccakHonkCircuits>;

export function getUltraHonkFlavorForCircuit(artifact: UltraKeccakHonkCircuits): 'ultra_keccak_honk';
export function getUltraHonkFlavorForCircuit(artifact: UltraHonkCircuits): 'ultra_honk';
export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor;
export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor {
return isUltraKeccakHonkCircuit(artifact) ? 'ultra_keccak_honk' : 'ultra_honk';
}

function isUltraKeccakHonkCircuit(artifact: ProtocolArtifact): artifact is UltraKeccakHonkCircuits {
return UltraKeccakHonkCircuits.includes(artifact as UltraKeccakHonkCircuits);
}

// TODO (alexg) remove these once UltraKeccakHonk proofs are the same size as regular UltraHonk proofs
// see https://github.com/AztecProtocol/aztec-packages/pull/8243
export function getExpectedVerificationKeyLength(artifact: ProtocolArtifact): number {
return getUltraHonkFlavorForCircuit(artifact) === 'ultra_keccak_honk' ? 120 : VERIFICATION_KEY_LENGTH_IN_FIELDS;
}

export function getExpectedProofLength(artifact: UltraKeccakHonkCircuits): 393;
export function getExpectedProofLength(artifact: UltraHonkCircuits): typeof RECURSIVE_PROOF_LENGTH;
export function getExpectedProofLength(artifact: ProtocolArtifact): number;
export function getExpectedProofLength(artifact: ProtocolArtifact): number {
return isUltraKeccakHonkCircuit(artifact) ? 393 : RECURSIVE_PROOF_LENGTH;
}
20 changes: 17 additions & 3 deletions yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
verifyProof,
} from '../bb/execute.js';
import { type BBConfig } from '../config.js';
import { type UltraHonkFlavor, getUltraHonkFlavorForCircuit } from '../honk.js';
import { mapProtocolArtifactNameToCircuitName } from '../stats.js';
import { extractVkData } from '../verification_key/verification_key_data.js';

Expand Down Expand Up @@ -213,7 +214,12 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {
this.log.debug(`${circuitType} BB out - ${message}`);
};

const result = await this.verifyProofFromKey(verificationKey.keyAsBytes, proof, logFunction);
const result = await this.verifyProofFromKey(
getUltraHonkFlavorForCircuit(circuitType),
verificationKey.keyAsBytes,
proof,
logFunction,
);

if (result.status === BB_RESULT.FAILURE) {
const errorMessage = `Failed to verify ${circuitType} proof!`;
Expand All @@ -224,6 +230,7 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {
}

private async verifyProofFromKey(
flavor: UltraHonkFlavor,
verificationKey: Buffer,
proof: Proof,
logFunction: (message: string) => void = () => {},
Expand All @@ -234,7 +241,7 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {

await fs.writeFile(proofFileName, proof.buffer);
await fs.writeFile(verificationKeyPath, verificationKey);
return await verifyProof(this.bbBinaryPath, proofFileName, verificationKeyPath!, logFunction);
return await verifyProof(this.bbBinaryPath, proofFileName, verificationKeyPath!, flavor, logFunction);
};
return await this.runInDirectory(operation);
}
Expand Down Expand Up @@ -301,7 +308,14 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver {

const timer = new Timer();

const vkResult = await computeVerificationKey(this.bbBinaryPath, directory, circuitType, bytecode, this.log.debug);
const vkResult = await computeVerificationKey(
this.bbBinaryPath,
directory,
circuitType,
bytecode,
circuitType === 'App' ? 'ultra_honk' : getUltraHonkFlavorForCircuit(circuitType),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is App outside the check? If it's because App is not a ProtocolArtifact, then let's change the signature of getUltraHonkFlavorForCircuit to accept a ProtocolArtifact | 'App'.

this.log.debug,
);

if (vkResult.status === BB_RESULT.FAILURE) {
this.log.error(`Failed to generate proof for ${circuitType}${dbgCircuitName}: ${vkResult.reason}`);
Expand Down
Loading
Loading