From 3760c64532dd8b55a46a75e0d98896b7131e754f Mon Sep 17 00:00:00 2001 From: Cody Gunton Date: Thu, 11 Jul 2024 14:11:01 -0400 Subject: [PATCH] feat: Verify ClientIVC proofs through Bb binary (#7407) Exposes a function to verify ClientIVC proofs through the Bb binary --- barretenberg/Earthfile | 2 + .../flows/prove_then_verify_client_ivc.sh | 9 +++ barretenberg/cpp/src/barretenberg/bb/main.cpp | 80 ++++++++++++++++--- .../barretenberg/client_ivc/client_ivc.cpp | 33 +++++--- .../barretenberg/client_ivc/client_ivc.hpp | 6 ++ .../zeromorph/zeromorph.hpp | 17 +++- 6 files changed, 121 insertions(+), 26 deletions(-) create mode 100755 barretenberg/acir_tests/flows/prove_then_verify_client_ivc.sh diff --git a/barretenberg/Earthfile b/barretenberg/Earthfile index 1eb57ff2507..56776780cfa 100644 --- a/barretenberg/Earthfile +++ b/barretenberg/Earthfile @@ -42,6 +42,8 @@ barretenberg-acir-tests-bb: RUN FLOW=prove_and_verify_mega_honk_program ./run_acir_tests.sh # Fold and verify an ACIR program stack using ClientIvc RUN FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic + # Fold and verify an ACIR program stack using ClientIvc, then natively verify the ClientIVC proof. + RUN FLOW=prove_then_verify_client_ivc ./run_acir_tests.sh fold_basic # Fold and verify an ACIR program stack using ClientIvc, recursively verify as part of the Tube circuit and produce and verify a Honk proof RUN FLOW=prove_then_verify_tube ./run_acir_tests.sh fold_basic # Construct and separately verify a UltraHonk proof for a single program that recursively verifies a Honk proof diff --git a/barretenberg/acir_tests/flows/prove_then_verify_client_ivc.sh b/barretenberg/acir_tests/flows/prove_then_verify_client_ivc.sh new file mode 100755 index 00000000000..eda013f0494 --- /dev/null +++ b/barretenberg/acir_tests/flows/prove_then_verify_client_ivc.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -eu + +VFLAG=${VERBOSE:+-v} +BFLAG="-b ./target/program.json" +FLAGS="-c $CRS_PATH $VFLAG" + +$BIN client_ivc_prove_output_all $FLAGS $BFLAG +$BIN verify_client_ivc $FLAGS diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 265c402bdcc..efbfd9baa84 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -315,7 +315,7 @@ std::vector decompressedBuffer(uint8_t* bytes, size_t size) void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath, const std::string& witnessPath, - const std::string& outputPath) + const std::string& outputDir) { using Flavor = MegaFlavor; // This is the only option using Builder = Flavor::CircuitBuilder; @@ -361,11 +361,11 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath, // Write the proof and verification keys into the working directory in 'binary' format (in practice it seems this // directory is passed by bb.js) - std::string vkPath = outputPath + "/inst_vk"; // the vk of the last instance - std::string accPath = outputPath + "/pg_acc"; - std::string proofPath = outputPath + "/client_ivc_proof"; - std::string translatorVkPath = outputPath + "/translator_vk"; - std::string eccVkPath = outputPath + "/ecc_vk"; + std::string vkPath = outputDir + "/inst_vk"; // the vk of the last instance + std::string accPath = outputDir + "/pg_acc"; + std::string proofPath = outputDir + "/client_ivc_proof"; + std::string translatorVkPath = outputDir + "/translator_vk"; + std::string eccVkPath = outputDir + "/ecc_vk"; auto proof = ivc.prove(); auto eccvm_vk = std::make_shared(ivc.goblin.get_eccvm_proving_key()); @@ -382,6 +382,48 @@ void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath, write_file(eccVkPath, to_buffer(eccvm_vk)); } +template std::shared_ptr read_to_shared_ptr(const std::filesystem::path& path) +{ + return std::make_shared(from_buffer(read_file(path))); +}; + +/** + * @brief Verifies a client ivc proof and writes the result to stdout + * + * Communication: + * - proc_exit: A boolean value is returned indicating whether the proof is valid. + * an exit code of 0 will be returned for success and 1 for failure. + * + * @param proof_path Path to the file containing the serialized proof + * @param vk_path Path to the file containing the serialized verification key of the final mega honk instance + * @param accumualtor_path Path to the file containing the serialized protogalaxy accumulator + * @return true (resp., false) if the proof is valid (resp., invalid). + */ +bool verify_client_ivc(const std::filesystem::path& proof_path, + const std::filesystem::path& accumulator_path, + const std::filesystem::path& final_vk_path, + const std::filesystem::path& eccvm_vk_path, + const std::filesystem::path& translator_vk_path) +{ + init_bn254_crs(1 << 24); + init_grumpkin_crs(1 << 14); + + const auto proof = from_buffer(read_file(proof_path)); + const auto accumulator = read_to_shared_ptr(accumulator_path); + accumulator->verification_key->pcs_verification_key = std::make_shared>(); + const auto final_vk = read_to_shared_ptr(final_vk_path); + const auto eccvm_vk = read_to_shared_ptr(eccvm_vk_path); + eccvm_vk->pcs_verification_key = + std::make_shared>(eccvm_vk->circuit_size + 1); + const auto translator_vk = read_to_shared_ptr(translator_vk_path); + translator_vk->pcs_verification_key = std::make_shared>(); + + const bool verified = ClientIVC::verify( + proof, accumulator, std::make_shared(final_vk), eccvm_vk, translator_vk); + vinfo("verified: ", verified); + return verified; +} + bool foldAndVerifyProgram(const std::string& bytecodePath, const std::string& witnessPath) { using Flavor = MegaFlavor; // This is the only option @@ -534,8 +576,7 @@ void prove_tube(const std::string& output_path) ClientIVC verifier{ builder, input }; verifier.verify(proof); - info("num gates: ", builder->get_num_gates()); - info("generating proof"); + info("num gates in tube circuit: ", builder->get_num_gates()); using Prover = UltraProver_; using Verifier = UltraVerifier_; Prover tube_prover{ *builder }; @@ -1269,10 +1310,23 @@ int main(int argc, char* argv[]) // TODO(https://github.com/AztecProtocol/barretenberg/issues/1050) we need a verify_client_ivc bb cli command // TODO(#7371): remove this if (command == "client_ivc_prove_output_all_msgpack") { - std::string output_path = get_option(args, "-o", "./proofs/proof"); - client_ivc_prove_output_all_msgpack(bytecode_path, witness_path, output_path); + std::filesystem::path output_dir = get_option(args, "-o", "./target"); + client_ivc_prove_output_all_msgpack(bytecode_path, witness_path, output_dir); return 0; } + if (command == "verify_client_ivc") { + std::filesystem::path output_dir = get_option(args, "-o", "./target"); + std::filesystem::path client_ivc_proof_path = output_dir / "client_ivc_proof"; + std::filesystem::path accumulator_path = output_dir / "pg_acc"; + std::filesystem::path final_vk_path = output_dir / "inst_vk"; + std::filesystem::path eccvm_vk_path = output_dir / "ecc_vk"; + std::filesystem::path translator_vk_path = output_dir / "translator_vk"; + + return verify_client_ivc( + client_ivc_proof_path, accumulator_path, final_vk_path, eccvm_vk_path, translator_vk_path) + ? 0 + : 1; + } if (command == "fold_and_verify_program") { return foldAndVerifyProgram(bytecode_path, witness_path) ? 0 : 1; } @@ -1290,13 +1344,13 @@ int main(int argc, char* argv[]) std::string output_path = get_option(args, "-o", "./proofs"); prove_honk_output_all(bytecode_path, witness_path, output_path); } else if (command == "client_ivc_prove_output_all") { - std::string output_path = get_option(args, "-o", "./proofs"); + std::string output_path = get_option(args, "-o", "./target"); client_ivc_prove_output_all(bytecode_path, witness_path, output_path); } else if (command == "prove_tube") { - std::string output_path = get_option(args, "-o", "./proofs"); + std::string output_path = get_option(args, "-o", "./target"); prove_tube(output_path); } else if (command == "verify_tube") { - std::string output_path = get_option(args, "-o", "./proofs"); + std::string output_path = get_option(args, "-o", "./target"); auto tube_proof_path = output_path + "/proof"; auto tube_vk_path = output_path + "/vk"; return verify_honk(tube_proof_path, tube_vk_path) ? 0 : 1; diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 2cea607effc..3dedf183a31 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -58,24 +58,20 @@ ClientIVC::Proof ClientIVC::prove() { max_block_sizes.print(); // print minimum structured sizes for each block return { fold_output.proof, decider_prove(), goblin.prove() }; -} +}; -/** - * @brief Verify a full proof of the IVC - * - * @param proof - * @return bool - */ -bool ClientIVC::verify(Proof& proof, const std::vector>& verifier_instances) +bool ClientIVC::verify(const Proof& proof, + const std::shared_ptr& accumulator, + const std::shared_ptr& final_verifier_instance, + const std::shared_ptr& eccvm_vk, + const std::shared_ptr& translator_vk) { // Goblin verification (merge, eccvm, translator) - auto eccvm_vkey = std::make_shared(goblin.get_eccvm_proving_key()); - auto translator_vkey = std::make_shared(goblin.get_translator_proving_key()); - GoblinVerifier goblin_verifier{ eccvm_vkey, translator_vkey }; + GoblinVerifier goblin_verifier{ eccvm_vk, translator_vk }; bool goblin_verified = goblin_verifier.verify(proof.goblin_proof); // Decider verification - ClientIVC::FoldingVerifier folding_verifier({ verifier_instances[0], verifier_instances[1] }); + ClientIVC::FoldingVerifier folding_verifier({ accumulator, final_verifier_instance }); auto verifier_accumulator = folding_verifier.verify_folding_proof(proof.folding_proof); ClientIVC::DeciderVerifier decider_verifier(verifier_accumulator); @@ -83,6 +79,19 @@ bool ClientIVC::verify(Proof& proof, const std::vector>& verifier_instances) +{ + auto eccvm_vk = std::make_shared(goblin.get_eccvm_proving_key()); + auto translator_vk = std::make_shared(goblin.get_translator_proving_key()); + return verify(proof, verifier_instances[0], verifier_instances[1], eccvm_vk, translator_vk); +} + /** * @brief Internal method for constructing a decider proof * diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index c002816dd7a..3bfca8046b7 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -121,6 +121,12 @@ class ClientIVC { Proof prove(); + static bool verify(const Proof& proof, + const std::shared_ptr& accumulator, + const std::shared_ptr& final_verifier_instance, + const std::shared_ptr& eccvm_vk, + const std::shared_ptr& translator_vk); + bool verify(Proof& proof, const std::vector>& verifier_instances); bool prove_and_verify(); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 2b507523865..07efea29451 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -691,8 +691,23 @@ template class ZeroMorphVerifier_ { * @brief Utility for native batch multiplication of group elements * @note This is used only for native verification and is not optimized for efficiency */ - static Commitment batch_mul_native(const std::vector& points, const std::vector& scalars) + static Commitment batch_mul_native(const std::vector& _points, const std::vector& _scalars) { + std::vector points; + std::vector scalars; + for (auto [point, scalar] : zip_view(_points, _scalars)) { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/866) Special handling of point at infinity here + // due to incorrect serialization. + if (!scalar.is_zero() && !point.is_point_at_infinity() && !point.y.is_zero()) { + points.emplace_back(point); + scalars.emplace_back(scalar); + } + } + + if (points.empty()) { + return Commitment::infinity(); + } + auto result = points[0] * scalars[0]; for (size_t idx = 1; idx < scalars.size(); ++idx) { result = result + points[idx] * scalars[idx];