From 4094cc37acb525ee03949dc828ec048efc8b4eb9 Mon Sep 17 00:00:00 2001 From: Cody Gunton Date: Tue, 3 Dec 2024 09:18:40 -0500 Subject: [PATCH] feat: Client IVC API (#10217) - Establish API in purely virtual class - This is just a first pass. I will continue to work on this before showing dev rel and others to get buy-in. - Implement some API functions for ClientIVC: prove, verify, prove_and_verify - Support for constructing CIVC proof for input a single circuit - This is interpreted as a "compiletime stack" - Produces ECCVM and Translator proofs from dummy/empty data; future optimization could avoid. - Add `one_circuit` to CIVC to encode whether the MH part of the CIVC proof should be a hiding circuit (which takes a folding proof) or a proof for the single circuit. - Run almost all ACIR tests against ClientIVC - Previously only ran MegaHonk tests, which are not totally meaningful. - Four are skipped because they fail. These failures are expected to be superficial (see https://github.com/AztecProtocol/barretenberg/issues/1164 and the references to it in the PR's new code). - fold_and_verify and mega honk flows go away in bb, but remain until bb.js alignment. - Delete large log file that should not be track (accounts for big negative diff). --- Earthfile | 16 +- acir_tests/flows/fold_and_verify_program.sh | 2 + .../flows/prove_and_verify_client_ivc.sh | 9 + .../flows/prove_and_verify_mega_honk.sh | 5 + .../prove_and_verify_mega_honk_program.sh | 5 + .../flows/prove_then_verify_client_ivc.sh | 8 +- acir_tests/flows/prove_then_verify_tube.sh | 2 +- acir_tests/flows/prove_tube.sh | 2 +- acir_tests/run_acir_tests.sh | 16 + cpp/docs/src/sumcheck-outline.md | 4 +- .../barretenberg/bb/acir_format_getters.hpp | 34 ++ cpp/src/barretenberg/bb/api.hpp | 39 ++ cpp/src/barretenberg/bb/api_client_ivc.hpp | 266 +++++++++++ cpp/src/barretenberg/bb/init_srs.hpp | 37 ++ cpp/src/barretenberg/bb/main.cpp | 443 +++--------------- .../barretenberg/client_ivc/client_ivc.cpp | 36 +- .../barretenberg/client_ivc/client_ivc.hpp | 8 +- .../client_ivc/client_ivc.test.cpp | 4 +- .../client_ivc_auto_verify.test.cpp | 4 +- .../client_ivc_integration.test.cpp | 2 +- .../client_ivc/test_bench_shared.hpp | 2 +- .../dsl/acir_format/acir_format.hpp | 1 + .../stdlib_circuit_builders/mega_flavor.hpp | 2 +- .../sumcheck/sumcheck_round.test.cpp | 2 +- .../ultra_honk/decider_prover.cpp | 2 +- .../barretenberg/ultra_honk/oink_prover.hpp | 3 + .../barretenberg/ultra_honk/ultra_prover.hpp | 1 + 27 files changed, 532 insertions(+), 423 deletions(-) create mode 100755 acir_tests/flows/prove_and_verify_client_ivc.sh create mode 100644 cpp/src/barretenberg/bb/acir_format_getters.hpp create mode 100644 cpp/src/barretenberg/bb/api.hpp create mode 100644 cpp/src/barretenberg/bb/api_client_ivc.hpp create mode 100644 cpp/src/barretenberg/bb/init_srs.hpp diff --git a/Earthfile b/Earthfile index 6030d85a7..841563b48 100644 --- a/Earthfile +++ b/Earthfile @@ -27,9 +27,9 @@ barretenberg-acir-tests-bb: ENV VERBOSE=1 # Fold and verify an ACIR program stack using ClientIvc - RUN FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic + RUN INPUT_TYPE=compiletime_stack FLOW=prove_and_verify_client_ivc ./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 + RUN INPUT_TYPE=compiletime_stack 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 # Run 1_mul through native bb build, all_cmds flow, to test all cli args. @@ -79,7 +79,7 @@ barretenberg-acir-tests-bb-ultra-honk: # Construct and verify a UltraHonk proof for a single program that recursively verifies a Honk proof RUN FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh verify_honk_proof -barretenberg-acir-tests-bb-mega-honk: +barretenberg-acir-tests-bb-client-ivc: FROM ../build-images/+from-registry COPY ./cpp/+preset-clang-assert/bin/bb /usr/src/barretenberg/cpp/build/bin/bb @@ -92,12 +92,10 @@ barretenberg-acir-tests-bb-mega-honk: ENV TEST_SRC /usr/src/acir_artifacts ENV VERBOSE=1 - # Construct and separately verify a MegaHonk proof for all acir programs - RUN FLOW=prove_then_verify_mega_honk ./run_acir_tests.sh - # Construct and verify a MegaHonk proof for a single arbitrary program - RUN FLOW=prove_and_verify_mega_honk ./run_acir_tests.sh 6_array - # Construct and verify a MegaHonk proof for all ACIR programs using the new witness stack workflow - RUN FLOW=prove_and_verify_mega_honk_program ./run_acir_tests.sh + # Construct and verify a ClientIVC proof for a single arbitrary program + RUN FLOW=prove_and_verify_client_ivc ./run_acir_tests.sh 6_array + # Construct and separately verify a ClientIVC proof for all acir programs + RUN FLOW=prove_then_verify_client_ivc CLIENT_IVC_SKIPS=true ./run_acir_tests.sh barretenberg-acir-tests-sol: FROM ../build-images/+from-registry diff --git a/acir_tests/flows/fold_and_verify_program.sh b/acir_tests/flows/fold_and_verify_program.sh index 870873bef..1157e802b 100755 --- a/acir_tests/flows/fold_and_verify_program.sh +++ b/acir_tests/flows/fold_and_verify_program.sh @@ -1,6 +1,8 @@ #!/bin/sh set -eu +# this flow is deprecated. currently it is bb.js only. for bb is is replaced by: +# prove_and_verify --scheme client_ivc --input-type compiletime_stack VFLAG=${VERBOSE:+-v} $BIN fold_and_verify_program $VFLAG -c $CRS_PATH -b ./target/program.json diff --git a/acir_tests/flows/prove_and_verify_client_ivc.sh b/acir_tests/flows/prove_and_verify_client_ivc.sh new file mode 100755 index 000000000..8931cff33 --- /dev/null +++ b/acir_tests/flows/prove_and_verify_client_ivc.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -eu + +VFLAG=${VERBOSE:+-v} +INFLAG=${INPUT_TYPE=compiletime_stack} + +FLAGS="$CRS_PATH -b ./target/program.json $VFLAG --scheme client_ivc -c --input_type $INFLAG" + +$BIN prove_and_verify $FLAGS diff --git a/acir_tests/flows/prove_and_verify_mega_honk.sh b/acir_tests/flows/prove_and_verify_mega_honk.sh index b22be05cc..c78845a9c 100755 --- a/acir_tests/flows/prove_and_verify_mega_honk.sh +++ b/acir_tests/flows/prove_and_verify_mega_honk.sh @@ -3,4 +3,9 @@ set -eu VFLAG=${VERBOSE:+-v} +# this flow is deprecated. currently it is bb.js only. for bb is is replaced by: +# prove_and_verify --scheme client_ivc --input-type compiletime_stack +# NB: In general, it is not meaningful to produce a MegaHonk proof an its own since +# the MegaHonk proof does not attest to the correctness of every possible kind +# of gate that could appear in a Mega execution trace. $BIN prove_and_verify_mega_honk $VFLAG -c $CRS_PATH -b ./target/program.json diff --git a/acir_tests/flows/prove_and_verify_mega_honk_program.sh b/acir_tests/flows/prove_and_verify_mega_honk_program.sh index 21e15fbf7..666607e86 100755 --- a/acir_tests/flows/prove_and_verify_mega_honk_program.sh +++ b/acir_tests/flows/prove_and_verify_mega_honk_program.sh @@ -3,4 +3,9 @@ set -eu VFLAG=${VERBOSE:+-v} +# this flow is deprecated. currently it is bb.js only. for bb is is replaced by: +# prove_and_verify --scheme client_ivc --input-type compiletime_stack +# NB: In general, it is not meaningful to produce a MegaHonk proof an its own since +# the MegaHonk proof does not attest to the correctness of every possible kind +# of gate that could appear in a Mega execution trace. $BIN prove_and_verify_mega_honk_program $VFLAG -c $CRS_PATH -b ./target/program.json diff --git a/acir_tests/flows/prove_then_verify_client_ivc.sh b/acir_tests/flows/prove_then_verify_client_ivc.sh index eda013f04..846e48339 100755 --- a/acir_tests/flows/prove_then_verify_client_ivc.sh +++ b/acir_tests/flows/prove_then_verify_client_ivc.sh @@ -3,7 +3,9 @@ set -eu VFLAG=${VERBOSE:+-v} BFLAG="-b ./target/program.json" -FLAGS="-c $CRS_PATH $VFLAG" +INFLAG=${INPUT_TYPE=compiletime_stack} -$BIN client_ivc_prove_output_all $FLAGS $BFLAG -$BIN verify_client_ivc $FLAGS +FLAGS="--scheme client_ivc -c $CRS_PATH $VFLAG" + +$BIN prove $FLAGS $BFLAG --input_type $INFLAG +$BIN verify $FLAGS diff --git a/acir_tests/flows/prove_then_verify_tube.sh b/acir_tests/flows/prove_then_verify_tube.sh index c73babf27..dfc298ccd 100755 --- a/acir_tests/flows/prove_then_verify_tube.sh +++ b/acir_tests/flows/prove_then_verify_tube.sh @@ -5,7 +5,7 @@ mkdir -p ./proofs VFLAG=${VERBOSE:+-v} -$BIN client_ivc_prove_output_all $VFLAG -c $CRS_PATH -b ./target/program.json +$BIN prove --scheme client_ivc --input_type compiletime_stack $VFLAG -c $CRS_PATH -b ./target/program.json $BIN prove_tube -k vk -p proof -c $CRS_PATH $VFLAG $BIN verify_tube -k vk -p proof -c $CRS_PATH $VFLAG diff --git a/acir_tests/flows/prove_tube.sh b/acir_tests/flows/prove_tube.sh index 111ede2da..d3798c0ad 100644 --- a/acir_tests/flows/prove_tube.sh +++ b/acir_tests/flows/prove_tube.sh @@ -5,5 +5,5 @@ VFLAG=${VERBOSE:+-v} BFLAG="-b ./target/program.json" FLAGS="-c $CRS_PATH $VFLAG" -$BIN client_ivc_prove_output_all $VFLAG -c $CRS_PATH -b ./target/program.json +$BIN prove --scheme client_ivc --input_type compiletime_stack $VFLAG -c $CRS_PATH -b ./target/program.json $BIN prove_tube -k vk -p proof $FLAGS diff --git a/acir_tests/run_acir_tests.sh b/acir_tests/run_acir_tests.sh index b31b8708e..a506eedf8 100755 --- a/acir_tests/run_acir_tests.sh +++ b/acir_tests/run_acir_tests.sh @@ -14,6 +14,7 @@ trap handle_sigchild SIGCHLD BIN=${BIN:-../cpp/build/bin/bb} FLOW=${FLOW:-prove_and_verify} HONK=${HONK:-false} +CLIENT_IVC_SKIPS=${CLIENT_IVC_SKIPS:-false} CRS_PATH=~/.bb-crs BRANCH=master VERBOSE=${VERBOSE:-} @@ -57,6 +58,21 @@ if [ "$HONK" = true ]; then SKIP_ARRAY+=(single_verify_proof double_verify_proof double_verify_nested_proof) fi +if [ "$CLIENT_IVC_SKIPS" = true ]; then + # At least for now, skip folding tests that fail when run against ClientIVC. + # This is not a regression--folding was not being properly tested. + # TODO(https://github.com/AztecProtocol/barretenberg/issues/1164): Resolve this + # The reason for failure is that compile-time folding, as initially conceived, is + # only supported by ClientIVC through hacks. ClientIVC in Aztec is ultimately to be + # used through runtime folding, since the kernels that are needed are detected and + # constructed at runtime in Aztec's typescript proving interface. ClientIVC appends + # folding verifiers and does databus and Goblin merge work depending on its inputs, + # detecting which circuits are Aztec kernels. These tests may simple fail for trivial + # reasons, e.g. because the number of circuits in the stack is odd. + SKIP_ARRAY+=(fold_basic_nested_call fold_fibonacci fold_numeric_generic_poseidon ram_blowup_regression) +fi + + function test() { cd $1 diff --git a/cpp/docs/src/sumcheck-outline.md b/cpp/docs/src/sumcheck-outline.md index 651ce0189..272c33b46 100644 --- a/cpp/docs/src/sumcheck-outline.md +++ b/cpp/docs/src/sumcheck-outline.md @@ -195,9 +195,9 @@ Observe that \f$ G \f$ has several important properties - The coefficients of \f$ G \f$ are independent and uniformly distributed. - Evaluations of \f$ G \f$ at \f$ \vec \ell \in \{0,1\}^d\f$ and related Sumcheck Round Univariates are efficiently computable. -The first two properties imply that the evaluations of Sumcheck Round Univariates for \f$G\f$ are independent and uniformly distributed. We call them Libra Round Univarites. +The first two properties imply that the evaluations of Sumcheck Round Univariates for \f$G\f$ are independent and uniformly distributed. We call them Libra Round Univariates. -Consider Round Univariates for \f$ \tilde{F} + \texttt{libra_challenge}\cdot G\f$ which are the sums of the Sumcheck Round Univariates for \f$ \tilde{F} \f$ and Libra Round Univarites multiplied by the challenge. +Consider Round Univariates for \f$ \tilde{F} + \texttt{libra_challenge}\cdot G\f$ which are the sums of the Sumcheck Round Univariates for \f$ \tilde{F} \f$ and Libra Round Univariates multiplied by the challenge. The fact that the degrees of Libra Round Univariates are big enough (i.e. \f$ \tilde{D}\geq D \f$) and that their evaluations are random imply that the evaluations \f$ \tilde{S}^i(0),\ldots,\tilde{S}^i(\tilde D)\f$ defined in [Compute Round Univariates](#ComputeRoundUnivariates) are now masked by the evaluations of Libra Round Univariates. These evaluations are described explicitly [below](#LibraRoundUnivariates). ### Example {#LibraPolynomialExample} diff --git a/cpp/src/barretenberg/bb/acir_format_getters.hpp b/cpp/src/barretenberg/bb/acir_format_getters.hpp new file mode 100644 index 000000000..9e1023c37 --- /dev/null +++ b/cpp/src/barretenberg/bb/acir_format_getters.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "barretenberg/bb/config.hpp" +#include "barretenberg/bb/file_io.hpp" +#include "barretenberg/bb/get_bytecode.hpp" +#include "barretenberg/dsl/acir_format/acir_format.hpp" +#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp" + +namespace bb { + +acir_format::WitnessVector get_witness(std::string const& witness_path) +{ + auto witness_data = get_bytecode(witness_path); + return acir_format::witness_buf_to_witness_data(witness_data); +} + +acir_format::AcirFormat get_constraint_system(std::string const& bytecode_path, bool honk_recursion) +{ + auto bytecode = get_bytecode(bytecode_path); + return acir_format::circuit_buf_to_acir_format(bytecode, honk_recursion); +} + +acir_format::WitnessVectorStack get_witness_stack(std::string const& witness_path) +{ + auto witness_data = get_bytecode(witness_path); + return acir_format::witness_buf_to_witness_stack(witness_data); +} + +std::vector get_constraint_systems(std::string const& bytecode_path, bool honk_recursion) +{ + auto bytecode = get_bytecode(bytecode_path); + return acir_format::program_buf_to_acir_format(bytecode, honk_recursion); +} + +} // namespace bb \ No newline at end of file diff --git a/cpp/src/barretenberg/bb/api.hpp b/cpp/src/barretenberg/bb/api.hpp new file mode 100644 index 000000000..f33568f18 --- /dev/null +++ b/cpp/src/barretenberg/bb/api.hpp @@ -0,0 +1,39 @@ +#pragma once +#include + +namespace bb { + +class API { + public: + struct Flags { + std::optional output_type; // bytes, fields, bytes_and_fields, fields_msgpack + std::optional input_type; // compiletime_stack, runtime_stack + }; + + virtual void prove(const Flags& flags, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path, + const std::filesystem::path& output_dir) = 0; + + virtual bool verify(const Flags& flags, + const std::filesystem::path& proof_path, + const std::filesystem::path& vk_path) = 0; + + virtual bool prove_and_verify(const Flags& flags, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path) = 0; + + virtual void gates(const Flags& flags, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path) = 0; + + virtual void contract(const Flags& flags, + const std::filesystem::path& output_path, + const std::filesystem::path& vk_path) = 0; + + virtual void to_fields(const Flags& flags, + const std::filesystem::path& proof_path, + const std::filesystem::path& vk_path, + const std::filesystem::path& output_path) = 0; +}; +} // namespace bb diff --git a/cpp/src/barretenberg/bb/api_client_ivc.hpp b/cpp/src/barretenberg/bb/api_client_ivc.hpp new file mode 100644 index 000000000..37b251bd8 --- /dev/null +++ b/cpp/src/barretenberg/bb/api_client_ivc.hpp @@ -0,0 +1,266 @@ +#pragma once + +#include "barretenberg/bb/acir_format_getters.hpp" +#include "barretenberg/bb/api.hpp" +#include "barretenberg/bb/init_srs.hpp" +#include "barretenberg/common/throw_or_abort.hpp" +#include "libdeflate.h" + +namespace bb { + +template std::shared_ptr read_to_shared_ptr(const std::filesystem::path& path) +{ + return std::make_shared(from_buffer(read_file(path))); +}; + +// TODO(#7371): this could probably be more idiomatic +template T unpack_from_file(const std::filesystem::path& filename) +{ + std::ifstream fin; + fin.open(filename, std::ios::ate | std::ios::binary); + if (!fin.is_open()) { + throw std::invalid_argument("file not found"); + } + if (fin.tellg() == -1) { + throw std::invalid_argument("something went wrong"); + } + + uint64_t fsize = static_cast(fin.tellg()); + fin.seekg(0, std::ios_base::beg); + + T result; + char* encoded_data = new char[fsize]; + fin.read(encoded_data, static_cast(fsize)); + msgpack::unpack(encoded_data, fsize).get().convert(result); + return result; +} + +// TODO(#7371) find a home for this +acir_format::WitnessVector witness_map_to_witness_vector(std::map const& witness_map) +{ + acir_format::WitnessVector wv; + size_t index = 0; + for (auto& e : witness_map) { + uint64_t value = std::stoull(e.first); + // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned. + // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices + // which do not exist within the `WitnessMap` with the dummy value of zero. + while (index < value) { + wv.push_back(fr(0)); + index++; + } + wv.push_back(fr(uint256_t(e.second))); + index++; + } + return wv; +} + +std::vector decompress(uint8_t* bytes, size_t size) +{ + std::vector content; + // initial size guess + content.resize(1024ULL * 128ULL); + for (;;) { + auto decompressor = std::unique_ptr{ + libdeflate_alloc_decompressor(), libdeflate_free_decompressor + }; + size_t actual_size = 0; + libdeflate_result decompress_result = libdeflate_gzip_decompress( + decompressor.get(), bytes, size, std::data(content), std::size(content), &actual_size); + if (decompress_result == LIBDEFLATE_INSUFFICIENT_SPACE) { + // need a bigger buffer + content.resize(content.size() * 2); + continue; + } + if (decompress_result == LIBDEFLATE_BAD_DATA) { + throw std::invalid_argument("bad gzip data in bb main"); + } + content.resize(actual_size); + break; + } + return content; +} + +class ClientIVCAPI : public API { + static std::vector _build_folding_stack(const std::string& input_type, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path) + { + using namespace acir_format; + + std::vector folding_stack; + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1162): Efficiently unify ACIR stack parsing + if (input_type == "compiletime_stack") { + auto program_stack = + acir_format::get_acir_program_stack(bytecode_path, witness_path, /*honk_recursion=*/false); + // Accumulate the entire program stack into the IVC + while (!program_stack.empty()) { + auto stack_item = program_stack.back(); + folding_stack.push_back(AcirProgram{ stack_item.constraints, stack_item.witness }); + program_stack.pop_back(); + } + } + + if (input_type == "runtime_stack") { + std::vector gzipped_bincodes; + std::vector witness_data; + gzipped_bincodes = unpack_from_file>(bytecode_path); + witness_data = unpack_from_file>(witness_path); + for (auto [bincode, wit] : zip_view(gzipped_bincodes, witness_data)) { + // TODO(#7371) there is a lot of copying going on in bincode, we should make sure this writes as a + // buffer in the future + std::vector constraint_buf = + decompress(reinterpret_cast(bincode.data()), bincode.size()); // NOLINT + std::vector witness_buf = + decompress(reinterpret_cast(wit.data()), wit.size()); // NOLINT + + AcirFormat constraints = circuit_buf_to_acir_format(constraint_buf, /*honk_recursion=*/false); + WitnessVector witness = witness_buf_to_witness_data(witness_buf); + + folding_stack.push_back(AcirProgram{ constraints, witness }); + } + } + + return folding_stack; + }; + + static ClientIVC _accumulate(std::vector& folding_stack) + { + using Builder = MegaCircuitBuilder; + using Program = acir_format::AcirProgram; + + using namespace acir_format; + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically + init_bn254_crs(1 << 20); + init_grumpkin_crs(1 << 15); + + // TODO(#7371) dedupe this with the rest of the similar code + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode + ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; + + // Accumulate the entire program stack into the IVC + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once + // databus has been integrated into noir kernel programs + bool is_kernel = false; + for (Program& program : folding_stack) { + // Construct a bberg circuit from the acir representation then accumulate it into the IVC + Builder circuit = acir_format::create_circuit( + program.constraints, true, 0, program.witness, false, ivc.goblin.op_queue); + + // Set the internal is_kernel flag based on the local mechanism only if it has not already been set to true + if (!circuit.databus_propagation_data.is_kernel) { + circuit.databus_propagation_data.is_kernel = is_kernel; + } + is_kernel = !is_kernel; + + // Do one step of ivc accumulator or, if there is only one circuit in the stack, prove that circuit. In this + // case, no work is added to the Goblin opqueue, but VM proofs for trivials inputs are produced. + ivc.accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1); + } + + return ivc; + }; + + public: + void prove(const API::Flags& flags, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path, + const std::filesystem::path& output_dir) override + { + if (!flags.output_type || *flags.output_type != "fields_msgpack") { + throw_or_abort("No output_type or output_type not supported"); + } + + if (!flags.input_type || !(*flags.input_type == "compiletime_stack" || *flags.input_type == "runtime_stack")) { + throw_or_abort("No input_type or input_type not supported"); + } + + std::vector folding_stack = + _build_folding_stack(*flags.input_type, bytecode_path, witness_path); + ClientIVC ivc = _accumulate(folding_stack); + ClientIVC::Proof proof = ivc.prove(); + + // Write the proof and verification keys into the working directory in 'binary' format (in practice it seems + // this directory is passed by bb.js) + vinfo("writing ClientIVC proof and vk..."); + write_file(output_dir / "client_ivc_proof", to_buffer(proof)); + + auto eccvm_vk = std::make_shared(ivc.goblin.get_eccvm_proving_key()); + auto translator_vk = + std::make_shared(ivc.goblin.get_translator_proving_key()); + write_file(output_dir / "client_ivc_vk", + to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_vk })); + }; + + /** + * @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 serialized verification key of the final (MegaHonk) circuit in the stack + * @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([[maybe_unused]] const API::Flags& flags, + const std::filesystem::path& proof_path, + const std::filesystem::path& vk_path) override + { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163): Set these dynamically + init_bn254_crs(1); + init_grumpkin_crs(1 << 15); + + const auto proof = from_buffer(read_file(proof_path)); + const auto vk = from_buffer(read_file(vk_path)); + + vk.mega->pcs_verification_key = std::make_shared>(); + vk.eccvm->pcs_verification_key = + std::make_shared>(vk.eccvm->circuit_size + 1); + vk.translator->pcs_verification_key = std::make_shared>(); + + const bool verified = ClientIVC::verify(proof, vk); + vinfo("verified: ", verified); + return verified; + }; + + bool prove_and_verify(const API::Flags& flags, + const std::filesystem::path& bytecode_path, + const std::filesystem::path& witness_path) override + { + if (!flags.input_type || !(*flags.input_type == "compiletime_stack" || *flags.input_type == "runtime_stack")) { + throw_or_abort("No input_type or input_type not supported"); + } + std::vector folding_stack = + _build_folding_stack(*flags.input_type, bytecode_path, witness_path); + ClientIVC ivc = _accumulate(folding_stack); + const bool verified = ivc.prove_and_verify(); + return verified; + }; + + void gates([[maybe_unused]] const API::Flags& flags, + [[maybe_unused]] const std::filesystem::path& bytecode_path, + [[maybe_unused]] const std::filesystem::path& witness_path) override + { + throw_or_abort("API function not implemented"); + }; + + void contract([[maybe_unused]] const API::Flags& flags, + [[maybe_unused]] const std::filesystem::path& output_path, + [[maybe_unused]] const std::filesystem::path& vk_path) override + { + throw_or_abort("API function not implemented"); + }; + + void to_fields([[maybe_unused]] const API::Flags& flags, + [[maybe_unused]] const std::filesystem::path& proof_path, + [[maybe_unused]] const std::filesystem::path& vk_path, + [[maybe_unused]] const std::filesystem::path& output_path) override + { + throw_or_abort("API function not implemented"); + }; +}; +} // namespace bb diff --git a/cpp/src/barretenberg/bb/init_srs.hpp b/cpp/src/barretenberg/bb/init_srs.hpp new file mode 100644 index 000000000..8d8780f25 --- /dev/null +++ b/cpp/src/barretenberg/bb/init_srs.hpp @@ -0,0 +1,37 @@ +#include "get_bn254_crs.hpp" +#include "get_grumpkin_crs.hpp" + +namespace bb { +std::string getHomeDir() +{ + char* home = std::getenv("HOME"); + return home != nullptr ? std::string(home) : "./"; +} + +std::string CRS_PATH = getHomeDir() + "/.bb-crs"; + +/** + * @brief Initialize the global crs_factory for bn254 based on a known dyadic circuit size + * + * @param dyadic_circuit_size power-of-2 circuit size + */ +void init_bn254_crs(size_t dyadic_circuit_size) +{ + // Must +1 for Plonk only! + auto bn254_g1_data = get_bn254_g1_data(CRS_PATH, dyadic_circuit_size + 1); + auto bn254_g2_data = get_bn254_g2_data(CRS_PATH); + srs::init_crs_factory(bn254_g1_data, bn254_g2_data); +} + +/** + * @brief Initialize the global crs_factory for grumpkin based on a known dyadic circuit size + * @details Grumpkin crs is required only for the ECCVM + * + * @param dyadic_circuit_size power-of-2 circuit size + */ +void init_grumpkin_crs(size_t eccvm_dyadic_circuit_size) +{ + auto grumpkin_g1_data = get_grumpkin_g1_data(CRS_PATH, eccvm_dyadic_circuit_size + 1); + srs::init_grumpkin_crs_factory(grumpkin_g1_data); +} +} // namespace bb \ No newline at end of file diff --git a/cpp/src/barretenberg/bb/main.cpp b/cpp/src/barretenberg/bb/main.cpp index a994b0b59..00b0b8a68 100644 --- a/cpp/src/barretenberg/bb/main.cpp +++ b/cpp/src/barretenberg/bb/main.cpp @@ -1,22 +1,28 @@ +#include "barretenberg/bb/api.hpp" +#include "barretenberg/bb/api_client_ivc.hpp" #include "barretenberg/bb/file_io.hpp" #include "barretenberg/client_ivc/client_ivc.hpp" +#include "barretenberg/common/benchmark.hpp" #include "barretenberg/common/map.hpp" #include "barretenberg/common/serialize.hpp" +#include "barretenberg/common/timer.hpp" #include "barretenberg/constants.hpp" #include "barretenberg/dsl/acir_format/acir_format.hpp" +#include "barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp" #include "barretenberg/dsl/acir_format/proof_surgeon.hpp" +#include "barretenberg/dsl/acir_proofs/acir_composer.hpp" #include "barretenberg/dsl/acir_proofs/honk_contract.hpp" #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/numeric/bitop/get_msb.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" #include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp" #include "barretenberg/serialize/cbind.hpp" +#include "barretenberg/srs/global_crs.hpp" #include "barretenberg/stdlib/client_ivc_verifier/client_ivc_recursive_verifier.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_keccak_flavor.hpp" #include "barretenberg/vm/avm/trace/public_inputs.hpp" -#include #ifndef DISABLE_AZTEC_VM #include "barretenberg/vm/avm/generated/flavor.hpp" #include "barretenberg/vm/avm/trace/common.hpp" @@ -24,64 +30,12 @@ #include "barretenberg/vm/aztec_constants.hpp" #include "barretenberg/vm/stats.hpp" #endif -#include "config.hpp" -#include "get_bn254_crs.hpp" -#include "get_bytecode.hpp" -#include "get_grumpkin_crs.hpp" -#include "libdeflate.h" -#include "log.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include using namespace bb; -std::string getHomeDir() -{ - char* home = std::getenv("HOME"); - return home != nullptr ? std::string(home) : "./"; -} - -std::string CRS_PATH = getHomeDir() + "/.bb-crs"; - const std::filesystem::path current_path = std::filesystem::current_path(); const auto current_dir = current_path.filename().string(); -/** - * @brief Initialize the global crs_factory for bn254 based on a known dyadic circuit size - * - * @param dyadic_circuit_size power-of-2 circuit size - */ -void init_bn254_crs(size_t dyadic_circuit_size) -{ - // Must +1 for Plonk only! - auto bn254_g1_data = get_bn254_g1_data(CRS_PATH, dyadic_circuit_size + 1); - auto bn254_g2_data = get_bn254_g2_data(CRS_PATH); - srs::init_crs_factory(bn254_g1_data, bn254_g2_data); -} - -/** - * @brief Initialize the global crs_factory for grumpkin based on a known dyadic circuit size - * @details Grumpkin crs is required only for the ECCVM - * - * @param dyadic_circuit_size power-of-2 circuit size - */ -void init_grumpkin_crs(size_t eccvm_dyadic_circuit_size) -{ - auto grumpkin_g1_data = get_grumpkin_g1_data(CRS_PATH, eccvm_dyadic_circuit_size + 1); - srs::init_grumpkin_crs_factory(grumpkin_g1_data); -} - // Initializes without loading G1 // TODO(https://github.com/AztecProtocol/barretenberg/issues/811) adapt for grumpkin acir_proofs::AcirComposer verifier_init() @@ -92,30 +46,6 @@ acir_proofs::AcirComposer verifier_init() return acir_composer; } -acir_format::WitnessVector get_witness(std::string const& witness_path) -{ - auto witness_data = get_bytecode(witness_path); - return acir_format::witness_buf_to_witness_data(witness_data); -} - -acir_format::AcirFormat get_constraint_system(std::string const& bytecode_path, bool honk_recursion) -{ - auto bytecode = get_bytecode(bytecode_path); - return acir_format::circuit_buf_to_acir_format(bytecode, honk_recursion); -} - -acir_format::WitnessVectorStack get_witness_stack(std::string const& witness_path) -{ - auto witness_data = get_bytecode(witness_path); - return acir_format::witness_buf_to_witness_stack(witness_data); -} - -std::vector get_constraint_systems(std::string const& bytecode_path, bool honk_recursion) -{ - auto bytecode = get_bytecode(bytecode_path); - return acir_format::program_buf_to_acir_format(bytecode, honk_recursion); -} - std::string to_json(std::vector& data) { return format("[", join(map(data, [](auto fr) { return format("\"", fr, "\""); })), "]"); @@ -255,272 +185,6 @@ bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const bool recur return true; } -// TODO(#7371): this could probably be more idiomatic -template T unpack_from_file(const std::string& filename) -{ - std::ifstream fin; - fin.open(filename, std::ios::ate | std::ios::binary); - if (!fin.is_open()) { - throw std::invalid_argument("file not found"); - } - if (fin.tellg() == -1) { - throw std::invalid_argument("something went wrong"); - } - - uint64_t fsize = static_cast(fin.tellg()); - fin.seekg(0, std::ios_base::beg); - - T result; - char* encoded_data = new char[fsize]; - fin.read(encoded_data, static_cast(fsize)); - msgpack::unpack(encoded_data, fsize).get().convert(result); - return result; -} - -// TODO(#7371) find a home for this -acir_format::WitnessVector witness_map_to_witness_vector(std::map const& witness_map) -{ - acir_format::WitnessVector wv; - size_t index = 0; - for (auto& e : witness_map) { - uint64_t value = std::stoull(e.first); - // ACIR uses a sparse format for WitnessMap where unused witness indices may be left unassigned. - // To ensure that witnesses sit at the correct indices in the `WitnessVector`, we fill any indices - // which do not exist within the `WitnessMap` with the dummy value of zero. - while (index < value) { - wv.push_back(fr(0)); - index++; - } - wv.push_back(fr(uint256_t(e.second))); - index++; - } - return wv; -} - -std::vector decompressedBuffer(uint8_t* bytes, size_t size) -{ - std::vector content; - // initial size guess - content.resize(1024ULL * 128ULL); - for (;;) { - auto decompressor = std::unique_ptr{ - libdeflate_alloc_decompressor(), libdeflate_free_decompressor - }; - size_t actual_size = 0; - libdeflate_result decompress_result = libdeflate_gzip_decompress( - decompressor.get(), bytes, size, std::data(content), std::size(content), &actual_size); - if (decompress_result == LIBDEFLATE_INSUFFICIENT_SPACE) { - // need a bigger buffer - content.resize(content.size() * 2); - continue; - } - if (decompress_result == LIBDEFLATE_BAD_DATA) { - throw std::invalid_argument("bad gzip data in bb main"); - } - content.resize(actual_size); - break; - } - return content; -} - -void client_ivc_prove_output_all_msgpack(const std::string& bytecodePath, - const std::string& witnessPath, - const std::string& outputDir) -{ - using Flavor = MegaFlavor; // This is the only option - using Builder = Flavor::CircuitBuilder; - using Program = acir_format::AcirProgram; - using ECCVMVK = ECCVMFlavor::VerificationKey; - using TranslatorVK = TranslatorFlavor::VerificationKey; - - using namespace acir_format; - - init_bn254_crs(1 << 24); - init_grumpkin_crs(1 << 15); - - auto gzipped_bincodes = unpack_from_file>(bytecodePath); - auto witness_data = unpack_from_file>(witnessPath); - std::vector folding_stack; - for (auto [bincode, wit] : zip_view(gzipped_bincodes, witness_data)) { - // TODO(#7371) there is a lot of copying going on in bincode, we should make sure this writes as a buffer in - // the future - std::vector constraint_buf = - decompressedBuffer(reinterpret_cast(bincode.data()), bincode.size()); // NOLINT - std::vector witness_buf = - decompressedBuffer(reinterpret_cast(wit.data()), wit.size()); // NOLINT - - AcirFormat constraints = circuit_buf_to_acir_format(constraint_buf, /*honk_recursion=*/false); - WitnessVector witness = witness_buf_to_witness_data(witness_buf); - - folding_stack.push_back(Program{ constraints, witness }); - } - // TODO(#7371) dedupe this with the rest of the similar code - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode - ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; - - // Accumulate the entire program stack into the IVC - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once databus - // has been integrated into noir kernel programs - bool is_kernel = false; - for (Program& program : folding_stack) { - // Construct a bberg circuit from the acir representation then accumulate it into the IVC - auto circuit = - create_circuit(program.constraints, true, 0, program.witness, false, ivc.goblin.op_queue); - - // Set the internal is_kernel flag based on the local mechanism only if it has not already been set to true - if (!circuit.databus_propagation_data.is_kernel) { - circuit.databus_propagation_data.is_kernel = is_kernel; - } - is_kernel = !is_kernel; - ivc.accumulate(circuit); - } - - // 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 = outputDir + "/client_ivc_vk"; // the vk of the last circuit in the stack - std::string proofPath = outputDir + "/client_ivc_proof"; - - auto proof = ivc.prove(); - auto eccvm_vk = std::make_shared(ivc.goblin.get_eccvm_proving_key()); - auto translator_vk = std::make_shared(ivc.goblin.get_translator_proving_key()); - vinfo("ensure valid proof: ", ivc.verify(proof)); - - vinfo("write proof and vk data to files.."); - write_file(proofPath, to_buffer(proof)); - write_file(vkPath, to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_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 serialized verification key of the final (MegaHonk) circuit in the stack - * @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& vk_path) -{ - init_bn254_crs(1); - init_grumpkin_crs(1 << 15); - - const auto proof = from_buffer(read_file(proof_path)); - const auto vk = from_buffer(read_file(vk_path)); - - vk.mega->pcs_verification_key = std::make_shared>(); - vk.eccvm->pcs_verification_key = - std::make_shared>(vk.eccvm->circuit_size + 1); - vk.translator->pcs_verification_key = std::make_shared>(); - - const bool verified = ClientIVC::verify(proof, vk); - vinfo("verified: ", verified); - return verified; -} - -bool foldAndVerifyProgram(const std::string& bytecodePath, const std::string& witnessPath) -{ - using Flavor = MegaFlavor; // This is the only option - using Builder = Flavor::CircuitBuilder; - - init_bn254_crs(1 << 22); - init_grumpkin_crs(1 << 16); - - ClientIVC ivc{ { SMALL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; - - auto program_stack = acir_format::get_acir_program_stack( - bytecodePath, witnessPath, false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): this - // assumes that folding is never done with ultrahonk. - - // Accumulate the entire program stack into the IVC - bool is_kernel = false; - while (!program_stack.empty()) { - auto stack_item = program_stack.back(); - - // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit(stack_item.constraints, - /*recursive=*/true, - 0, - stack_item.witness, - /*honk_recursion=*/false, - ivc.goblin.op_queue); - - // Set the internal is_kernel flag to trigger automatic appending of kernel logic if true - builder.databus_propagation_data.is_kernel = is_kernel; - - ivc.accumulate(builder); - - program_stack.pop_back(); - is_kernel = !is_kernel; // toggle the kernel indicator flag on/off - } - return ivc.prove_and_verify(); -} - -/** - * @brief Recieves an ACIR Program stack that gets accumulated with the ClientIVC logic and produces a client IVC proof. - * - * @param bytecodePath Path to the serialised circuit - * @param witnessPath Path to witness data - * @param outputPath Path to the folder where the proof and verification data are goingt obe wr itten (in practice this - * going to be specified when bb main is called, i.e. as the working directory in typescript). - */ -void client_ivc_prove_output_all(const std::string& bytecodePath, - const std::string& witnessPath, - const std::string& outputPath) -{ - using Flavor = MegaFlavor; // This is the only option - using Builder = Flavor::CircuitBuilder; - using ECCVMVK = ECCVMFlavor::VerificationKey; - using TranslatorVK = TranslatorFlavor::VerificationKey; - - init_bn254_crs(1 << 22); - init_grumpkin_crs(1 << 16); - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode - ClientIVC ivc{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; - - auto program_stack = acir_format::get_acir_program_stack( - bytecodePath, witnessPath, false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): this - // assumes that folding is never done with ultrahonk. - - // Accumulate the entire program stack into the IVC - bool is_kernel = false; - while (!program_stack.empty()) { - auto stack_item = program_stack.back(); - - // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit( - stack_item.constraints, true, 0, stack_item.witness, false, ivc.goblin.op_queue); - circuit.databus_propagation_data.is_kernel = is_kernel; - is_kernel = !is_kernel; // toggle on/off so every second circuit is intepreted as a kernel - - ivc.accumulate(circuit); - - program_stack.pop_back(); - } - - // 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 + "/client_ivc_vk"; // the vk of the last circuit in the stack - std::string proofPath = outputPath + "/client_ivc_proof"; - - auto proof = ivc.prove(); - auto eccvm_vk = std::make_shared(ivc.goblin.get_eccvm_proving_key()); - auto translator_vk = std::make_shared(ivc.goblin.get_translator_proving_key()); - vinfo("ensure valid proof: ", ivc.verify(proof)); - - vinfo("write proof and vk data to files.."); - write_file(proofPath, to_buffer(proof)); - write_file(vkPath, to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_vk })); -} - /** * @brief Creates a Honk Proof for the Tube circuit responsible for recursively verifying a ClientIVC proof. * @@ -536,7 +200,7 @@ void prove_tube(const std::string& output_path) using Builder = UltraCircuitBuilder; using GrumpkinVk = bb::VerifierCommitmentKey; - std::string vkPath = output_path + "/client_ivc_vk"; // the vk of the last circuit in the stack + std::string vkPath = output_path + "/client_ivc_vk"; std::string proofPath = output_path + "/client_ivc_proof"; // Note: this could be decreased once we optimise the size of the ClientIVC recursiveve rifier @@ -1083,7 +747,6 @@ void prove_honk(const std::string& bytecodePath, const std::string& outputPath, const bool recursive) { - // using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; // Construct Honk proof @@ -1408,55 +1071,66 @@ int main(int argc, char* argv[]) return 1; } - std::string command = args[0]; + const API::Flags flags = [&args]() { + return API::Flags{ .output_type = get_option(args, "--output_type", "fields_msgpack"), + .input_type = get_option(args, "--input_type", "compiletime_stack") }; + }(); + + const std::string command = args[0]; vinfo("bb command is: ", command); - std::string bytecode_path = get_option(args, "-b", "./target/program.json"); - std::string witness_path = get_option(args, "-w", "./target/witness.gz"); - std::string proof_path = get_option(args, "-p", "./proofs/proof"); - std::string vk_path = get_option(args, "-k", "./target/vk"); - std::string pk_path = get_option(args, "-r", "./target/pk"); - bool honk_recursion = flag_present(args, "-h"); - bool recursive = flag_present(args, "--recursive"); // Not every flavor handles it. + const std::string proof_system = get_option(args, "--scheme", ""); + const std::string bytecode_path = get_option(args, "-b", "./target/program.json"); + const std::string witness_path = get_option(args, "-w", "./target/witness.gz"); + const std::string proof_path = get_option(args, "-p", "./proofs/proof"); + const std::string vk_path = get_option(args, "-k", "./target/vk"); + const std::string pk_path = get_option(args, "-r", "./target/pk"); + + const bool honk_recursion = flag_present(args, "-h"); + const bool recursive = flag_present(args, "--recursive"); CRS_PATH = get_option(args, "-c", CRS_PATH); + const auto execute_command = [&](const std::string& command, const API::Flags& flags, API& api) { + ASSERT(flags.input_type.has_value()); + ASSERT(flags.output_type.has_value()); + if (command == "prove") { + const std::filesystem::path output_dir = get_option(args, "-o", "./target"); + // TODO(#7371): remove this (msgpack version...) + api.prove(flags, bytecode_path, witness_path, output_dir); + return 0; + } + + if (command == "verify") { + const std::filesystem::path output_dir = get_option(args, "-o", "./target"); + const std::filesystem::path proof_path = output_dir / "client_ivc_proof"; + const std::filesystem::path vk_path = output_dir / "client_ivc_vk"; + + return api.verify(flags, proof_path, vk_path) ? 0 : 1; + } + + if (command == "prove_and_verify") { + return api.prove_and_verify(flags, bytecode_path, witness_path) ? 0 : 1; + } + + throw_or_abort("Invalid command passed to execute_command in bb"); + return 1; + }; + // Skip CRS initialization for any command which doesn't require the CRS. if (command == "--version") { writeStringToStdout(BB_VERSION); return 0; } - if (command == "prove_and_verify") { + + if (proof_system == "client_ivc") { + ClientIVCAPI api; + execute_command(command, flags, api); + } else if (command == "prove_and_verify") { return proveAndVerify(bytecode_path, recursive, witness_path) ? 0 : 1; - } - if (command == "prove_and_verify_ultra_honk") { + } else if (command == "prove_and_verify_ultra_honk") { return proveAndVerifyHonk(bytecode_path, recursive, witness_path) ? 0 : 1; - } - if (command == "prove_and_verify_mega_honk") { - return proveAndVerifyHonk(bytecode_path, recursive, witness_path) ? 0 : 1; - } - if (command == "prove_and_verify_ultra_honk_program") { + } else if (command == "prove_and_verify_ultra_honk_program") { return proveAndVerifyHonkProgram(bytecode_path, recursive, witness_path) ? 0 : 1; - } - if (command == "prove_and_verify_mega_honk_program") { - return proveAndVerifyHonkProgram(bytecode_path, recursive, witness_path) ? 0 : 1; - } - // TODO(#7371): remove this - if (command == "client_ivc_prove_output_all_msgpack") { - 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 proof_path = output_dir / "client_ivc_proof"; - std::filesystem::path vk_path = output_dir / "client_ivc_vk"; - - return verify_client_ivc(proof_path, vk_path) ? 0 : 1; - } - if (command == "fold_and_verify_program") { - return foldAndVerifyProgram(bytecode_path, witness_path) ? 0 : 1; - } - - if (command == "prove") { + } else if (command == "prove") { std::string output_path = get_option(args, "-o", "./proofs/proof"); prove(bytecode_path, witness_path, output_path, recursive); } else if (command == "prove_output_all") { @@ -1474,9 +1148,6 @@ int main(int argc, char* argv[]) } else if (command == "prove_mega_honk_output_all") { std::string output_path = get_option(args, "-o", "./proofs"); prove_honk_output_all(bytecode_path, witness_path, output_path, recursive); - } else if (command == "client_ivc_prove_output_all") { - 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", "./target"); prove_tube(output_path); diff --git a/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 6412b711c..4ba9ae098 100644 --- a/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -157,8 +157,9 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) * @param precomputed_vk */ void ClientIVC::accumulate(ClientCircuit& circuit, + const bool _one_circuit, const std::shared_ptr& precomputed_vk, - bool mock_vk) + const bool mock_vk) { if (auto_verify_mode && circuit.databus_propagation_data.is_kernel) { complete_kernel_circuit_logic(circuit); @@ -191,13 +192,25 @@ void ClientIVC::accumulate(ClientCircuit& circuit, honk_vk = precomputed_vk ? precomputed_vk : std::make_shared(proving_key->proving_key); if (mock_vk) { honk_vk->set_metadata(proving_key->proving_key); + vinfo("set honk vk metadata"); } - vinfo("set honk vk metadata"); - // If this is the first circuit in the IVC, use oink to complete the decider proving key and generate an oink - // proof - if (!initialized) { - OinkProver oink_prover{ proving_key }; + if (_one_circuit) { + one_circuit = _one_circuit; + MegaProver prover{ proving_key }; + vinfo("computing mega proof..."); + mega_proof = prover.prove(); + vinfo("mega proof computed"); + + proving_key->is_accumulator = true; // indicate to PG that it should not run oink on this key + // Initialize the gate challenges to zero for use in first round of folding + proving_key->gate_challenges = std::vector(CONST_PG_LOG_N, 0); + + fold_output.accumulator = proving_key; + } else if (!initialized) { + // If this is the first circuit in the IVC, use oink to complete the decider proving key and generate an oink + // proof + MegaOinkProver oink_prover{ proving_key }; vinfo("computing oink proof..."); oink_prover.prove(); vinfo("oink proof constructed"); @@ -247,10 +260,8 @@ HonkProof ClientIVC::construct_and_prove_hiding_circuit() // inputs to the tube circuit) which are intermediate stages. // TODO(https://github.com/AztecProtocol/barretenberg/issues/1048): link these properly, likely insecure auto num_public_inputs = static_cast(static_cast(fold_proof[PUBLIC_INPUTS_SIZE_INDEX])); - vinfo("num_public_inputs of the last folding proof BEFORE SUBTRACTION", num_public_inputs); num_public_inputs -= bb::PAIRING_POINT_ACCUMULATOR_SIZE; // exclude aggregation object num_public_inputs -= bb::PROPAGATED_DATABUS_COMMITMENTS_SIZE; // exclude propagated databus commitments - vinfo("num_public_inputs of the last folding proof ", num_public_inputs); for (size_t i = 0; i < num_public_inputs; i++) { size_t offset = HONK_PROOF_PUBLIC_INPUT_OFFSET; builder.add_public_variable(fold_proof[i + offset]); @@ -298,8 +309,11 @@ HonkProof ClientIVC::construct_and_prove_hiding_circuit() */ ClientIVC::Proof ClientIVC::prove() { - HonkProof mega_proof = construct_and_prove_hiding_circuit(); - ASSERT(merge_verification_queue.size() == 1); // ensure only a single merge proof remains in the queue + if (!one_circuit) { + mega_proof = construct_and_prove_hiding_circuit(); + ASSERT(merge_verification_queue.size() == 1); // ensure only a single merge proof remains in the queue + } + MergeProof& merge_proof = merge_verification_queue[0]; return { mega_proof, goblin.prove(merge_proof) }; }; @@ -341,8 +355,8 @@ HonkProof ClientIVC::decider_prove() const vinfo("prove decider..."); fold_output.accumulator->proving_key.commitment_key = bn254_commitment_key; MegaDeciderProver decider_prover(fold_output.accumulator); - return decider_prover.construct_proof(); vinfo("finished decider proving."); + return decider_prover.construct_proof(); } /** diff --git a/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/cpp/src/barretenberg/client_ivc/client_ivc.hpp index 5fcf187a3..12ec8cdc0 100644 --- a/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -108,6 +108,7 @@ class ClientIVC { public: ProverFoldOutput fold_output; // prover accumulator and fold proof + HonkProof mega_proof; std::shared_ptr verifier_accumulator; // verifier accumulator std::shared_ptr honk_vk; // honk vk to be completed and folded into the accumulator @@ -133,6 +134,10 @@ class ClientIVC { GoblinProver goblin; + // We dynamically detect whether the input stack consists of one circuit, in which case we do not construct the + // hiding circuit and instead simply prove the single input circuit. + bool one_circuit = false; + bool initialized = false; // Is the IVC accumulator initialized ClientIVC(TraceSettings trace_settings = {}, bool auto_verify_mode = false) @@ -168,8 +173,9 @@ class ClientIVC { * @param mock_vk A boolean to say whether the precomputed vk shoudl have its metadata set. */ void accumulate(ClientCircuit& circuit, + const bool _one_circuit = false, const std::shared_ptr& precomputed_vk = nullptr, - bool mock_vk = false); + const bool mock_vk = false); Proof prove(); diff --git a/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp b/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp index ec0d253ce..befb8d82a 100644 --- a/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp +++ b/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp @@ -306,7 +306,7 @@ TEST_F(ClientIVCTests, PrecomputedVerificationKeys) // Construct and accumulate set of circuits using the precomputed vkeys for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc); - ivc.accumulate(circuit, precomputed_vks[idx]); + ivc.accumulate(circuit, /*one_circuit=*/false, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -331,7 +331,7 @@ TEST_F(ClientIVCTests, StructuredPrecomputedVKs) // Construct and accumulate set of circuits using the precomputed vkeys for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates); - ivc.accumulate(circuit, precomputed_vks[idx]); + ivc.accumulate(circuit, /*one_circuit=*/false, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); diff --git a/cpp/src/barretenberg/client_ivc/client_ivc_auto_verify.test.cpp b/cpp/src/barretenberg/client_ivc/client_ivc_auto_verify.test.cpp index a462adc2b..f4dafde38 100644 --- a/cpp/src/barretenberg/client_ivc/client_ivc_auto_verify.test.cpp +++ b/cpp/src/barretenberg/client_ivc/client_ivc_auto_verify.test.cpp @@ -165,7 +165,7 @@ TEST_F(ClientIVCAutoVerifyTests, PrecomputedVerificationKeys) // Accumulate each circuit using the precomputed VKs for (auto [circuit, precomputed_vk] : zip_view(circuits, precomputed_vkeys)) { - ivc.accumulate(circuit, precomputed_vk); + ivc.accumulate(circuit, /*one_circuit=*/false, precomputed_vk); } EXPECT_TRUE(ivc.prove_and_verify()); @@ -192,7 +192,7 @@ TEST_F(ClientIVCAutoVerifyTests, StructuredPrecomputedVKs) // Accumulate each circuit for (auto [circuit, precomputed_vk] : zip_view(circuits, precomputed_vkeys)) { - ivc.accumulate(circuit, precomputed_vk); + ivc.accumulate(circuit, /*one_circuit=*/false, precomputed_vk); } EXPECT_TRUE(ivc.prove_and_verify()); diff --git a/cpp/src/barretenberg/client_ivc/client_ivc_integration.test.cpp b/cpp/src/barretenberg/client_ivc/client_ivc_integration.test.cpp index 1eaf3c84d..2a32853c4 100644 --- a/cpp/src/barretenberg/client_ivc/client_ivc_integration.test.cpp +++ b/cpp/src/barretenberg/client_ivc/client_ivc_integration.test.cpp @@ -102,7 +102,7 @@ TEST_F(ClientIVCIntegrationTests, BenchmarkCasePrecomputedVKs) for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { Builder circuit = circuit_producer.create_next_circuit(ivc); - ivc.accumulate(circuit, precomputed_vks[idx]); + ivc.accumulate(circuit, /* one_circuit=*/false, precomputed_vks[idx]); } EXPECT_TRUE(ivc.prove_and_verify()); diff --git a/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp b/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp index 34fce9b31..44d07e4cd 100644 --- a/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp +++ b/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp @@ -46,7 +46,7 @@ void perform_ivc_accumulation_rounds(size_t NUM_CIRCUITS, circuit = circuit_producer.create_next_circuit(ivc); } - ivc.accumulate(circuit, precomputed_vks[circuit_idx], mock_vk); + ivc.accumulate(circuit, /*one_circuit=*/false, precomputed_vks[circuit_idx], mock_vk); } } diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index dba936225..c092e55fd 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -197,6 +197,7 @@ struct AcirProgramStack { void pop_back() { witness_stack.pop_back(); } }; +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1161) Refactor this function template Builder create_circuit(AcirFormat& constraint_system, // Specifies whether a prover that produces SNARK recursion friendly proofs should be used. diff --git a/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index f7110df3e..f4129c363 100644 --- a/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -570,7 +570,7 @@ class MegaFlavor { VerificationKey(const VerificationKey& vk) = default; - void set_metadata(ProvingKey& proving_key) + void set_metadata(const ProvingKey& proving_key) { this->pcs_verification_key = std::make_shared(); this->circuit_size = proving_key.circuit_size; diff --git a/cpp/src/barretenberg/sumcheck/sumcheck_round.test.cpp b/cpp/src/barretenberg/sumcheck/sumcheck_round.test.cpp index 236757ac1..3894ce604 100644 --- a/cpp/src/barretenberg/sumcheck/sumcheck_round.test.cpp +++ b/cpp/src/barretenberg/sumcheck/sumcheck_round.test.cpp @@ -42,7 +42,7 @@ TEST(SumcheckRound, SumcheckTupleOfTuplesOfUnivariates) univariate_2.template extend_to() * challenge[0] + univariate_3.template extend_to() * challenge[1]; - // Compare final batched univarites + // Compare final batched univariates EXPECT_EQ(result, result_expected); // Reinitialize univariate accumulators to zero diff --git a/cpp/src/barretenberg/ultra_honk/decider_prover.cpp b/cpp/src/barretenberg/ultra_honk/decider_prover.cpp index 7052c8e83..2a27bd4b2 100644 --- a/cpp/src/barretenberg/ultra_honk/decider_prover.cpp +++ b/cpp/src/barretenberg/ultra_honk/decider_prover.cpp @@ -80,7 +80,7 @@ template void DeciderProver_::execute_pcs_rounds( zk_sumcheck_data.libra_univariates_monomial, sumcheck_output.claimed_libra_evaluations); } - vinfo("executed multivariate-to-univarite reduction"); + vinfo("executed multivariate-to-univariate reduction"); PCS::compute_opening_proof(ck, prover_opening_claim, transcript); vinfo("computed opening proof"); } diff --git a/cpp/src/barretenberg/ultra_honk/oink_prover.hpp b/cpp/src/barretenberg/ultra_honk/oink_prover.hpp index 06e2d884f..8ab9fed7a 100644 --- a/cpp/src/barretenberg/ultra_honk/oink_prover.hpp +++ b/cpp/src/barretenberg/ultra_honk/oink_prover.hpp @@ -60,4 +60,7 @@ template class OinkProver { void execute_grand_product_computation_round(); RelationSeparator generate_alphas_round(); }; + +using MegaOinkProver = OinkProver; + } // namespace bb \ No newline at end of file diff --git a/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp b/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp index 11264c721..82608299e 100644 --- a/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp +++ b/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp @@ -46,6 +46,7 @@ template class UltraProver_ { HonkProof export_proof(); HonkProof construct_proof(); + HonkProof prove() { return construct_proof(); }; private: HonkProof proof;