Skip to content

Commit

Permalink
chore(bb): refactor bb CLI interface (#1672)
Browse files Browse the repository at this point in the history
Resolves #1671
  • Loading branch information
kevaundray authored Aug 21, 2023
1 parent cf61108 commit a5bf6e0
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 33 deletions.
149 changes: 134 additions & 15 deletions circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,41 @@ acir_format::acir_format get_constraint_system(std::string const& bytecode_path)
return acir_format::circuit_buf_to_acir_format(bytecode);
}

/**
* @brief Proves and Verifies an ACIR circuit
*
* Communication:
* - stdout: A boolean value is printed to stdout indicating whether the proof is valid
*
* @param bytecodePath Path to the file containing the serialized circuit
* @param witnessPath Path to the file containing the serialized witness
* @param recursive Whether to use recursive proof generation of non-recursive
* @return true if the proof is valid
* @return false if the proof is invalid
*/
bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessPath, bool recursive)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);
auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);
auto verified = acir_composer->verify_proof(proof, recursive);
info("verified: ", verified);

std::cout << verified << std::endl;
return verified;
}

/**
* @brief Creates a proof for an ACIR circuit
*
* Communication:
* - stdout: The proof is written to stdout as a byte array
* - Filesystem: The proof is written to the path specified by outputPath
*
* @param bytecodePath Path to the file containing the serialized circuit
* @param witnessPath Path to the file containing the serialized witness
* @param recursive Whether to use recursive proof generation of non-recursive
* @param outputPath Path to write the proof to
*/
void prove(const std::string& bytecodePath,
const std::string& witnessPath,
bool recursive,
Expand All @@ -56,62 +80,154 @@ void prove(const std::string& bytecodePath,
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);
auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive);

std::cout << proof << std::endl;
write_file(outputPath, proof);

info("proof written to: ", outputPath);
}

/**
* @brief Computes the number of Barretenberg specific gates needed to create a proof for the specific ACIR circuit
*
* Communication:
* - stdout: The number of gates is written to stdout
*
* @param bytecodePath Path to the file containing the serialized circuit
*/
void gateCount(const std::string& bytecodePath)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto constraint_system = get_constraint_system(bytecodePath);
acir_composer->create_circuit(constraint_system);
info("gates: ", acir_composer->get_total_circuit_size());
auto gate_count = acir_composer->get_total_circuit_size();
std::cout << gate_count << std::endl;
}

/**
* @brief Verifies a proof for an ACIR circuit
*
* Note: The fact that the proof was computed originally by parsing an ACIR circuit is not of importance
* because this method uses the verification key to verify the proof.
*
* Communication:
* - stdout: A boolean value is printed to stdout indicating whether the proof is valid
*
* @param proof_path Path to the file containing the serialized proof
* @param recursive Whether to use recursive proof generation of non-recursive
* @param vk_path Path to the file containing the serialized verification key
* @return true If the proof is valid
* @return false If the proof is invalid
*/
bool verify(const std::string& proof_path, bool recursive, const std::string& vk_path)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto vk_data = from_buffer<plonk::verification_key_data>(read_file(vk_path));
acir_composer->load_verification_key(barretenberg::srs::get_crs_factory(), std::move(vk_data));
auto verified = acir_composer->verify_proof(read_file(proof_path), recursive);
info("verified: ", verified);

std::cout << verified << std::endl;
return verified;
}

/**
* @brief Writes a verification key for an ACIR circuit to a file
*
* Communication:
* - stdout: The verification key is written to stdout as a byte array
* - Filesystem: The verification key is written to the path specified by outputPath
*
* @param bytecodePath Path to the file containing the serialized circuit
* @param outputPath Path to write the verification key to
*/
void writeVk(const std::string& bytecodePath, const std::string& outputPath)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto constraint_system = get_constraint_system(bytecodePath);
acir_composer->init_proving_key(srs::get_crs_factory(), constraint_system);
auto vk = acir_composer->init_verification_key();
write_file(outputPath, to_buffer(*vk));
auto serialized_vk = to_buffer(*vk);

std::cout << serialized_vk << std::endl;
write_file(outputPath, serialized_vk);

info("vk written to: ", outputPath);
}

/**
* @brief Writes a Solidity verifier contract for an ACIR circuit to a file
*
* Communication:
* - stdout: The Solidity verifier contract is written to stdout as a string
* - Filesystem: The Solidity verifier contract is written to the path specified by outputPath
*
* Note: The fact that the contract was computed is for an ACIR circuit is not of importance
* because this method uses the verification key to compute the Solidity verifier contract.
*
* @param output_path Path to write the contract to
* @param vk_path Path to the file containing the serialized verification key
*/
void contract(const std::string& output_path, const std::string& vk_path)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto vk_data = from_buffer<plonk::verification_key_data>(read_file(vk_path));
acir_composer->load_verification_key(barretenberg::srs::get_crs_factory(), std::move(vk_data));
auto contract = acir_composer->get_solidity_verifier();
if (output_path == "-") {
info(contract);
} else {
write_file(output_path, { contract.begin(), contract.end() });
info("contract written to: ", output_path);
}
}

std::cout << contract << std::endl;
write_file(output_path, { contract.begin(), contract.end() });

info("contract written to: ", output_path);
}
/**
* @brief Converts a proof from a byte array into a list of field elements
*
* Why is this needed?
*
* The proof computed by the non-recursive proof system is a byte array. This is fine since the proof will be verified
* either natively or in a Solidity verifier. For the recursive proof system, the proof is verified in a circuit where
* it is cheaper to work with field elements than byte arrays. This method converts the proof into a list of field
* elements which can be used in the recursive proof system.
*
* This is an optimization which unfortunately leaks through the API. The repercussions of this are that users need to
* convert proofs which are byte arrays to proofs which are lists of field elements, using the below method.
*
* Ideally, we find out what is the cost to convert this in the circuit and if it is not too expensive, we pass the
* byte array directly to the circuit and convert it there. This also applies to the `vkAsFields` method.
*
* Communication:
* - stdout: The proof as a list of field elements is written to stdout as a string
* - Filesystem: The proof as a list of field elements is written to the path specified by outputPath
*
*
* @param proof_path Path to the file containing the serialized proof
* @param vk_path Path to the file containing the serialized verification key
* @param output_path Path to write the proof to
*/
void proofAsFields(const std::string& proof_path, std::string const& vk_path, const std::string& output_path)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
auto vk_data = from_buffer<plonk::verification_key_data>(read_file(vk_path));
auto data = acir_composer->serialize_proof_into_fields(read_file(proof_path), vk_data.num_public_inputs);
auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");

std::cout << json << std::endl;
write_file(output_path, { json.begin(), json.end() });

info("proof as fields written to: ", output_path);
}

/**
* @brief Converts a verification key from a byte array into a list of field elements
*
* Why is this needed?
* This follows the same rationale as `proofAsFields`.
*
* Communication:
* - stdout: The verification key as a list of field elements is written to stdout as a string
* - Filesystem: The verification key as a list of field elements is written to the path specified by outputPath
*
* @param vk_path Path to the file containing the serialized verification key
* @param output_path Path to write the verification key to
*/
void vkAsFields(const std::string& vk_path, const std::string& output_path)
{
auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose);
Expand All @@ -123,7 +239,10 @@ void vkAsFields(const std::string& vk_path, const std::string& output_path)
std::rotate(data.begin(), data.end() - 1, data.end());

auto json = format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]");

std::cout << json << std::endl;
write_file(output_path, { json.begin(), json.end() });

info("vk as fields written to: ", output_path);
}

Expand Down Expand Up @@ -167,7 +286,7 @@ int main(int argc, char* argv[])
} else if (command == "gates") {
gateCount(bytecode_path);
} else if (command == "verify") {
verify(proof_path, recursive, vk_path);
return verify(proof_path, recursive, vk_path) ? 0 : 1;
} else if (command == "contract") {
std::string output_path = getOption(args, "-o", "./target/contract.sol");
contract(output_path, vk_path);
Expand Down
1 change: 1 addition & 0 deletions circuits/cpp/barretenberg/ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"resolve-typescript-plugin": "^2.0.1",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"tslib": "^2.6.2",
"tsconfig-paths-webpack-plugin": "^4.0.1",
"typescript": "^5.0.4",
"webpack": "^5.82.1",
Expand Down
44 changes: 26 additions & 18 deletions circuits/cpp/barretenberg/ts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string,

debug(`verifying...`);
const verified = await api.acirVerifyProof(acirComposer, proof, isRecursive);
console.log(`verified: ${verified}`);
process.stdout.write(`${verified}`);
return verified;
} finally {
await api.destroy();
Expand All @@ -107,8 +107,10 @@ export async function prove(
const proof = await api.acirCreateProof(acirComposer, bytecode, witness, isRecursive);
debug(`done.`);

process.stdout.write(proof);
writeFileSync(outputPath, proof);
console.log(`proof written to: ${outputPath}`);

debug(`proof written to: ${outputPath}`);
} finally {
await api.destroy();
}
Expand All @@ -117,7 +119,7 @@ export async function prove(
export async function gateCount(bytecodePath: string) {
const api = await newBarretenbergApiAsync(1);
try {
console.log(`gates: ${await getGates(bytecodePath, api)}`);
process.stdout.write(`${await getGates(bytecodePath, api)}`);
} finally {
await api.destroy();
}
Expand All @@ -128,7 +130,8 @@ export async function verify(proofPath: string, isRecursive: boolean, vkPath: st
try {
await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath)));
const verified = await api.acirVerifyProof(acirComposer, readFileSync(proofPath), isRecursive);
console.log(`verified: ${verified}`);

process.stdout.write(`${verified}`);
return verified;
} finally {
await api.destroy();
Expand All @@ -140,12 +143,11 @@ export async function contract(outputPath: string, vkPath: string) {
try {
await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath)));
const contract = await api.acirGetSolidityVerifier(acirComposer);
if (outputPath === '-') {
console.log(contract);
} else {
writeFileSync(outputPath, contract);
console.log(`contract written to: ${outputPath}`);
}

process.stdout.write(contract);
writeFileSync(outputPath, contract);

debug(`contract written to: ${outputPath}`);
} finally {
await api.destroy();
}
Expand All @@ -160,12 +162,11 @@ export async function writeVk(bytecodePath: string, crsPath: string, outputPath:

debug('initing verification key...');
const vk = await api.acirGetVerificationKey(acirComposer);
if (outputPath === '-') {
process.stdout.write(vk);
} else {
writeFileSync(outputPath, vk);
console.log(`vk written to: ${outputPath}`);
}

process.stdout.write(vk);
writeFileSync(outputPath, vk);

debug(`vk written to: ${outputPath}`);
} finally {
await api.destroy();
}
Expand All @@ -181,8 +182,11 @@ export async function proofAsFields(proofPath: string, numInnerPublicInputs: num
readFileSync(proofPath),
numInnerPublicInputs,
);
const jsonProofAsFields = JSON.stringify(proofAsFields.map(f => f.toString()));

process.stdout.write(jsonProofAsFields);
writeFileSync(outputPath, jsonProofAsFields);

writeFileSync(outputPath, JSON.stringify(proofAsFields.map(f => f.toString())));
debug('done.');
} finally {
await api.destroy();
Expand All @@ -197,7 +201,11 @@ export async function vkAsFields(vkPath: string, vkeyOutputPath: string) {
await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath)));
const [vkAsFields, vkHash] = await api.acirSerializeVerificationKeyIntoFields(acirComposer);
const output = [vkHash, ...vkAsFields].map(f => f.toString());
writeFileSync(vkeyOutputPath, JSON.stringify(output));
const jsonVKAsFields = JSON.stringify(output);

process.stdout.write(jsonVKAsFields);
writeFileSync(vkeyOutputPath, jsonVKAsFields);

debug('done.');
} finally {
await api.destroy();
Expand Down
8 changes: 8 additions & 0 deletions circuits/cpp/barretenberg/ts/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ __metadata:
ts-jest: ^29.1.0
ts-loader: ^9.4.2
tsconfig-paths-webpack-plugin: ^4.0.1
tslib: ^2.6.2
typescript: ^5.0.4
webpack: ^5.82.1
webpack-cli: ^5.1.1
Expand Down Expand Up @@ -6506,6 +6507,13 @@ __metadata:
languageName: node
linkType: hard

"tslib@npm:^2.6.2":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
languageName: node
linkType: hard

"tsutils@npm:^3.21.0":
version: 3.21.0
resolution: "tsutils@npm:3.21.0"
Expand Down

0 comments on commit a5bf6e0

Please sign in to comment.