-
Notifications
You must be signed in to change notification settings - Fork 271
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
Changes from all commits
e8c3a26
17b0fed
bec2548
ec7926b
69c3b37
d5db078
faba007
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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>) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just pointing this change out! Without it the proofs generated by |
||
honk_recursion = true; | ||
} | ||
|
||
|
@@ -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"); | ||
|
@@ -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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'; | ||
|
@@ -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> { | ||
|
@@ -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); | ||
|
||
|
@@ -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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like we could drop pks altogether, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why did we even need this functionality before? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
}; | ||
} | ||
|
@@ -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, | ||
}; | ||
} | ||
|
||
|
@@ -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 | ||
|
@@ -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, | ||
); | ||
|
@@ -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, | ||
); | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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) { | ||
|
@@ -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); | ||
} | ||
|
||
/** | ||
|
@@ -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 | ||
|
@@ -851,7 +856,7 @@ export async function generateContractForCircuit( | |
workingDirectory, | ||
circuitName, | ||
compiledCircuit, | ||
'vk', | ||
'ultra_keccak_honk', | ||
log, | ||
force, | ||
); | ||
|
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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'; | ||
|
||
|
@@ -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!`; | ||
|
@@ -224,6 +230,7 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver { | |
} | ||
|
||
private async verifyProofFromKey( | ||
flavor: UltraHonkFlavor, | ||
verificationKey: Buffer, | ||
proof: Proof, | ||
logFunction: (message: string) => void = () => {}, | ||
|
@@ -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); | ||
} | ||
|
@@ -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), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is |
||
this.log.debug, | ||
); | ||
|
||
if (vkResult.status === BB_RESULT.FAILURE) { | ||
this.log.error(`Failed to generate proof for ${circuitType}${dbgCircuitName}: ${vkResult.reason}`); | ||
|
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks okay to me
There was a problem hiding this comment.
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 whereflavor="ultra_honk" | "ultra_keccak_honk"