diff --git a/barretenberg/acir_tests/Dockerfile.bb b/barretenberg/acir_tests/Dockerfile.bb index a20d3d53280..006db0e5333 100644 --- a/barretenberg/acir_tests/Dockerfile.bb +++ b/barretenberg/acir_tests/Dockerfile.bb @@ -7,10 +7,13 @@ COPY --from=0 /usr/src/barretenberg/cpp/build /usr/src/barretenberg/cpp/build COPY --from=noir-acir-tests /usr/src/noir/test_programs /usr/src/noir/test_programs WORKDIR /usr/src/barretenberg/acir_tests COPY . . -# Run every acir test through native bb build prove_then_verify flow. +# Run every acir test through native bb build prove_then_verify flow for UltraPlonk. # This ensures we test independent pk construction through real/garbage witness data paths. RUN FLOW=prove_then_verify ./run_acir_tests.sh -# TODO(https://github.com/AztecProtocol/barretenberg/issues/811) make this able to run the default test -RUN FLOW=prove_and_verify_goblin ./run_acir_tests.sh +# This flow is essentially the GoblinUltraHonk equivalent to the UltraPlonk "prove and verify". (This functionality is +# accessed via the goblin "accumulate" mechanism). +RUN FLOW=accumulate_and_verify_goblin ./run_acir_tests.sh +# This is a "full" Goblin flow. It constructs and verifies four proofs: GoblinUltraHonk, ECCVM, Translator, and merge +RUN FLOW=prove_and_verify_goblin ./run_acir_tests.sh 6_array # Run 1_mul through native bb build, all_cmds flow, to test all cli args. RUN VERBOSE=1 FLOW=all_cmds ./run_acir_tests.sh 1_mul diff --git a/barretenberg/acir_tests/Dockerfile.bb.js b/barretenberg/acir_tests/Dockerfile.bb.js index 4409bf7ce22..b894826a5c2 100644 --- a/barretenberg/acir_tests/Dockerfile.bb.js +++ b/barretenberg/acir_tests/Dockerfile.bb.js @@ -14,7 +14,10 @@ COPY . . ENV VERBOSE=1 # Run double_verify_proof through bb.js on node to check 512k support. RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify ./run_acir_tests.sh double_verify_proof -RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_goblin ./run_acir_tests.sh double_verify_proof +# Run a single arbitrary test not involving recursion through bb.js for GoblinUltraHonk +RUN BIN=../ts/dest/node/main.js FLOW=accumulate_and_verify_goblin ./run_acir_tests.sh 6_array +# Run a single arbitrary test not involving recursion through bb.js for full Goblin +RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_goblin ./run_acir_tests.sh 6_array # Run 1_mul through bb.js build, all_cmds flow, to test all cli args. RUN BIN=../ts/dest/node/main.js FLOW=all_cmds ./run_acir_tests.sh 1_mul # Run double_verify_proof through bb.js on chrome testing multi-threaded browser support. diff --git a/barretenberg/acir_tests/flows/accumulate_and_verify_goblin.sh b/barretenberg/acir_tests/flows/accumulate_and_verify_goblin.sh new file mode 100755 index 00000000000..a89e1a1dba1 --- /dev/null +++ b/barretenberg/acir_tests/flows/accumulate_and_verify_goblin.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +VFLAG=${VERBOSE:+-v} + +$BIN accumulate_and_verify_goblin $VFLAG -c $CRS_PATH -b ./target/acir.gz \ No newline at end of file diff --git a/barretenberg/acir_tests/run_acir_tests.sh b/barretenberg/acir_tests/run_acir_tests.sh index ee28c975113..5c7bb60c9e2 100755 --- a/barretenberg/acir_tests/run_acir_tests.sh +++ b/barretenberg/acir_tests/run_acir_tests.sh @@ -29,6 +29,7 @@ fi export BIN CRS_PATH VERBOSE BRANCH +# copy the gzipped acir test data from noir/test_programs to barretenberg/acir_tests ./clone_test_vectors.sh cd acir_tests diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 5f75206108e..b3d45629600 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,12 @@ void init_bn254_crs(size_t dyadic_circuit_size) 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); @@ -115,6 +122,43 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP return verified; } +/** + * @brief Constructs and verifies a Honk proof for an ACIR circuit via the Goblin accumulate mechanism + * + * 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 bytecodePath Path to the file containing the serialized acir constraint system + * @param witnessPath Path to the file containing the serialized witness + * @return verified + */ +bool accumulateAndVerifyGoblin(const std::string& bytecodePath, const std::string& witnessPath) +{ + // Populate the acir constraint system and witness from gzipped data + auto constraint_system = get_constraint_system(bytecodePath); + auto witness = get_witness(witnessPath); + + // Instantiate a Goblin acir composer and construct a bberg circuit from the acir representation + acir_proofs::GoblinAcirComposer acir_composer; + acir_composer.create_circuit(constraint_system, witness); + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set + // to max circuit size present in acir tests suite. + size_t hardcoded_bn254_dyadic_size_hack = 1 << 18; + init_bn254_crs(hardcoded_bn254_dyadic_size_hack); + size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only + init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack); + + // Call accumulate to generate a GoblinUltraHonk proof + auto proof = acir_composer.accumulate(); + + // Verify the GoblinUltraHonk proof + auto verified = acir_composer.verify_accumulator(proof); + + return verified; +} + /** * @brief Proves and Verifies an ACIR circuit * @@ -132,11 +176,13 @@ bool proveAndVerifyGoblin(const std::string& bytecodePath, const std::string& witnessPath, [[maybe_unused]] bool recursive) { + // Populate the acir constraint system and witness from gzipped data auto constraint_system = get_constraint_system(bytecodePath); auto witness = get_witness(witnessPath); - acir_proofs::AcirComposer acir_composer; - acir_composer.create_goblin_circuit(constraint_system, witness); + // Instantiate a Goblin acir composer and construct a bberg circuit from the acir representation + acir_proofs::GoblinAcirComposer acir_composer; + acir_composer.create_circuit(constraint_system, witness); // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set // to max circuit size present in acir tests suite. @@ -145,9 +191,11 @@ bool proveAndVerifyGoblin(const std::string& bytecodePath, size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack); - auto proof = acir_composer.create_goblin_proof(); + // Generate a GoblinUltraHonk proof and a full Goblin proof + auto proof = acir_composer.accumulate_and_prove(); - auto verified = acir_composer.verify_goblin_proof(proof); + // Verify the GoblinUltraHonk proof and the full Goblin proof + auto verified = acir_composer.verify(proof); return verified; } @@ -458,6 +506,9 @@ int main(int argc, char* argv[]) if (command == "prove_and_verify") { return proveAndVerify(bytecode_path, witness_path, recursive) ? 0 : 1; } + if (command == "accumulate_and_verify_goblin") { + return accumulateAndVerifyGoblin(bytecode_path, witness_path) ? 0 : 1; + } if (command == "prove_and_verify_goblin") { return proveAndVerifyGoblin(bytecode_path, witness_path, recursive) ? 0 : 1; } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp index 7c17e46d19b..86239cbb481 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp @@ -3,7 +3,6 @@ #include "barretenberg/common/throw_or_abort.hpp" #include "barretenberg/dsl/acir_format/acir_format.hpp" #include "barretenberg/dsl/types.hpp" -#include "barretenberg/goblin/mock_circuits.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" @@ -61,27 +60,6 @@ std::vector AcirComposer::create_proof(bool is_recursive) return proof; } -void AcirComposer::create_goblin_circuit(acir_format::acir_format& constraint_system, - acir_format::WitnessVector& witness) -{ - // Construct a builder using the witness and public input data from acir - goblin_builder_ = acir_format::GoblinBuilder{ - goblin.op_queue, witness, constraint_system.public_inputs, constraint_system.varnum - }; - - // Populate constraints in the builder via the data in constraint_system - acir_format::build_constraints(goblin_builder_, constraint_system, true); - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/817): Add some arbitrary op gates to ensure the - // associated polynomials are non-zero and to give ECCVM and Translator some ECC ops to process. - GoblinMockCircuits::construct_goblin_ecc_op_circuit(goblin_builder_); -} - -std::vector AcirComposer::create_goblin_proof() -{ - return goblin.construct_proof(goblin_builder_); -} - std::shared_ptr AcirComposer::init_verification_key() { if (!proving_key_) { @@ -132,11 +110,6 @@ bool AcirComposer::verify_proof(std::vector const& proof, bool is_recur } } -bool AcirComposer::verify_goblin_proof(std::vector const& proof) -{ - return goblin.verify_proof({ proof }); -} - std::string AcirComposer::get_solidity_verifier() { std::ostringstream stream; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp index 7acad4ff622..1010660ce5f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include namespace acir_proofs { @@ -38,15 +37,8 @@ class AcirComposer { std::vector serialize_verification_key_into_fields(); - // Goblin specific methods - void create_goblin_circuit(acir_format::acir_format& constraint_system, acir_format::WitnessVector& witness); - std::vector create_goblin_proof(); - bool verify_goblin_proof(std::vector const& proof); - private: acir_format::Builder builder_; - acir_format::GoblinBuilder goblin_builder_; - Goblin goblin; size_t size_hint_; std::shared_ptr proving_key_; std::shared_ptr verification_key_; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 954fe16832e..18d44941e56 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -6,6 +6,7 @@ #include "barretenberg/common/serialize.hpp" #include "barretenberg/common/slab_allocator.hpp" #include "barretenberg/dsl/acir_format/acir_format.hpp" +#include "barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" #include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" #include "barretenberg/srs/global_crs.hpp" @@ -26,6 +27,11 @@ WASM_EXPORT void acir_new_acir_composer(uint32_t const* size_hint, out_ptr out) *out = new acir_proofs::AcirComposer(ntohl(*size_hint)); } +WASM_EXPORT void acir_new_goblin_acir_composer(out_ptr out) +{ + *out = new acir_proofs::GoblinAcirComposer(); +} + WASM_EXPORT void acir_delete_acir_composer(in_ptr acir_composer_ptr) { delete reinterpret_cast(*acir_composer_ptr); @@ -57,17 +63,31 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, *out = to_heap_buffer(proof_data); } -WASM_EXPORT void acir_create_goblin_proof(in_ptr acir_composer_ptr, - uint8_t const* acir_vec, - uint8_t const* witness_vec, - uint8_t** out) +WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr, + uint8_t const* acir_vec, + uint8_t const* witness_vec, + uint8_t** out) { - auto acir_composer = reinterpret_cast(*acir_composer_ptr); + auto acir_composer = reinterpret_cast(*acir_composer_ptr); auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - acir_composer->create_goblin_circuit(constraint_system, witness); - auto proof_data = acir_composer->create_goblin_proof(); + acir_composer->create_circuit(constraint_system, witness); + auto proof_data = acir_composer->accumulate(); + *out = to_heap_buffer(proof_data); +} + +WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr, + uint8_t const* acir_vec, + uint8_t const* witness_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 witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); + + acir_composer->create_circuit(constraint_system, witness); + auto proof_data = acir_composer->accumulate_and_prove(); *out = to_heap_buffer(proof_data); } @@ -102,11 +122,18 @@ WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* a *out = to_heap_buffer(to_buffer(*pk)); } -WASM_EXPORT void acir_verify_goblin_proof(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result) +WASM_EXPORT void acir_goblin_verify_accumulator(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result) { - auto acir_composer = reinterpret_cast(*acir_composer_ptr); + auto acir_composer = reinterpret_cast(*acir_composer_ptr); + auto proof = from_buffer>(proof_buf); + *result = acir_composer->verify_accumulator(proof); +} + +WASM_EXPORT void acir_goblin_verify(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result) +{ + auto acir_composer = reinterpret_cast(*acir_composer_ptr); auto proof = from_buffer>(proof_buf); - *result = acir_composer->verify_goblin_proof(proof); + *result = acir_composer->verify(proof); } WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index cb6b8643512..76b24886b1d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -13,6 +13,8 @@ WASM_EXPORT void acir_get_circuit_sizes(uint8_t const* constraint_system_buf, WASM_EXPORT void acir_new_acir_composer(uint32_t const* size_hint, out_ptr out); +WASM_EXPORT void acir_new_goblin_acir_composer(out_ptr out); + WASM_EXPORT void acir_delete_acir_composer(in_ptr acir_composer_ptr); WASM_EXPORT void acir_create_circuit(in_ptr acir_composer_ptr, @@ -32,10 +34,25 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, bool const* is_recursive, uint8_t** out); -WASM_EXPORT void acir_create_goblin_proof(in_ptr acir_composer_ptr, - uint8_t const* constraint_system_buf, - uint8_t const* witness_buf, - uint8_t** out); +/** + * @brief Perform the goblin accumulate operation + * @details Constructs a GUH proof and possibly handles transcript merge logic + * + */ +WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr, + uint8_t const* constraint_system_buf, + uint8_t const* witness_buf, + uint8_t** out); + +/** + * @brief Construct a full goblin proof + * @details Makes a call to accumulate to a final circuit before constructing a Goblin proof + * + */ +WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr, + uint8_t const* constraint_system_buf, + uint8_t const* witness_buf, + uint8_t** out); WASM_EXPORT void acir_load_verification_key(in_ptr acir_composer_ptr, uint8_t const* vk_buf); @@ -50,7 +67,17 @@ WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, bool const* is_recursive, bool* result); -WASM_EXPORT void acir_verify_goblin_proof(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result); +/** + * @brief Verifies a GUH proof produced during goblin accumulation + * + */ +WASM_EXPORT void acir_goblin_verify_accumulator(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result); + +/** + * @brief Verifies a full goblin proof (and the GUH proof produced by accumulation) + * + */ +WASM_EXPORT void acir_goblin_verify(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result); WASM_EXPORT void acir_get_solidity_verifier(in_ptr acir_composer_ptr, out_str_buf out); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp new file mode 100644 index 00000000000..c5e3698d182 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp @@ -0,0 +1,68 @@ +#include "goblin_acir_composer.hpp" +#include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/dsl/acir_format/acir_format.hpp" +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" + +namespace acir_proofs { + +GoblinAcirComposer::GoblinAcirComposer() {} + +void GoblinAcirComposer::create_circuit(acir_format::acir_format& constraint_system, + acir_format::WitnessVector& witness) +{ + // Construct a builder using the witness and public input data from acir and with the goblin-owned op_queue + builder_ = acir_format::GoblinBuilder{ + goblin.op_queue, witness, constraint_system.public_inputs, constraint_system.varnum + }; + + // Populate constraints in the builder via the data in constraint_system + acir_format::build_constraints(builder_, constraint_system, true); + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/817): Add some arbitrary op gates to ensure the + // associated polynomials are non-zero and to give ECCVM and Translator some ECC ops to process. + GoblinMockCircuits::construct_goblin_ecc_op_circuit(builder_); +} + +std::vector GoblinAcirComposer::accumulate() +{ + // // Construct a GUH proof for the circuit via the accumulate mechanism + // return goblin.accumulate_for_acir(builder_); + + // Construct one final GUH proof via the accumulate mechanism + std::vector ultra_proof = goblin.accumulate_for_acir(builder_); + + // Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally + goblin.prove_for_acir(); + + return ultra_proof; +} + +bool GoblinAcirComposer::verify_accumulator(std::vector const& proof) +{ + return goblin.verify_accumulator_for_acir(proof); +} + +std::vector GoblinAcirComposer::accumulate_and_prove() +{ + // Construct one final GUH proof via the accumulate mechanism + std::vector ultra_proof = goblin.accumulate_for_acir(builder_); + + // Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally + goblin.prove_for_acir(); + + return ultra_proof; +} + +bool GoblinAcirComposer::verify(std::vector const& proof) +{ + // Verify the final GUH proof + bool ultra_verified = goblin.verify_accumulator_for_acir(proof); + + // Verify the Goblin proof (ECCVM, Translator, Merge) + bool goblin_verified = goblin.verify_for_acir(); + + return ultra_verified && goblin_verified; +} + +} // namespace acir_proofs diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp new file mode 100644 index 00000000000..d8169202e62 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp @@ -0,0 +1,73 @@ +#pragma once +#include +#include + +namespace acir_proofs { + +/** + * @brief A class responsible for marshalling construction of keys and prover and verifier instances used to prove + * satisfiability of circuits written in ACIR. + * + */ +class GoblinAcirComposer { + + using WitnessVector = std::vector>; + + public: + GoblinAcirComposer(); + + /** + * @brief Create a GUH circuit from an acir constraint system and a witness + * + * @param constraint_system ACIR representation of the constraints defining the circuit + * @param witness The witness values known to ACIR during construction of the constraint system + */ + void create_circuit(acir_format::acir_format& constraint_system, acir_format::WitnessVector& witness); + + /** + * @brief Accumulate a circuit via Goblin + * @details For the present circuit, construct a GUH proof and the vkey needed to verify it + * + * @return std::vector The GUH proof bytes + */ + std::vector accumulate(); + + /** + * @brief Verify the Goblin accumulator (the GUH proof) using the vkey internal to Goblin + * + * @param proof + * @return bool Whether or not the proof was verified + */ + bool verify_accumulator(std::vector const& proof); + + /** + * @brief Accumulate a final circuit and construct a full Goblin proof + * @details Accumulation means constructing a GUH proof of a single (final) circuit. A full Goblin proof consists of + * a merge proof, an ECCVM proof and a Translator proof. The Goblin proof is only constructed at the end of the + * accumulation phase and establishes the correctness of the ECC operations written to the op queue throughout the + * accumulation phase. + * + */ + std::vector accumulate_and_prove(); + + /** + * @brief Verify the final GUH proof and the full Goblin proof + * + * @return bool verified + */ + bool verify(std::vector const& proof); + + private: + acir_format::GoblinBuilder builder_; + Goblin goblin; + bool verbose_ = true; + + template inline void vinfo(Args... args) + { + if (verbose_) { + info(args...); + } + } +}; + +} // namespace acir_proofs diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp index 7ba019a6f8f..aac0ef305ca 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -84,12 +84,12 @@ class Goblin { std::unique_ptr eccvm_prover; std::unique_ptr translator_composer; - AccumulationOutput accumulator; // ACIRHACK - Proof proof_; // ACIRHACK + AccumulationOutput accumulator; // Used only for ACIR methods for now public: /** - * @brief If there is a previous merge proof, recursively verify it. Generate next accmulated proof and merge proof. + * @brief Construct a GUH proof and a merge proof for the present circuit. + * @details If there is a previous merge proof, recursively verify it. * * @param circuit_builder */ @@ -118,10 +118,12 @@ class Goblin { return { ultra_proof, instance->verification_key }; }; + /** + * @brief Construct an ECCVM proof and the translation polynomial evaluations + * + */ void prove_eccvm() { - goblin_proof.merge_proof = std::move(merge_proof); - eccvm_builder = std::make_unique(op_queue); eccvm_composer = std::make_unique(); eccvm_prover = std::make_unique(eccvm_composer->create_prover(*eccvm_builder)); @@ -129,6 +131,10 @@ class Goblin { goblin_proof.translation_evaluations = eccvm_prover->translation_evaluations; }; + /** + * @brief Construct a translator proof + * + */ void prove_translator() { translator_builder = std::make_unique( @@ -138,13 +144,28 @@ class Goblin { goblin_proof.translator_proof = translator_prover.construct_proof(); }; + /** + * @brief Constuct a full Goblin proof (ECCVM, Translator, merge) + * @details The merge proof is assumed to already have been constucted in the last accumulate step. It is simply + * moved into the final proof here. + * + * @return Proof + */ Proof prove() { + goblin_proof.merge_proof = std::move(merge_proof); prove_eccvm(); prove_translator(); return goblin_proof; }; + /** + * @brief Verify a full Goblin proof (ECCVM, Translator, merge) + * + * @param proof + * @return true + * @return false + */ bool verify(const Proof& proof) { MergeVerifier merge_verifier; @@ -162,14 +183,23 @@ class Goblin { return merge_verified && eccvm_verified && accumulator_construction_verified && translation_verified; }; - // ACIRHACK - AccumulationOutput accumulate_for_acir(GoblinUltraCircuitBuilder& circuit_builder) + // The methods below this point are to be used only for ACIR. They exist while the interface is in flux. Eventually + // there will be agreement and no acir-specific methods should be needed. + + /** + * @brief Construct a GUH proof for the given circuit. (No merge proof for now) + * + * @param circuit_builder + * @return std::vector + */ + std::vector accumulate_for_acir(GoblinUltraCircuitBuilder& circuit_builder) { - // Complete the circuit logic by recursively verifying previous merge proof if it exists - if (merge_proof_exists) { - RecursiveMergeVerifier merge_verifier{ &circuit_builder }; - [[maybe_unused]] auto pairing_points = merge_verifier.verify_proof(merge_proof); - } + // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): no merge prover for now + // // Complete the circuit logic by recursively verifying previous merge proof if it exists + // if (merge_proof_exists) { + // RecursiveMergeVerifier merge_verifier{ &circuit_builder }; + // [[maybe_unused]] auto pairing_points = merge_verifier.verify_proof(merge_proof); + // } // Construct a Honk proof for the main circuit GoblinUltraComposer composer; @@ -177,6 +207,8 @@ class Goblin { auto prover = composer.create_prover(instance); auto ultra_proof = prover.construct_proof(); + accumulator = { ultra_proof, instance->verification_key }; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): no merge prover for now since we're not // mocking the first set of ecc ops // // Construct and store the merge proof to be recursively verified on the next call to accumulate @@ -187,88 +219,56 @@ class Goblin { // merge_proof_exists = true; // } - accumulator = { ultra_proof, instance->verification_key }; - return accumulator; + return ultra_proof.proof_data; }; - // ACIRHACK - Proof prove_for_acir() + /** + * @brief Verify a GUH proof + * + * @param proof_buf + * @return true + * @return false + */ + bool verify_accumulator_for_acir(const std::vector& proof_buf) const { - Proof proof; - - proof.merge_proof = std::move(merge_proof); - - eccvm_builder = std::make_unique(op_queue); - eccvm_composer = std::make_unique(); - auto eccvm_prover = eccvm_composer->create_prover(*eccvm_builder); - proof.eccvm_proof = eccvm_prover.construct_proof(); - proof.translation_evaluations = eccvm_prover.translation_evaluations; + GoblinUltraVerifier verifier{ accumulator.verification_key }; + HonkProof proof{ proof_buf }; + bool verified = verifier.verify_proof(proof); - translator_builder = std::make_unique( - eccvm_prover.translation_batching_challenge_v, eccvm_prover.evaluation_challenge_x, op_queue); - translator_composer = std::make_unique(); - auto translator_prover = translator_composer->create_prover(*translator_builder, eccvm_prover.transcript); - proof.translator_proof = translator_prover.construct_proof(); + return verified; + } - proof_ = proof; // ACIRHACK - return proof; - }; + /** + * @brief Construct a Goblin proof + * + * @return Proof + */ + Proof prove_for_acir() { return prove(); }; - // ACIRHACK - bool verify_for_acir(const Proof& proof) const + /** + * @brief Verify a Goblin proof (excluding the merge proof for now) + * + * @return true + * @return false + */ + bool verify_for_acir() const { - // ACIRHACK + // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): No merge proof for now // MergeVerifier merge_verifier; - // bool merge_verified = merge_verifier.verify_proof(proof.merge_proof); + // bool merge_verified = merge_verifier.verify_proof(goblin_proof.merge_proof); auto eccvm_verifier = eccvm_composer->create_verifier(*eccvm_builder); - bool eccvm_verified = eccvm_verifier.verify_proof(proof.eccvm_proof); + bool eccvm_verified = eccvm_verifier.verify_proof(goblin_proof.eccvm_proof); auto translator_verifier = translator_composer->create_verifier(*translator_builder, eccvm_verifier.transcript); - bool accumulator_construction_verified = translator_verifier.verify_proof(proof.translator_proof); + bool translation_accumulator_construction_verified = + translator_verifier.verify_proof(goblin_proof.translator_proof); // TODO(https://github.com/AztecProtocol/barretenberg/issues/799): Ensure translation_evaluations are passed // correctly - bool translation_verified = translator_verifier.verify_translation(proof.translation_evaluations); + bool translation_verified = translator_verifier.verify_translation(goblin_proof.translation_evaluations); - return /* merge_verified && */ eccvm_verified && accumulator_construction_verified && translation_verified; + return /* merge_verified && */ eccvm_verified && translation_accumulator_construction_verified && + translation_verified; }; - - // ACIRHACK - std::vector construct_proof(GoblinUltraCircuitBuilder& builder) - { - // Construct a GUH proof - accumulate_for_acir(builder); - - std::vector result(accumulator.proof.proof_data.size()); - - const auto insert = [&result](const std::vector& buf) { - result.insert(result.end(), buf.begin(), buf.end()); - }; - - insert(accumulator.proof.proof_data); - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/819): Skip ECCVM/Translator proof for now - // std::vector goblin_proof = prove_for_acir().to_buffer(); - // insert(goblin_proof); - - return result; - } - - // ACIRHACK - bool verify_proof([[maybe_unused]] const bb::plonk::proof& proof) const - { - // ACIRHACK: to do this properly, extract the proof correctly or maybe share transcripts. - const auto extract_final_kernel_proof = [&]([[maybe_unused]] auto& input_proof) { return accumulator.proof; }; - - GoblinUltraVerifier verifier{ accumulator.verification_key }; - bool verified = verifier.verify_proof(extract_final_kernel_proof(proof)); - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/819): Skip ECCVM/Translator verification for now - // const auto extract_goblin_proof = [&]([[maybe_unused]] auto& input_proof) { return proof_; }; - // auto goblin_proof = extract_goblin_proof(proof); - // verified = verified && verify_for_acir(goblin_proof); - - return verified; - } }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 6a8bd360ccf..3925ff1fa54 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -466,6 +466,17 @@ ], "isAsync": false }, + { + "functionName": "acir_new_goblin_acir_composer", + "inArgs": [], + "outArgs": [ + { + "name": "out", + "type": "out_ptr" + } + ], + "isAsync": false + }, { "functionName": "acir_delete_acir_composer", "inArgs": [ @@ -540,7 +551,31 @@ "isAsync": false }, { - "functionName": "acir_create_goblin_proof", + "functionName": "acir_goblin_accumulate", + "inArgs": [ + { + "name": "acir_composer_ptr", + "type": "in_ptr" + }, + { + "name": "constraint_system_buf", + "type": "const uint8_t *" + }, + { + "name": "witness_buf", + "type": "const uint8_t *" + } + ], + "outArgs": [ + { + "name": "out", + "type": "uint8_t **" + } + ], + "isAsync": false + }, + { + "functionName": "acir_goblin_prove", "inArgs": [ { "name": "acir_composer_ptr", @@ -650,7 +685,27 @@ "isAsync": false }, { - "functionName": "acir_verify_goblin_proof", + "functionName": "acir_goblin_verify_accumulator", + "inArgs": [ + { + "name": "acir_composer_ptr", + "type": "in_ptr" + }, + { + "name": "proof_buf", + "type": "const uint8_t *" + } + ], + "outArgs": [ + { + "name": "result", + "type": "bool *" + } + ], + "isAsync": false + }, + { + "functionName": "acir_goblin_verify", "inArgs": [ { "name": "acir_composer_ptr", diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index 9ee50ba908b..d6280851562 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -304,6 +304,18 @@ export class BarretenbergApi { return out[0]; } + async acirNewGoblinAcirComposer(): Promise { + const inArgs = [].map(serializeBufferable); + const outTypes: OutputType[] = [Ptr]; + const result = await this.wasm.callWasmExport( + 'acir_new_goblin_acir_composer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + async acirDeleteAcirComposer(acirComposerPtr: Ptr): Promise { const inArgs = [acirComposerPtr].map(serializeBufferable); const outTypes: OutputType[] = []; @@ -357,7 +369,23 @@ export class BarretenbergApi { return out[0]; } - async acirCreateGoblinProof( + async acirGoblinAccumulate( + acirComposerPtr: Ptr, + constraintSystemBuf: Uint8Array, + witnessBuf: Uint8Array, + ): Promise { + const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = await this.wasm.callWasmExport( + 'acir_goblin_accumulate', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + async acirGoblinProve( acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array, @@ -365,7 +393,7 @@ export class BarretenbergApi { const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = await this.wasm.callWasmExport( - 'acir_create_goblin_proof', + 'acir_goblin_prove', inArgs, outTypes.map(t => t.SIZE_IN_BYTES), ); @@ -433,11 +461,23 @@ export class BarretenbergApi { return out[0]; } - async acirVerifyGoblinProof(acirComposerPtr: Ptr, proofBuf: Uint8Array): Promise { + async acirGoblinVerifyAccumulator(acirComposerPtr: Ptr, proofBuf: Uint8Array): Promise { + const inArgs = [acirComposerPtr, proofBuf].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = await this.wasm.callWasmExport( + 'acir_goblin_verify_accumulator', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + async acirGoblinVerify(acirComposerPtr: Ptr, proofBuf: Uint8Array): Promise { const inArgs = [acirComposerPtr, proofBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = await this.wasm.callWasmExport( - 'acir_verify_goblin_proof', + 'acir_goblin_verify', inArgs, outTypes.map(t => t.SIZE_IN_BYTES), ); @@ -777,6 +817,18 @@ export class BarretenbergApiSync { return out[0]; } + acirNewGoblinAcirComposer(): Ptr { + const inArgs = [].map(serializeBufferable); + const outTypes: OutputType[] = [Ptr]; + const result = this.wasm.callWasmExport( + 'acir_new_goblin_acir_composer', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + acirDeleteAcirComposer(acirComposerPtr: Ptr): void { const inArgs = [acirComposerPtr].map(serializeBufferable); const outTypes: OutputType[] = []; @@ -830,11 +882,23 @@ export class BarretenbergApiSync { return out[0]; } - acirCreateGoblinProof(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Uint8Array { + acirGoblinAccumulate(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Uint8Array { + const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_goblin_accumulate', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirGoblinProve(acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Uint8Array { const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = this.wasm.callWasmExport( - 'acir_create_goblin_proof', + 'acir_goblin_prove', inArgs, outTypes.map(t => t.SIZE_IN_BYTES), ); @@ -902,11 +966,23 @@ export class BarretenbergApiSync { return out[0]; } - acirVerifyGoblinProof(acirComposerPtr: Ptr, proofBuf: Uint8Array): boolean { + acirGoblinVerifyAccumulator(acirComposerPtr: Ptr, proofBuf: Uint8Array): boolean { + const inArgs = [acirComposerPtr, proofBuf].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_goblin_verify_accumulator', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + const out = result.map((r, i) => outTypes[i].fromBuffer(r)); + return out[0]; + } + + acirGoblinVerify(acirComposerPtr: Ptr, proofBuf: Uint8Array): boolean { const inArgs = [acirComposerPtr, proofBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = this.wasm.callWasmExport( - 'acir_verify_goblin_proof', + 'acir_goblin_verify', inArgs, outTypes.map(t => t.SIZE_IN_BYTES), ); diff --git a/barretenberg/ts/src/main.ts b/barretenberg/ts/src/main.ts index 208c4851a1f..d06de96431d 100755 --- a/barretenberg/ts/src/main.ts +++ b/barretenberg/ts/src/main.ts @@ -76,6 +76,7 @@ async function initGoblin(bytecodePath: string, crsPath: string) { const hardcodedGrumpkinSubgroupSizeHack = 262144; const initData = await init(bytecodePath, crsPath, hardcodedGrumpkinSubgroupSizeHack); const { api } = initData; + initData.acirComposer = await api.acirNewGoblinAcirComposer(); // Plus 1 needed! (Move +1 into Crs?) // Need both grumpkin and bn254 SRS's currently @@ -128,6 +129,35 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string, /* eslint-enable camelcase */ } +export async function accumulateAndVerifyGoblin(bytecodePath: string, witnessPath: string, crsPath: string) { + /* eslint-disable camelcase */ + const acir_test = path.basename(process.cwd()); + + const { api, acirComposer, circuitSize, subgroupSize } = await initGoblin(bytecodePath, crsPath); + try { + debug(`In accumulateAndVerifyGoblin:`); + const bytecode = getBytecode(bytecodePath); + const witness = getWitness(witnessPath); + + writeBenchmark('gate_count', circuitSize, { acir_test, threads }); + writeBenchmark('subgroup_size', subgroupSize, { acir_test, threads }); + + debug(`acirGoblinAccumulate()`); + const proofTimer = new Timer(); + const proof = await api.acirGoblinAccumulate(acirComposer, bytecode, witness); + writeBenchmark('proof_construction_time', proofTimer.ms(), { acir_test, threads }); + + debug(`acirVerifyGoblinProof()`); + const verified = await api.acirGoblinVerifyAccumulator(acirComposer, proof); + debug(`verified: ${verified}`); + console.log({ verified }); + return verified; + } finally { + await api.destroy(); + } + /* eslint-enable camelcase */ +} + export async function proveAndVerifyGoblin(bytecodePath: string, witnessPath: string, crsPath: string) { /* eslint-disable camelcase */ const acir_test = path.basename(process.cwd()); @@ -142,11 +172,11 @@ export async function proveAndVerifyGoblin(bytecodePath: string, witnessPath: st writeBenchmark('subgroup_size', subgroupSize, { acir_test, threads }); const proofTimer = new Timer(); - const proof = await api.acirCreateGoblinProof(acirComposer, bytecode, witness); + const proof = await api.acirGoblinProve(acirComposer, bytecode, witness); writeBenchmark('proof_construction_time', proofTimer.ms(), { acir_test, threads }); debug(`verifying...`); - const verified = await api.acirVerifyGoblinProof(acirComposer, proof); + const verified = await api.acirGoblinVerify(acirComposer, proof); debug(`verified: ${verified}`); console.log({ verified }); return verified; @@ -356,9 +386,20 @@ program process.exit(result ? 0 : 1); }); +program + .command('accumulate_and_verify_goblin') + .description('Generate a GUH proof and verify it. Process exits with success or failure code.') + .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/acir.gz') + .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') + .action(async ({ bytecodePath, witnessPath, crsPath }) => { + handleGlobalOptions(); + const result = await accumulateAndVerifyGoblin(bytecodePath, witnessPath, crsPath); + process.exit(result ? 0 : 1); + }); + program .command('prove_and_verify_goblin') - .description('Generate a proof and verify it. Process exits with success or failure code.') + .description('Generate a Goblin proof and verify it. Process exits with success or failure code.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/acir.gz') .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') .action(async ({ bytecodePath, witnessPath, crsPath }) => {