From 2670be04ba8b48ea47d4c62647047e06d3c20ea3 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sat, 19 Aug 2023 21:59:45 +0000 Subject: [PATCH 1/8] add docs and standardize interface --- .../cpp/src/barretenberg/bb/main.cpp | 150 ++++++++++++++++-- 1 file changed, 134 insertions(+), 16 deletions(-) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp index bd6c9c79bf1..b4635266f6f 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -36,6 +36,18 @@ 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); @@ -43,10 +55,22 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP 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, @@ -56,62 +80,153 @@ 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; } - -bool verify(const std::string& proof_path, bool recursive, const std::string& vk_path) +/** + * @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 + * @return false + */ +void 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(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); - return verified; + + std::cout << verified << std::endl; } +/** + * @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(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(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); @@ -123,7 +238,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); } From 52f06ba5685fa0baf1dbec84d15d6410a35566f7 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sat, 19 Aug 2023 22:00:44 +0000 Subject: [PATCH 2/8] copy same interface as bb binary --- circuits/cpp/barretenberg/ts/src/main.ts | 48 ++++++++++++++---------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/circuits/cpp/barretenberg/ts/src/main.ts b/circuits/cpp/barretenberg/ts/src/main.ts index a83f14aac30..85744de4a57 100755 --- a/circuits/cpp/barretenberg/ts/src/main.ts +++ b/circuits/cpp/barretenberg/ts/src/main.ts @@ -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(); @@ -106,9 +106,11 @@ export async function prove( const witness = getWitness(witnessPath); 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(); } @@ -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(); } @@ -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(); @@ -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(); } @@ -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(); } @@ -181,8 +182,11 @@ export async function proofAsFields(proofPath: string, numInnerPublicInputs: num readFileSync(proofPath), numInnerPublicInputs, ); - - writeFileSync(outputPath, JSON.stringify(proofAsFields.map(f => f.toString()))); + const jsonProofAsFields = JSON.stringify(proofAsFields.map(f => f.toString())); + + process.stdout.write(jsonProofAsFields); + writeFileSync(outputPath, jsonProofAsFields); + debug('done.'); } finally { await api.destroy(); @@ -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(); From fe6b4c27cbd6a721cd88fc20127753388c46ed04 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sat, 19 Aug 2023 22:01:27 +0000 Subject: [PATCH 3/8] add tslib to dev-dependencies --- circuits/cpp/barretenberg/ts/package.json | 1 + circuits/cpp/barretenberg/ts/yarn.lock | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/circuits/cpp/barretenberg/ts/package.json b/circuits/cpp/barretenberg/ts/package.json index d2e0297026f..de19ce72582 100644 --- a/circuits/cpp/barretenberg/ts/package.json +++ b/circuits/cpp/barretenberg/ts/package.json @@ -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", diff --git a/circuits/cpp/barretenberg/ts/yarn.lock b/circuits/cpp/barretenberg/ts/yarn.lock index d3a83f73895..eb2e9ae466b 100644 --- a/circuits/cpp/barretenberg/ts/yarn.lock +++ b/circuits/cpp/barretenberg/ts/yarn.lock @@ -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 @@ -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" From 3dd5d850976106ac835b8844a6d740a8b00992b8 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sat, 19 Aug 2023 22:14:27 +0000 Subject: [PATCH 4/8] `yarn formatting:fix` --- circuits/cpp/barretenberg/ts/src/main.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/circuits/cpp/barretenberg/ts/src/main.ts b/circuits/cpp/barretenberg/ts/src/main.ts index 85744de4a57..e05ad8f4918 100755 --- a/circuits/cpp/barretenberg/ts/src/main.ts +++ b/circuits/cpp/barretenberg/ts/src/main.ts @@ -106,10 +106,10 @@ export async function prove( const witness = getWitness(witnessPath); const proof = await api.acirCreateProof(acirComposer, bytecode, witness, isRecursive); debug(`done.`); - + process.stdout.write(proof); writeFileSync(outputPath, proof); - + debug(`proof written to: ${outputPath}`); } finally { await api.destroy(); @@ -130,7 +130,7 @@ 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); - + process.stdout.write(`${verified}`); return verified; } finally { @@ -143,7 +143,7 @@ export async function contract(outputPath: string, vkPath: string) { try { await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath))); const contract = await api.acirGetSolidityVerifier(acirComposer); - + process.stdout.write(contract); writeFileSync(outputPath, contract); @@ -162,10 +162,10 @@ export async function writeVk(bytecodePath: string, crsPath: string, outputPath: debug('initing verification key...'); const vk = await api.acirGetVerificationKey(acirComposer); - + process.stdout.write(vk); writeFileSync(outputPath, vk); - + debug(`vk written to: ${outputPath}`); } finally { await api.destroy(); @@ -183,10 +183,10 @@ export async function proofAsFields(proofPath: string, numInnerPublicInputs: num numInnerPublicInputs, ); const jsonProofAsFields = JSON.stringify(proofAsFields.map(f => f.toString())); - + process.stdout.write(jsonProofAsFields); writeFileSync(outputPath, jsonProofAsFields); - + debug('done.'); } finally { await api.destroy(); @@ -205,7 +205,7 @@ export async function vkAsFields(vkPath: string, vkeyOutputPath: string) { process.stdout.write(jsonVKAsFields); writeFileSync(vkeyOutputPath, jsonVKAsFields); - + debug('done.'); } finally { await api.destroy(); From 478ecd9291d47234f0683f999afa71a56b8ab1d6 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sun, 20 Aug 2023 21:37:57 +0000 Subject: [PATCH 5/8] add load_proving_key to ACIRComposer and return provingKey for init_proving_key --- .../barretenberg/dsl/acir_proofs/acir_composer.cpp | 12 +++++++++++- .../barretenberg/dsl/acir_proofs/acir_composer.hpp | 8 ++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp index 29463a06db7..353375613ac 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp @@ -4,6 +4,7 @@ #include "barretenberg/dsl/acir_format/acir_format.hpp" #include "barretenberg/dsl/acir_format/recursion_constraint.hpp" #include "barretenberg/dsl/types.hpp" +#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" #include "barretenberg/plonk/proof_system/verification_key/sol_gen.hpp" #include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" @@ -31,7 +32,7 @@ void AcirComposer::create_circuit(acir_format::acir_format& constraint_system) size_hint_ = circuit_subgroup_size_; } -void AcirComposer::init_proving_key( +std::shared_ptr AcirComposer::init_proving_key( std::shared_ptr> const& crs_factory, acir_format::acir_format& constraint_system) { @@ -50,6 +51,7 @@ void AcirComposer::init_proving_key( composer_ = acir_format::Composer(crs_factory); vinfo("computing proving key..."); proving_key_ = composer_.compute_proving_key(builder_); + return proving_key_; } std::vector AcirComposer::create_proof( @@ -108,6 +110,14 @@ std::shared_ptr AcirComposer::init_verifi return verification_key_; } +void AcirComposer::load_proving_key( + std::shared_ptr> const& crs_factory, + proof_system::plonk::proving_key_data&& data) +{ + auto proving_key_ = + std::make_shared(std::move(data), crs_factory->get_prover_crs(data.circuit_size + 1)); + composer_ = acir_format::Composer(proving_key_, verification_key_); +} void AcirComposer::load_verification_key( std::shared_ptr> const& crs_factory, proof_system::plonk::verification_key_data&& data) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp index 25814e78d91..090520041f7 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp @@ -14,8 +14,12 @@ class AcirComposer { void create_circuit(acir_format::acir_format& constraint_system); - void init_proving_key(std::shared_ptr> const& crs_factory, - acir_format::acir_format& constraint_system); + void load_proving_key(std::shared_ptr> const& crs_factory, + proof_system::plonk::proving_key_data&& data); + + std::shared_ptr init_proving_key( + std::shared_ptr> const& crs_factory, + acir_format::acir_format& constraint_system); std::vector create_proof( std::shared_ptr> const& crs_factory, From 6d66180d6aba963fca39fb8c53468f315f0fdbd3 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sun, 20 Aug 2023 21:39:01 +0000 Subject: [PATCH 6/8] add `acir_get_proving_key` to cbinds. Analogous to `acir_get_verification_key` --- .../cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp | 9 +++++++++ .../cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 0827e4797a2..a9eb45e2f89 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -38,6 +38,15 @@ WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_composer->init_proving_key(barretenberg::srs::get_crs_factory(), constraint_system); } +WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t** out) +{ + auto acir_composer = reinterpret_cast(*acir_composer_ptr); + auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); + + auto pk = acir_composer->init_proving_key(barretenberg::srs::get_crs_factory(), constraint_system); + *out = to_heap_buffer(to_buffer(*pk)); +} + WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t const* witness_vec, diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index e17af7a260d..8b122547fef 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -21,6 +21,8 @@ WASM_EXPORT void acir_create_circuit(in_ptr acir_composer_ptr, WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* constraint_system_buf); +WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t** out); + /** * It would have been nice to just hold onto the constraint_system in the acir_composer, but we can't waste the * memory. Being able to reuse the underlying Composer would help as well. But, given the situation, we just have From 48c570e9fa49c8665643ed0cc79044eb9acba4b6 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sun, 20 Aug 2023 21:40:03 +0000 Subject: [PATCH 7/8] add `writeKeys` command and pass the procinKeyPath to prove method --- .../cpp/src/barretenberg/bb/main.cpp | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp index b4635266f6f..624638edbf6 100644 --- a/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -1,4 +1,6 @@ #include "barretenberg/bb/get_crs.hpp" +#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" +#include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" #include "get_bytecode.hpp" #include "get_witness.hpp" #include @@ -67,17 +69,24 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP * - Filesystem: The proof is written to the path specified by outputPath * * @param bytecodePath Path to the file containing the serialized circuit + * @param provingKeyPath Path to the file containing the provingKey * @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& provingKeyPath, const std::string& witnessPath, bool recursive, const std::string& outputPath) { auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose); auto constraint_system = get_constraint_system(bytecodePath); + if (provingKeyPath.empty()) { + auto pk_data = from_buffer(read_file(provingKeyPath)); + acir_composer->load_proving_key(barretenberg::srs::get_crs_factory(), std::move(pk_data)); + } + auto witness = get_witness(witnessPath); auto proof = acir_composer->create_proof(srs::get_crs_factory(), constraint_system, witness, recursive); @@ -151,6 +160,38 @@ void writeVk(const std::string& bytecodePath, const std::string& outputPath) info("vk written to: ", outputPath); } +/** + * @brief Writes a verification and proving key for an ACIR circuit to a file + * + * Why is this needed? + * Generally we want to save the proving key, so that we can create proofs without having to recompute the proving key. + * and so that verification procedures are not slow. + * + * + * + * Communication: + * - We do not write anything to stdout because proving key can be very large + * - Filesystem: The verification key and proving key are written to the path specified by outputPkPath and outputVkPath + * + * @param bytecodePath Path to the file containing the serialized circuit + * @param outputVkPath Path to write the verification key to + * @param outputPkPath Path to write the proving key to + */ +void writeKeys(const std::string& bytecodePath, const std::string& outputVkPath, const std::string& outputPkPath) +{ + auto acir_composer = new acir_proofs::AcirComposer(MAX_CIRCUIT_SIZE, verbose); + auto constraint_system = get_constraint_system(bytecodePath); + auto pk = acir_composer->init_proving_key(srs::get_crs_factory(), constraint_system); + auto vk = acir_composer->init_verification_key(); + auto serialized_pk = to_buffer(*pk); + auto serialized_vk = to_buffer(*vk); + write_file(outputVkPath, serialized_vk); + write_file(outputPkPath, serialized_pk); + + info("vk written to: ", outputVkPath); + info("pk written to: ", outputPkPath); +} + /** * @brief Writes a Solidity verifier contract for an ACIR circuit to a file * @@ -272,7 +313,10 @@ int main(int argc, char* argv[]) std::string bytecode_path = getOption(args, "-b", "./target/main.bytecode"); std::string witness_path = getOption(args, "-w", "./target/witness.tr"); std::string proof_path = getOption(args, "-p", "./proofs/proof"); - std::string vk_path = getOption(args, "-k", "./target/vk"); + std::string vk_path = getOption(args, "-vk", "./target/vk"); + // "" this optional so we don't supply a default + // Unlike other argument, we want to differentiate between the user not supplying a path + std::string pk_path = getOption(args, "-pk", ""); CRS_PATH = getOption(args, "-c", "./crs"); bool recursive = flagPresent(args, "-r") || flagPresent(args, "--recursive"); init(); @@ -281,7 +325,7 @@ int main(int argc, char* argv[]) return proveAndVerify(bytecode_path, witness_path, recursive) ? 0 : 1; } else if (command == "prove") { std::string output_path = getOption(args, "-o", "./proofs/proof"); - prove(bytecode_path, witness_path, recursive, output_path); + prove(bytecode_path, pk_path, witness_path, recursive, output_path); } else if (command == "gates") { gateCount(bytecode_path); } else if (command == "verify") { @@ -292,6 +336,10 @@ int main(int argc, char* argv[]) } else if (command == "write_vk") { std::string output_path = getOption(args, "-o", "./target/vk"); writeVk(bytecode_path, output_path); + } else if (command == "write_keys") { + std::string output_vk_path = getOption(args, "-ovk", "./target/vk"); + std::string output_pk_path = getOption(args, "-opk", "./target/pk"); + writeKeys(bytecode_path, output_vk_path, output_pk_path); } else if (command == "proof_as_fields") { std::string output_path = getOption(args, "-o", proof_path + "_fields.json"); proofAsFields(proof_path, vk_path, output_path); From 26aa8cece4fcc870c7b70c955a7fe5cafd5cf7a9 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Sun, 20 Aug 2023 21:41:17 +0000 Subject: [PATCH 8/8] add analogous writeKeys method to ts binary --- circuits/cpp/barretenberg/ts/src/main.ts | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/circuits/cpp/barretenberg/ts/src/main.ts b/circuits/cpp/barretenberg/ts/src/main.ts index e05ad8f4918..30ab4011fd8 100755 --- a/circuits/cpp/barretenberg/ts/src/main.ts +++ b/circuits/cpp/barretenberg/ts/src/main.ts @@ -172,6 +172,26 @@ export async function writeVk(bytecodePath: string, crsPath: string, outputPath: } } +export async function writeKeys(bytecodePath: string, crsPath: string, outputVkPath: string, outputPkPath: string) { + const { api, acirComposer } = await init(bytecodePath, crsPath); + try { + debug('initing proving key...'); + const bytecode = getBytecode(bytecodePath); + const pk = await api.acirGetProvingKey(acirComposer, bytecode); + + debug('initing verification key...'); + const vk = await api.acirGetVerificationKey(acirComposer); + + writeFileSync(outputVkPath, vk); + writeFileSync(outputPkPath, pk); + + debug(`vk written to: ${outputVkPath}`); + debug(`pk written to: ${outputPkPath}`); + } finally { + await api.destroy(); + } +} + export async function proofAsFields(proofPath: string, numInnerPublicInputs: number, outputPath: string) { const { api, acirComposer } = await initLite(); @@ -289,6 +309,17 @@ program await writeVk(bytecodePath, crsPath, outputPath); }); +program + .command('write_keys') + .description('Output verification and proving key.') + .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/main.bytecode') + .requiredOption('-ovk, --output-vk-path ', 'Specify the path to write the verification key') + .requiredOption('-opk, --output-pk-path ', 'Specify the path to write the proving key') + .action(async ({ bytecodePath, outputVkPath, outputPkPath,crsPath }) => { + handleGlobalOptions(); + await writeKeys(bytecodePath, crsPath, outputVkPath, outputPkPath); + }); + program .command('proof_as_fields') .description('Return the proof as fields elements')