diff --git a/barretenberg/Earthfile b/barretenberg/Earthfile index ba15020ce71..64515490646 100644 --- a/barretenberg/Earthfile +++ b/barretenberg/Earthfile @@ -38,6 +38,8 @@ barretenberg-acir-tests-bb: RUN FLOW=prove_and_verify_mega_honk ./run_acir_tests.sh 6_array # Construct and verify a UltraHonk proof for all ACIR programs using the new witness stack workflow RUN FLOW=prove_and_verify_ultra_honk_program ./run_acir_tests.sh + # Fold and verify an ACIR program stack using ClientIvc + RUN FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic # This is a "full" Goblin flow. It constructs and verifies four proofs: MegaHonk, 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. diff --git a/barretenberg/acir_tests/Dockerfile.bb.js b/barretenberg/acir_tests/Dockerfile.bb.js index e485ba86b6f..dd5269e6902 100644 --- a/barretenberg/acir_tests/Dockerfile.bb.js +++ b/barretenberg/acir_tests/Dockerfile.bb.js @@ -21,10 +21,12 @@ RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify ./run_acir_tests.sh ecdsa RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify_ultra_honk ./run_acir_tests.sh nested_array_dynamic # Run a single arbitrary test not involving recursion through bb.js for Plonk RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify ./run_acir_tests.sh poseidon_bn254_hash -# Run a single arbitrary test not involving recursion through bb.js for MegaHonk +# Run a single arbitrary test not involving recursion through bb.js for UltraHonk RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh closures_mut_ref -# Run a single arbitrary test for separate prove and verify for UltraHonk +# Run a single arbitrary test for separate prove and verify for MegaHonk RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_mega_honk ./run_acir_tests.sh 6_array +# Fold and verify an ACIR program stack +RUN BIN=../ts/dest/node/main.js FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic # 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. diff --git a/barretenberg/acir_tests/flows/fold_and_verify_program.sh b/barretenberg/acir_tests/flows/fold_and_verify_program.sh new file mode 100755 index 00000000000..870873befcd --- /dev/null +++ b/barretenberg/acir_tests/flows/fold_and_verify_program.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +VFLAG=${VERBOSE:+-v} + +$BIN fold_and_verify_program $VFLAG -c $CRS_PATH -b ./target/program.json diff --git a/barretenberg/acir_tests/flows/prove_and_verify_mega_honk_program.sh b/barretenberg/acir_tests/flows/prove_and_verify_mega_honk_program.sh new file mode 100755 index 00000000000..21e15fbf7c5 --- /dev/null +++ b/barretenberg/acir_tests/flows/prove_and_verify_mega_honk_program.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +VFLAG=${VERBOSE:+-v} + +$BIN prove_and_verify_mega_honk_program $VFLAG -c $CRS_PATH -b ./target/program.json diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index b375249999d..2a69a57706d 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -104,6 +104,7 @@ include(GNUInstallDirs) message(STATUS "Compiling all-in-one barretenberg archive") set(BARRETENBERG_TARGET_OBJECTS + $ $ $ $ diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index d69f92869f1..091c87ad0cb 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -1,4 +1,5 @@ #include "barretenberg/bb/file_io.hpp" +#include "barretenberg/client_ivc/client_ivc.hpp" #include "barretenberg/common/serialize.hpp" #include "barretenberg/dsl/acir_format/acir_format.hpp" #include "barretenberg/dsl/types.hpp" @@ -219,6 +220,34 @@ bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const std::strin return true; } +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 << 18); + init_grumpkin_crs(1 << 14); + + ClientIVC ivc; + ivc.structured_flag = true; + + auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath); + + // Accumulate the entire program stack into the IVC + 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, 0, stack_item.witness, false, ivc.goblin.op_queue); + + ivc.accumulate(circuit); + + program_stack.pop_back(); + } + return ivc.prove_and_verify(); +} + /** * @brief Proves and Verifies an ACIR circuit * @@ -832,6 +861,12 @@ int main(int argc, char* argv[]) if (command == "prove_and_verify_ultra_honk_program") { return proveAndVerifyHonkProgram(bytecode_path, witness_path) ? 0 : 1; } + if (command == "prove_and_verify_mega_honk_program") { + return proveAndVerifyHonkProgram(bytecode_path, witness_path) ? 0 : 1; + } + if (command == "fold_and_verify_program") { + return foldAndVerifyProgram(bytecode_path, witness_path) ? 0 : 1; + } if (command == "prove_and_verify_goblin") { return proveAndVerifyGoblin(bytecode_path, witness_path) ? 0 : 1; } diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 5f7df7163c3..53e0eaaa194 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -117,4 +117,18 @@ std::vector> ClientIVC::precompute_f return vkeys; } +/** + * @brief Construct and verify a proof for the IVC + * @note Use of this method only makes sense when the prover and verifier are the same entity, e.g. in + * development/testing. + * + */ +bool ClientIVC::prove_and_verify() +{ + auto proof = prove(); + + auto verifier_inst = std::make_shared(this->instance_vk); + return verify(proof, { this->verifier_accumulator, verifier_inst }); +} + } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index b7885ae4e0c..deea3d3c86e 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -87,6 +87,8 @@ class ClientIVC { bool verify(Proof& proof, const std::vector>& verifier_instances); + bool prove_and_verify(); + HonkProof decider_prove() const; std::vector> precompute_folding_verification_keys(std::vector); diff --git a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt index e385ee6b05a..c5822d80b4c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt @@ -2,6 +2,7 @@ barretenberg_module( dsl plonk ultra_honk + client_ivc stdlib_sha256 stdlib_aes128 stdlib_keccak diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 878920afdb0..45ecf778496 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -340,7 +340,8 @@ template <> UltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, size_t size_hint, WitnessVector const& witness, - bool honk_recursion) + bool honk_recursion, + [[maybe_unused]] std::shared_ptr) { Builder builder{ size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, constraint_system.recursive @@ -365,10 +366,10 @@ template <> MegaCircuitBuilder create_circuit(const AcirFormat& constraint_system, [[maybe_unused]] size_t size_hint, WitnessVector const& witness, - bool honk_recursion) + bool honk_recursion, + std::shared_ptr op_queue) { // Construct a builder using the witness and public input data from acir and with the goblin-owned op_queue - auto op_queue = std::make_shared(); // instantiate empty op_queue auto builder = MegaCircuitBuilder{ op_queue, witness, constraint_system.public_inputs, constraint_system.varnum }; // Populate constraints in the builder via the data in constraint_system diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index d749fe001f2..8c876d064a1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -119,6 +119,11 @@ struct AcirProgramStack { std::vector constraint_systems; WitnessVectorStack witness_stack; + AcirProgramStack(std::vector& constraint_systems_in, WitnessVectorStack& witness_stack_in) + : constraint_systems(constraint_systems_in) + , witness_stack(witness_stack_in) + {} + size_t size() const { return witness_stack.size(); } bool empty() const { return witness_stack.empty(); } @@ -138,7 +143,8 @@ template Builder create_circuit(const AcirFormat& constraint_system, size_t size_hint = 0, WitnessVector const& witness = {}, - bool honk_recursion = false); + bool honk_recursion = false, + std::shared_ptr op_queue = std::make_shared()); template void build_constraints(Builder& builder, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp index e410fded195..33045c675cd 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp @@ -1,3 +1,4 @@ +#include "barretenberg/client_ivc/client_ivc.hpp" #ifndef __wasm__ #include "barretenberg/bb/exec_pipe.hpp" #include "barretenberg/common/streams.hpp" @@ -88,16 +89,49 @@ class AcirIntegrationTest : public ::testing::Test { auto verifier = composer.create_verifier(builder); return verifier.verify_proof(proof); } -}; -class AcirIntegrationSingleTest : public AcirIntegrationTest, public testing::WithParamInterface { + void add_some_simple_RAM_gates(auto& circuit) + { + std::array ram_values{ circuit.add_variable(5), + circuit.add_variable(10), + circuit.add_variable(20) }; + + size_t ram_id = circuit.create_RAM_array(3); + + for (size_t i = 0; i < 3; ++i) { + circuit.init_RAM_element(ram_id, i, ram_values[i]); + } + + auto val_idx_1 = circuit.read_RAM_array(ram_id, circuit.add_variable(1)); + auto val_idx_2 = circuit.read_RAM_array(ram_id, circuit.add_variable(2)); + auto val_idx_3 = circuit.read_RAM_array(ram_id, circuit.add_variable(0)); + + circuit.create_big_add_gate({ + val_idx_1, + val_idx_2, + val_idx_3, + circuit.zero_idx, + 1, + 1, + 1, + 0, + -35, + }); + } + protected: static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); } }; +class AcirIntegrationSingleTest : public AcirIntegrationTest, public testing::WithParamInterface {}; + class AcirIntegrationFoldingTest : public AcirIntegrationTest, public testing::WithParamInterface { protected: - static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); } + static void SetUpTestSuite() + { + srs::init_crs_factory("../srs_db/ignition"); + srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } }; TEST_P(AcirIntegrationSingleTest, DISABLED_ProveAndVerifyProgram) @@ -350,14 +384,72 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_ProveAndVerifyProgramStack) } } +TEST_P(AcirIntegrationFoldingTest, DISABLED_FoldAndVerifyProgramStack) +{ + using Flavor = MegaFlavor; + using Builder = Flavor::CircuitBuilder; + + std::string test_name = GetParam(); + auto program_stack = get_program_stack_data_from_test_file(test_name); + + ClientIVC ivc; + ivc.structured_flag = true; + + while (!program_stack.empty()) { + auto program = program_stack.back(); + + // Construct a bberg circuit from the acir representation + auto circuit = + acir_format::create_circuit(program.constraints, 0, program.witness, false, ivc.goblin.op_queue); + + ivc.accumulate(circuit); + + CircuitChecker::check(circuit); + // EXPECT_TRUE(prove_and_verify_honk(ivc.prover_instance)); + + program_stack.pop_back(); + } + + EXPECT_TRUE(ivc.prove_and_verify()); +} + INSTANTIATE_TEST_SUITE_P(AcirTests, AcirIntegrationFoldingTest, - testing::Values("fold_after_inlined_calls", - "fold_basic", - "fold_basic_nested_call", - "fold_call_witness_condition", - "fold_complex_outputs", - "fold_distinct_return", - "fold_fibonacci", - "fold_numeric_generic_poseidon")); -#endif \ No newline at end of file + testing::Values("fold_basic", "fold_basic_nested_call")); + +/** + * @brief Ensure that adding gates post-facto to a circuit generated from acir still results in a valid circuit + * @details This is a pattern required by e.g. ClientIvc which appends recursive verifiers to acir-generated circuits + * + */ +TEST_F(AcirIntegrationTest, DISABLED_UpdateAcirCircuit) +{ + using Flavor = MegaFlavor; + using Builder = Flavor::CircuitBuilder; + + std::string test_name = "6_array"; // arbitrary program with RAM gates + auto acir_program = get_program_data_from_test_file(test_name); + + // Construct a bberg circuit from the acir representation + auto circuit = acir_format::create_circuit(acir_program.constraints, 0, acir_program.witness); + + EXPECT_TRUE(CircuitChecker::check(circuit)); + + // Now append some RAM gates onto the circuit generated from acir and confirm that its still valid. (First, check + // that the RAM operations constitute a valid independent circuit). + { + Builder circuit; + add_some_simple_RAM_gates(circuit); + EXPECT_TRUE(CircuitChecker::check(circuit)); + EXPECT_TRUE(prove_and_verify_honk(circuit)); + } + + // Now manually append the simple RAM circuit to the circuit generated from acir + add_some_simple_RAM_gates(circuit); + + // Confirm that the result is still valid + EXPECT_TRUE(CircuitChecker::check(circuit)); + EXPECT_TRUE(prove_and_verify_honk(circuit)); +} + +#endif diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index 493baa2f515..eebb97dfc20 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -10,7 +10,7 @@ AcirFormat circuit_buf_to_acir_format(std::vector const& buf); * @brief Converts from the ACIR-native `WitnessMap` format to Barretenberg's internal `WitnessVector` format. * * @param buf Serialized representation of a `WitnessMap`. - * @return A `WitnessVector` equivalent to the passed `WitnessMap`.xo + * @return A `WitnessVector` equivalent to the passed `WitnessMap`. * @note This transformation results in all unassigned witnesses within the `WitnessMap` being assigned the value 0. * Converting the `WitnessVector` back to a `WitnessMap` is unlikely to return the exact same `WitnessMap`. */ 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 6d29b4452c9..b0fe2da200e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -1,6 +1,7 @@ #include "c_bind.hpp" #include "../acir_format/acir_to_constraint_buf.hpp" #include "acir_composer.hpp" +#include "barretenberg/client_ivc/client_ivc.hpp" #include "barretenberg/common/mem.hpp" #include "barretenberg/common/net.hpp" #include "barretenberg/common/serialize.hpp" @@ -78,6 +79,33 @@ WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* acir_vec, uint8 *result = verifier.verify_proof(proof); } +WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uint8_t const* witness_vec, bool* result) +{ + using ProgramStack = acir_format::AcirProgramStack; + using Builder = MegaCircuitBuilder; + + auto constraint_systems = acir_format::program_buf_to_acir_format(from_buffer>(acir_vec)); + auto witness_stack = acir_format::witness_buf_to_witness_stack(from_buffer>(witness_vec)); + + ProgramStack program_stack{ constraint_systems, witness_stack }; + + ClientIVC ivc; + ivc.structured_flag = true; + + 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, 0, stack_item.witness, false, ivc.goblin.op_queue); + + ivc.accumulate(builder); + + program_stack.pop_back(); + } + *result = ivc.prove_and_verify(); +} + WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, bool* result) { auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); 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 52c19bbae73..78f6eae51e7 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -49,6 +49,14 @@ WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* constraint_syste uint8_t const* witness_buf, bool* result); +/** + * @brief Fold and verify a set of circuits using ClientIvc + * + */ +WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* constraint_system_buf, + uint8_t const* witness_buf, + bool* result); + /** * @brief Construct a full goblin proof * @details Makes a call to accumulate to a final circuit before constructing a Goblin proof diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp index 95f0ca86435..9642799f01a 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -148,6 +148,15 @@ class Goblin { [[maybe_unused]] auto pairing_points = merge_verifier.verify_proof(merge_proof); } + // TODO(https://github.com/AztecProtocol/barretenberg/issues/993): Some circuits (particularly on the first call + // to accumulate) may not have any goblin ecc ops prior to the call to merge(), so the commitment to the new + // contribution (C_t_shift) in the merge prover will be the point at infinity. (Note: Some dummy ops are added + // in 'add_gates_to_ensure...' but not until instance construction which comes later). See issue for ideas about + // how to resolve. + if (circuit_builder.blocks.ecc_op.size() == 0) { + MockCircuits::construct_goblin_ecc_op_circuit(circuit_builder); // Add some arbitrary goblin ECC ops + } + // Construct and store the merge proof to be recursively verified on the next call to accumulate MergeProver merge_prover{ circuit_builder.op_queue }; merge_proof = merge_prover.construct_proof(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp index b49b7f79d0e..02fbb08ff15 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover_impl.hpp @@ -201,6 +201,10 @@ template FoldingResult ProtoGalaxyProver_::fold_instances() { BB_OP_COUNT_TIME_NAME("ProtogalaxyProver::fold_instances"); + // Ensure instances are all of the same size + for (size_t idx = 0; idx < ProverInstances::NUM - 1; ++idx) { + ASSERT(instances[idx]->proving_key.circuit_size == instances[idx + 1]->proving_key.circuit_size); + } preparation_round(); perturbator_round(); combiner_quotient_round(); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp index d5831859b43..4ff576e171a 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp @@ -43,6 +43,7 @@ template HonkProof MergeProver_::construct_proof() auto T_prev = op_queue->get_previous_aggregate_transcript(); // TODO(#723): Cannot currently support an empty T_{i-1}. Need to be able to properly handle zero commitment. ASSERT(T_prev[0].size() > 0); + ASSERT(T_current[0].size() > T_prev[0].size()); // Must have some new ops to accumulate otherwise C_t_shift = 0 // Construct t_i^{shift} as T_i - T_{i-1} std::array t_shift; diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 74ea1a057bf..080b6c0ca4c 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -654,6 +654,26 @@ ], "isAsync": false }, + { + "functionName": "acir_fold_and_verify_program_stack", + "inArgs": [ + { + "name": "constraint_system_buf", + "type": "const uint8_t *" + }, + { + "name": "witness_buf", + "type": "const uint8_t *" + } + ], + "outArgs": [ + { + "name": "result", + "type": "bool *" + } + ], + "isAsync": false + }, { "functionName": "acir_goblin_prove", "inArgs": [ diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index 30b143eb7e0..619c89b8f21 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -440,6 +440,18 @@ export class BarretenbergApi { return out[0]; } + async acirFoldAndVerifyProgramStack(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): Promise { + const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = await this.wasm.callWasmExport( + 'acir_fold_and_verify_program_stack', + 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, @@ -1052,6 +1064,18 @@ export class BarretenbergApiSync { return out[0]; } + acirFoldAndVerifyProgramStack(constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array): boolean { + const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable); + const outTypes: OutputType[] = [BoolDeserializer()]; + const result = this.wasm.callWasmExport( + 'acir_fold_and_verify_program_stack', + 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()]; diff --git a/barretenberg/ts/src/main.ts b/barretenberg/ts/src/main.ts index 722fa1b428f..ada83203b08 100755 --- a/barretenberg/ts/src/main.ts +++ b/barretenberg/ts/src/main.ts @@ -166,6 +166,21 @@ export async function proveAndVerifyMegaHonk(bytecodePath: string, witnessPath: /* eslint-enable camelcase */ } +export async function foldAndVerifyProgram(bytecodePath: string, witnessPath: string, crsPath: string) { + /* eslint-disable camelcase */ + const { api } = await init(bytecodePath, crsPath); + try { + const bytecode = getBytecode(bytecodePath); + const witness = getWitness(witnessPath); + + const verified = await api.acirFoldAndVerifyProgramStack(bytecode, witness); + 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()); @@ -492,6 +507,17 @@ program process.exit(result ? 0 : 1); }); +program + .command('fold_and_verify_program') + .description('Accumulate a set of circuits using ClientIvc then verify. Process exits with success or failure code.') + .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') + .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') + .action(async ({ bytecodePath, witnessPath, crsPath }) => { + handleGlobalOptions(); + const result = await foldAndVerifyProgram(bytecodePath, witnessPath, crsPath); + process.exit(result ? 0 : 1); + }); + program .command('prove_and_verify_goblin') .description('Generate a Goblin proof and verify it. Process exits with success or failure code.')