diff --git a/barretenberg/acir_tests/browser-test-app/src/index.ts b/barretenberg/acir_tests/browser-test-app/src/index.ts index 56708214eeb..45b39346ddf 100644 --- a/barretenberg/acir_tests/browser-test-app/src/index.ts +++ b/barretenberg/acir_tests/browser-test-app/src/index.ts @@ -31,10 +31,9 @@ async function runTest( acirComposer, bytecode, witness, - true ); debug(`verifying...`); - const verified = await api.acirVerifyProof(acirComposer, proof, true); + const verified = await api.acirVerifyProof(acirComposer, proof); debug(`verified: ${verified}`); await api.destroy(); diff --git a/barretenberg/acir_tests/gen_inner_proof_inputs.sh b/barretenberg/acir_tests/gen_inner_proof_inputs.sh index 36137bde82e..ade57bcea4f 100755 --- a/barretenberg/acir_tests/gen_inner_proof_inputs.sh +++ b/barretenberg/acir_tests/gen_inner_proof_inputs.sh @@ -20,7 +20,7 @@ export BRANCH ./clone_test_vectors.sh -cd acir_tests/assert_statement +cd acir_tests/assert_statement_recursive PROOF_DIR=$PWD/proofs PROOF_PATH=$PROOF_DIR/$PROOF_NAME diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 6c567b00bcd..b49d6f95908 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -91,7 +91,7 @@ acir_format::AcirFormat get_constraint_system(std::string const& bytecode_path) * @return true if the proof is valid * @return false if the proof is invalid */ -bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessPath, bool recursive) +bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessPath) { auto constraint_system = get_constraint_system(bytecodePath); auto witness = get_witness(witnessPath); @@ -109,14 +109,14 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP write_benchmark("subgroup_size", acir_composer.get_dyadic_circuit_size(), "acir_test", current_dir); Timer proof_timer; - auto proof = acir_composer.create_proof(recursive); + auto proof = acir_composer.create_proof(); write_benchmark("proof_construction_time", proof_timer.milliseconds(), "acir_test", current_dir); Timer vk_timer; acir_composer.init_verification_key(); write_benchmark("vk_construction_time", vk_timer.milliseconds(), "acir_test", current_dir); - auto verified = acir_composer.verify_proof(proof, recursive); + auto verified = acir_composer.verify_proof(proof); vinfo("verified: ", verified); return verified; @@ -172,9 +172,7 @@ bool accumulateAndVerifyGoblin(const std::string& bytecodePath, const std::strin * @return true if the proof is valid * @return false if the proof is invalid */ -bool proveAndVerifyGoblin(const std::string& bytecodePath, - const std::string& witnessPath, - [[maybe_unused]] bool recursive) +bool proveAndVerifyGoblin(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); @@ -212,10 +210,7 @@ bool proveAndVerifyGoblin(const std::string& bytecodePath, * @param recursive Whether to use recursive proof generation of non-recursive * @param outputPath Path to write the proof to */ -void prove(const std::string& bytecodePath, - const std::string& witnessPath, - bool recursive, - const std::string& outputPath) +void prove(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath) { auto constraint_system = get_constraint_system(bytecodePath); auto witness = get_witness(witnessPath); @@ -224,7 +219,7 @@ void prove(const std::string& bytecodePath, acir_composer.create_circuit(constraint_system, witness); init_bn254_crs(acir_composer.get_dyadic_circuit_size()); acir_composer.init_proving_key(); - auto proof = acir_composer.create_proof(recursive); + auto proof = acir_composer.create_proof(); if (outputPath == "-") { writeRawBytesToStdout(proof); @@ -270,12 +265,12 @@ void gateCount(const std::string& bytecodePath) * @return true If the proof is valid * @return false If the proof is invalid */ -bool verify(const std::string& proof_path, bool recursive, const std::string& vk_path) +bool verify(const std::string& proof_path, const std::string& vk_path) { auto acir_composer = verifier_init(); auto vk_data = from_buffer(read_file(vk_path)); acir_composer.load_verification_key(std::move(vk_data)); - auto verified = acir_composer.verify_proof(read_file(proof_path), recursive); + auto verified = acir_composer.verify_proof(read_file(proof_path)); vinfo("verified: ", verified); return verified; @@ -491,7 +486,6 @@ int main(int argc, char* argv[]) std::string vk_path = get_option(args, "-k", "./target/vk"); std::string pk_path = get_option(args, "-r", "./target/pk"); CRS_PATH = get_option(args, "-c", CRS_PATH); - bool recursive = flag_present(args, "-r") || flag_present(args, "--recursive"); // Skip CRS initialization for any command which doesn't require the CRS. if (command == "--version") { @@ -504,21 +498,21 @@ int main(int argc, char* argv[]) return 0; } if (command == "prove_and_verify") { - return proveAndVerify(bytecode_path, witness_path, recursive) ? 0 : 1; + return proveAndVerify(bytecode_path, witness_path) ? 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; + return proveAndVerifyGoblin(bytecode_path, witness_path) ? 0 : 1; } if (command == "prove") { std::string output_path = get_option(args, "-o", "./proofs/proof"); - prove(bytecode_path, witness_path, recursive, output_path); + prove(bytecode_path, witness_path, output_path); } else if (command == "gates") { gateCount(bytecode_path); } else if (command == "verify") { - return verify(proof_path, recursive, vk_path) ? 0 : 1; + return verify(proof_path, vk_path) ? 0 : 1; } else if (command == "contract") { std::string output_path = get_option(args, "-o", "./target/contract.sol"); contract(output_path, vk_path); 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 360d43324e9..dfe00b03703 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -193,7 +193,9 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo template Builder create_circuit(const AcirFormat& constraint_system, size_t size_hint, WitnessVector const& witness) { - Builder builder{ size_hint, witness, constraint_system.public_inputs, constraint_system.varnum }; + Builder builder{ + size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, constraint_system.recursive + }; bool has_valid_witness_assignments = !witness.empty(); build_constraints(builder, constraint_system, has_valid_witness_assignments); 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 f88777038b0..3c053d83814 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -22,6 +22,12 @@ namespace acir_format { struct AcirFormat { // The number of witnesses in the circuit uint32_t varnum; + // Specifies whether a prover that produces SNARK recursion friendly proofs should be used. + // The proof produced when this flag is true should be friendly for recursive verification inside + // of another SNARK. For example, a recursive friendly proof may use Blake3Pedersen for + // hashing in its transcript, while we still want a prove that uses Keccak for its transcript in order + // to be able to verify SNARKs on Ethereum. + bool recursive; std::vector public_inputs; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 3c85f61d4d1..8908b348096 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -29,6 +29,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) AcirFormat constraint_system{ .varnum = 4, + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, @@ -141,6 +142,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) // EXPR [ (-1, _6) 1 ] AcirFormat constraint_system{ .varnum = 6, + .recursive = false, .public_inputs = { 1 }, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, @@ -205,6 +207,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .signature = signature, }; AcirFormat constraint_system{ .varnum = 81, + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = range_constraints, @@ -297,6 +300,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) }; AcirFormat constraint_system{ .varnum = 81, + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = range_constraints, @@ -408,6 +412,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) AcirFormat constraint_system{ .varnum = 36, + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = { range_a, range_b, range_c, range_d }, @@ -451,6 +456,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) }; AcirFormat constraint_system{ .varnum = 51, + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, 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 058feb5e6a9..52dd9935241 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 @@ -340,6 +340,7 @@ AcirFormat circuit_buf_to_acir_format(std::vector const& buf) AcirFormat af; // `varnum` is the true number of variables, thus we add one to the index which starts at zero af.varnum = circuit.current_witness_index + 1; + af.recursive = circuit.recursive; af.public_inputs = join({ map(circuit.public_parameters.value, [](auto e) { return e.value; }), map(circuit.return_values.value, [](auto e) { return e.value; }) }); std::map block_id_to_block_constraint; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index e8c4b22b4bf..77717980a4e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -48,6 +48,7 @@ TEST_F(BigIntTests, TestBigIntConstraintDummy) AcirFormat constraint_system{ .varnum = 4, + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 384520e7f80..a88e89de566 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -110,6 +110,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) size_t num_variables = generate_block_constraint(block, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index 11ea6a77b9d..d64110d938d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -49,6 +49,7 @@ TEST_F(EcOperations, TestECOperations) AcirFormat constraint_system{ .varnum = static_cast(num_variables + 1), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index fb3e405e2f1..ba6c67b2162 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -89,6 +89,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) size_t num_variables = generate_ecdsa_constraint(ecdsa_k1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, @@ -134,6 +135,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) size_t num_variables = generate_ecdsa_constraint(ecdsa_k1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, @@ -174,6 +176,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index fe64491c90c..ee9f4e2faff 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -124,6 +124,7 @@ TEST(ECDSASecp256r1, test_hardcoded) AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, @@ -170,6 +171,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) size_t num_variables = generate_ecdsa_constraint(ecdsa_r1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, @@ -214,6 +216,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) size_t num_variables = generate_ecdsa_constraint(ecdsa_r1_constraint, witness_values); AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, @@ -253,6 +256,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) AcirFormat constraint_system{ .varnum = static_cast(num_variables), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index f281f965eea..cc8fde631b4 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -82,6 +82,7 @@ Builder create_inner_circuit() }; AcirFormat constraint_system{ .varnum = 6, + .recursive = true, .public_inputs = { 1, 2 }, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, @@ -235,6 +236,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) } AcirFormat constraint_system{ .varnum = static_cast(witness.size()), + .recursive = false, .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index c983baf68b0..02adc0e63e6 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -1118,6 +1118,7 @@ struct Circuit { PublicInputs public_parameters; PublicInputs return_values; std::vector> assert_messages; + bool recursive; friend bool operator==(const Circuit&, const Circuit&); std::vector bincodeSerialize() const; @@ -5938,6 +5939,9 @@ inline bool operator==(const Circuit& lhs, const Circuit& rhs) if (!(lhs.assert_messages == rhs.assert_messages)) { return false; } + if (!(lhs.recursive == rhs.recursive)) { + return false; + } return true; } @@ -5971,6 +5975,7 @@ void serde::Serializable::serialize(const Circuit::Circuit& ob serde::Serializable::serialize(obj.public_parameters, serializer); serde::Serializable::serialize(obj.return_values, serializer); serde::Serializable::serialize(obj.assert_messages, serializer); + serde::Serializable::serialize(obj.recursive, serializer); serializer.decrease_container_depth(); } @@ -5986,6 +5991,7 @@ Circuit::Circuit serde::Deserializable::deserialize(Deserializ obj.public_parameters = serde::Deserializable::deserialize(deserializer); obj.return_values = serde::Deserializable::deserialize(deserializer); obj.assert_messages = serde::Deserializable::deserialize(deserializer); + obj.recursive = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } 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 072540db80f..c7fae9f8c19 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp @@ -8,6 +8,7 @@ #include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" #include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders_fwd.hpp" #include "contract.hpp" +#include namespace acir_proofs { @@ -29,6 +30,7 @@ void AcirComposer::create_circuit(acir_format::AcirFormat& constraint_system, Wi vinfo("building circuit..."); builder_ = acir_format::create_circuit(constraint_system, size_hint_, witness); vinfo("gates: ", builder_.get_total_circuit_size()); + vinfo("circuit is recursive friendly: ", builder_.is_recursive_circuit); } std::shared_ptr AcirComposer::init_proving_key() @@ -39,7 +41,7 @@ std::shared_ptr AcirComposer::init_proving_key() return proving_key_; } -std::vector AcirComposer::create_proof(bool is_recursive) +std::vector AcirComposer::create_proof() { if (!proving_key_) { throw_or_abort("Must compute proving key before constructing proof."); @@ -49,7 +51,7 @@ std::vector AcirComposer::create_proof(bool is_recursive) vinfo("creating proof..."); std::vector proof; - if (is_recursive) { + if (builder_.is_recursive_circuit) { auto prover = composer.create_prover(builder_); proof = prover.construct_proof().proof_data; } else { @@ -68,6 +70,7 @@ std::shared_ptr AcirComposer::init_verification_key vinfo("computing verification key..."); acir_format::Composer composer(proving_key_, nullptr); verification_key_ = composer.compute_verification_key(builder_); + vinfo("done."); return verification_key_; } @@ -78,7 +81,7 @@ void AcirComposer::load_verification_key(bb::plonk::verification_key_data&& data std::make_shared(std::move(data), srs::get_crs_factory()->get_verifier_crs()); } -bool AcirComposer::verify_proof(std::vector const& proof, bool is_recursive) +bool AcirComposer::verify_proof(std::vector const& proof) { acir_format::Composer composer(proving_key_, verification_key_); @@ -91,17 +94,7 @@ bool AcirComposer::verify_proof(std::vector const& proof, bool is_recur // Hack. Shouldn't need to do this. 2144 is size with no public inputs. builder_.public_inputs.resize((proof.size() - 2144) / 32); - // TODO: We could get rid of this, if we made the Noir program specify whether something should be - // TODO: created with the recursive setting or not. ie: - // - // #[recursive_friendly] - // fn main() {} - // would put in the ACIR that we want this to be recursion friendly with a flag maybe and the backend - // would set the is_recursive flag to be true. - // This would eliminate the need for nargo to have a --recursive flag - // - // End result is that we may just be able to get it off of builder_, like builder_.is_recursive_friendly - if (is_recursive) { + if (verification_key_->is_recursive_circuit) { auto verifier = composer.create_verifier(builder_); return verifier.verify_proof({ proof }); } else { 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 a83d7f85c95..9ff9b51ace3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.hpp @@ -21,13 +21,13 @@ class AcirComposer { std::shared_ptr init_proving_key(); - std::vector create_proof(bool is_recursive); + std::vector create_proof(); void load_verification_key(bb::plonk::verification_key_data&& data); std::shared_ptr init_verification_key(); - bool verify_proof(std::vector const& proof, bool is_recursive); + bool verify_proof(std::vector const& proof); std::string get_solidity_verifier(); size_t get_total_circuit_size() { return builder_.get_total_circuit_size(); }; 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 6572e08a6a2..7fc9eff28f3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -49,7 +49,6 @@ WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t const* witness_vec, - bool const* is_recursive, uint8_t** out) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); @@ -59,8 +58,8 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, acir_composer->create_circuit(constraint_system, witness); acir_composer->init_proving_key(); - auto proof = acir_composer->create_proof(*is_recursive); - *out = to_heap_buffer(proof); + auto proof_data = acir_composer->create_proof(); + *out = to_heap_buffer(proof_data); } WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr, @@ -144,14 +143,11 @@ WASM_EXPORT void acir_goblin_verify(in_ptr acir_composer_ptr, uint8_t const* pro *result = acir_composer->verify(proof); } -WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, - uint8_t const* proof_buf, - bool const* is_recursive, - bool* result) +WASM_EXPORT void acir_verify_proof(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_proof(proof, *is_recursive); + *result = acir_composer->verify_proof(proof); } 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/c_bind.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 76b24886b1d..4dfc3259947 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -31,7 +31,6 @@ WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, uint8_t const* constraint_system_buf, uint8_t const* witness_buf, - bool const* is_recursive, uint8_t** out); /** @@ -62,10 +61,7 @@ WASM_EXPORT void acir_get_verification_key(in_ptr acir_composer_ptr, uint8_t** o WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t** out); -WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, - uint8_t const* proof_buf, - bool const* is_recursive, - bool* result); +WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result); /** * @brief Verifies a GUH proof produced during goblin accumulation diff --git a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp index d293f509d4f..ad3aa5fd260 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/composer/ultra_composer.cpp @@ -497,6 +497,8 @@ std::shared_ptr UltraComposer::compute_verification_key circuit_verification_key->contains_recursive_proof = circuit_constructor.contains_recursive_proof; + circuit_verification_key->is_recursive_circuit = circuit_constructor.is_recursive_circuit; + return circuit_verification_key; } diff --git a/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index 2b7ee9fa75e..0022b3de741 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -92,6 +92,7 @@ verification_key::verification_key(verification_key_data&& data, , polynomial_manifest(static_cast(data.circuit_type)) , contains_recursive_proof(data.contains_recursive_proof) , recursive_proof_public_input_indices(std::move(data.recursive_proof_public_input_indices)) + , is_recursive_circuit(data.is_recursive_circuit) {} verification_key::verification_key(const verification_key& other) diff --git a/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp b/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp index 5bf59acd4d2..c09c8d32558 100644 --- a/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.hpp @@ -18,6 +18,7 @@ struct verification_key_data { std::map commitments; bool contains_recursive_proof = false; std::vector recursive_proof_public_input_indices; + bool is_recursive_circuit = false; // for serialization: update with any new fields MSGPACK_FIELDS(circuit_type, @@ -25,7 +26,8 @@ struct verification_key_data { num_public_inputs, commitments, contains_recursive_proof, - recursive_proof_public_input_indices); + recursive_proof_public_input_indices, + is_recursive_circuit); [[nodiscard]] bb::fr hash_native(size_t hash_index = 0) const; }; @@ -36,7 +38,8 @@ inline std::ostream& operator<<(std::ostream& os, verification_key_data const& k << "key.num_public_inputs: " << static_cast(key.num_public_inputs) << "\n" << "key.commitments: " << key.commitments << "\n" << "key.contains_recursive_proof: " << key.contains_recursive_proof << "\n" - << "key.recursive_proof_public_input_indices: " << key.recursive_proof_public_input_indices << "\n"; + << "key.recursive_proof_public_input_indices: " << key.recursive_proof_public_input_indices << "\n" + << "key.is_recursive_circuit: " << key.is_recursive_circuit << "\n"; }; inline bool operator==(verification_key_data const& lhs, verification_key_data const& rhs) @@ -72,6 +75,7 @@ struct verification_key { .commitments = commitments, .contains_recursive_proof = contains_recursive_proof, .recursive_proof_public_input_indices = recursive_proof_public_input_indices, + .is_recursive_circuit = is_recursive_circuit, }; } @@ -94,17 +98,23 @@ struct verification_key { bool contains_recursive_proof = false; std::vector recursive_proof_public_input_indices; + + bool is_recursive_circuit = false; + size_t program_width = 3; // for serialization: update with new fields void msgpack_pack(auto& packer) const { - verification_key_data data = { static_cast(circuit_type), - static_cast(circuit_size), - static_cast(num_public_inputs), - commitments, - contains_recursive_proof, - recursive_proof_public_input_indices }; + verification_key_data data = { + static_cast(circuit_type), + static_cast(circuit_size), + static_cast(num_public_inputs), + commitments, + contains_recursive_proof, + recursive_proof_public_input_indices, + is_recursive_circuit, + }; packer.pack(data); } void msgpack_unpack(auto obj) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp index ffc8e1dce5d..5231f1cad23 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp @@ -35,10 +35,17 @@ template class CircuitBuilderBase { // DOCTODO(#231): replace with the relevant wiki link. std::map tau; - // Publicin put indices which contain recursive proof information + // Public input indices which contain recursive proof information std::vector recursive_proof_public_input_indices; bool contains_recursive_proof = false; + // We only know from the circuit description whether a circuit should use a prover which produces + // proofs that are friendly to verify in a circuit themselves. However, a verifier does not need a full circuit + // description and should be able to verify a proof with just the verification key and the proof. + // This field exists to later set the same field in the verification key, and make sure + // that we are using the correct prover/verifier. + bool is_recursive_circuit = false; + bool _failed = false; std::string _err; static constexpr uint32_t REAL_VARIABLE = UINT32_MAX - 1; diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp index 2ef60b71cc3..4ee4751b403 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp @@ -673,7 +673,8 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase& public_inputs, - size_t varnum) + size_t varnum, + bool recursive = false) : CircuitBuilderBase(size_hint) { selectors.reserve(size_hint); @@ -696,6 +697,8 @@ class UltraCircuitBuilder_ : public CircuitBuilderBasezero_idx = put_constant_variable(FF::zero()); this->tau.insert({ DUMMY_TAG, DUMMY_TAG }); // TODO(luke): explain this + + this->is_recursive_circuit = recursive; }; UltraCircuitBuilder_(const UltraCircuitBuilder_& other) = default; UltraCircuitBuilder_(UltraCircuitBuilder_&& other) diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 3925ff1fa54..f04b2871163 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -536,10 +536,6 @@ { "name": "witness_buf", "type": "const uint8_t *" - }, - { - "name": "is_recursive", - "type": "const bool *" } ], "outArgs": [ @@ -670,10 +666,6 @@ { "name": "proof_buf", "type": "const uint8_t *" - }, - { - "name": "is_recursive", - "type": "const bool *" } ], "outArgs": [ diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index d6280851562..1a337c33e2a 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -356,9 +356,8 @@ export class BarretenbergApi { acirComposerPtr: Ptr, constraintSystemBuf: Uint8Array, witnessBuf: Uint8Array, - isRecursive: boolean, ): Promise { - const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf, isRecursive].map(serializeBufferable); + const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf].map(serializeBufferable); const outTypes: OutputType[] = [BufferDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_create_proof', @@ -449,8 +448,8 @@ export class BarretenbergApi { return out[0]; } - async acirVerifyProof(acirComposerPtr: Ptr, proofBuf: Uint8Array, isRecursive: boolean): Promise { - const inArgs = [acirComposerPtr, proofBuf, isRecursive].map(serializeBufferable); + async acirVerifyProof(acirComposerPtr: Ptr, proofBuf: Uint8Array): Promise { + const inArgs = [acirComposerPtr, proofBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_verify_proof', @@ -865,13 +864,8 @@ export class BarretenbergApiSync { return; } - acirCreateProof( - acirComposerPtr: Ptr, - constraintSystemBuf: Uint8Array, - witnessBuf: Uint8Array, - isRecursive: boolean, - ): Uint8Array { - const inArgs = [acirComposerPtr, constraintSystemBuf, witnessBuf, isRecursive].map(serializeBufferable); + acirCreateProof(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_proof', @@ -954,8 +948,8 @@ export class BarretenbergApiSync { return out[0]; } - acirVerifyProof(acirComposerPtr: Ptr, proofBuf: Uint8Array, isRecursive: boolean): boolean { - const inArgs = [acirComposerPtr, proofBuf, isRecursive].map(serializeBufferable); + acirVerifyProof(acirComposerPtr: Ptr, proofBuf: Uint8Array): boolean { + const inArgs = [acirComposerPtr, proofBuf].map(serializeBufferable); const outTypes: OutputType[] = [BoolDeserializer()]; const result = this.wasm.callWasmExport( 'acir_verify_proof', diff --git a/barretenberg/ts/src/main.ts b/barretenberg/ts/src/main.ts index d06de96431d..ec974fd1c35 100755 --- a/barretenberg/ts/src/main.ts +++ b/barretenberg/ts/src/main.ts @@ -99,7 +99,7 @@ async function initLite() { return { api, acirComposer }; } -export async function proveAndVerify(bytecodePath: string, witnessPath: string, crsPath: string, isRecursive: boolean) { +export async function proveAndVerify(bytecodePath: string, witnessPath: string, crsPath: string) { /* eslint-disable camelcase */ const acir_test = path.basename(process.cwd()); @@ -116,11 +116,11 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string, writeBenchmark('subgroup_size', subgroupSize, { acir_test, threads }); const proofTimer = new Timer(); - const proof = await api.acirCreateProof(acirComposer, bytecode, witness, isRecursive); + const proof = await api.acirCreateProof(acirComposer, bytecode, witness); writeBenchmark('proof_construction_time', proofTimer.ms(), { acir_test, threads }); debug(`verifying...`); - const verified = await api.acirVerifyProof(acirComposer, proof, isRecursive); + const verified = await api.acirVerifyProof(acirComposer, proof); debug(`verified: ${verified}`); return verified; } finally { @@ -186,19 +186,13 @@ export async function proveAndVerifyGoblin(bytecodePath: string, witnessPath: st /* eslint-enable camelcase */ } -export async function prove( - bytecodePath: string, - witnessPath: string, - crsPath: string, - isRecursive: boolean, - outputPath: string, -) { +export async function prove(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) { const { api, acirComposer } = await init(bytecodePath, crsPath); try { debug(`creating proof...`); const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); - const proof = await api.acirCreateProof(acirComposer, bytecode, witness, isRecursive); + const proof = await api.acirCreateProof(acirComposer, bytecode, witness); debug(`done.`); if (outputPath === '-') { @@ -241,11 +235,11 @@ export function acvmInfo(outputPath: string) { } } -export async function verify(proofPath: string, isRecursive: boolean, vkPath: string) { +export async function verify(proofPath: string, vkPath: string) { const { api, acirComposer } = await initLite(); try { await api.acirLoadVerificationKey(acirComposer, new RawBuffer(readFileSync(vkPath))); - const verified = await api.acirVerifyProof(acirComposer, readFileSync(proofPath), isRecursive); + const verified = await api.acirVerifyProof(acirComposer, readFileSync(proofPath)); debug(`verified: ${verified}`); return verified; } finally { @@ -379,10 +373,9 @@ program .description('Generate a 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') - .option('-r, --recursive', 'prove and verify using recursive prover and verifier', false) - .action(async ({ bytecodePath, witnessPath, recursive, crsPath }) => { + .action(async ({ bytecodePath, witnessPath, crsPath }) => { handleGlobalOptions(); - const result = await proveAndVerify(bytecodePath, witnessPath, crsPath, recursive); + const result = await proveAndVerify(bytecodePath, witnessPath, crsPath); process.exit(result ? 0 : 1); }); @@ -413,11 +406,10 @@ program .description('Generate a proof and write it to a file.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/acir.gz') .option('-w, --witness-path ', 'Specify the witness path', './target/witness.gz') - .option('-r, --recursive', 'prove using recursive prover', false) .option('-o, --output-path ', 'Specify the proof output path', './proofs/proof') - .action(async ({ bytecodePath, witnessPath, recursive, outputPath, crsPath }) => { + .action(async ({ bytecodePath, witnessPath, outputPath, crsPath }) => { handleGlobalOptions(); - await prove(bytecodePath, witnessPath, crsPath, recursive, outputPath); + await prove(bytecodePath, witnessPath, crsPath, outputPath); }); program @@ -433,11 +425,10 @@ program .command('verify') .description('Verify a proof. Process exists with success or failure code.') .requiredOption('-p, --proof-path ', 'Specify the path to the proof') - .option('-r, --recursive', 'prove using recursive prover', false) .requiredOption('-k, --vk ', 'path to a verification key. avoids recomputation.') - .action(async ({ proofPath, recursive, vk }) => { + .action(async ({ proofPath, vk }) => { handleGlobalOptions(); - const result = await verify(proofPath, recursive, vk); + const result = await verify(proofPath, vk); process.exit(result ? 0 : 1); }); diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 1b9b9c2e5bd..bdee08794e6 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -1057,6 +1057,7 @@ namespace Circuit { Circuit::PublicInputs public_parameters; Circuit::PublicInputs return_values; std::vector> assert_messages; + bool recursive; friend bool operator==(const Circuit&, const Circuit&); std::vector bincodeSerialize() const; @@ -4889,6 +4890,7 @@ namespace Circuit { if (!(lhs.public_parameters == rhs.public_parameters)) { return false; } if (!(lhs.return_values == rhs.return_values)) { return false; } if (!(lhs.assert_messages == rhs.assert_messages)) { return false; } + if (!(lhs.recursive == rhs.recursive)) { return false; } return true; } @@ -4919,6 +4921,7 @@ void serde::Serializable::serialize(const Circuit::Circuit &ob serde::Serializable::serialize(obj.public_parameters, serializer); serde::Serializable::serialize(obj.return_values, serializer); serde::Serializable::serialize(obj.assert_messages, serializer); + serde::Serializable::serialize(obj.recursive, serializer); serializer.decrease_container_depth(); } @@ -4933,6 +4936,7 @@ Circuit::Circuit serde::Deserializable::deserialize(Deserializ obj.public_parameters = serde::Deserializable::deserialize(deserializer); obj.return_values = serde::Deserializable::deserialize(deserializer); obj.assert_messages = serde::Deserializable::deserialize(deserializer); + obj.recursive = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } diff --git a/noir/acvm-repo/acir/src/circuit/mod.rs b/noir/acvm-repo/acir/src/circuit/mod.rs index b248b30b1d9..ccfb19bbf05 100644 --- a/noir/acvm-repo/acir/src/circuit/mod.rs +++ b/noir/acvm-repo/acir/src/circuit/mod.rs @@ -39,6 +39,11 @@ pub struct Circuit { // c++ code at the moment when it is, due to OpcodeLocation needing a comparison // implementation which is never generated. pub assert_messages: Vec<(OpcodeLocation, String)>, + + /// States whether the backend should use a SNARK recursion friendly prover. + /// If implemented by a backend, this means that proofs generated with this circuit + /// will be friendly for recursively verifying inside of another SNARK. + pub recursive: bool, } impl Circuit { @@ -318,6 +323,7 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(12)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(4), Witness(12)])), assert_messages: Default::default(), + recursive: false, }; fn read_write(circuit: Circuit) -> (Circuit, Circuit) { @@ -348,6 +354,7 @@ mod tests { public_parameters: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2)])), assert_messages: Default::default(), + recursive: false, }; let json = serde_json::to_string_pretty(&circuit).unwrap(); diff --git a/noir/compiler/integration-tests/test/browser/compile_prove_verify.test.ts b/noir/compiler/integration-tests/test/browser/compile_prove_verify.test.ts index 0a829def09e..dba51895bb8 100644 --- a/noir/compiler/integration-tests/test/browser/compile_prove_verify.test.ts +++ b/noir/compiler/integration-tests/test/browser/compile_prove_verify.test.ts @@ -59,11 +59,11 @@ test_cases.forEach((testInfo) => { // JS Proving - const proofWithPublicInputs = await program.generateFinalProof(inputs); + const proofWithPublicInputs = await program.generateProof(inputs); // JS verification - const verified = await program.verifyFinalProof(proofWithPublicInputs); + const verified = await program.verifyProof(proofWithPublicInputs); expect(verified, 'Proof fails verification in JS').to.be.true; }); diff --git a/noir/compiler/integration-tests/test/browser/recursion.test.ts b/noir/compiler/integration-tests/test/browser/recursion.test.ts index 80199de5701..a8927aa6a75 100644 --- a/noir/compiler/integration-tests/test/browser/recursion.test.ts +++ b/noir/compiler/integration-tests/test/browser/recursion.test.ts @@ -19,7 +19,7 @@ await newABICoder(); await initACVM(); const base_relative_path = '../../../../..'; -const circuit_main = 'test_programs/execution_success/assert_statement'; +const circuit_main = 'test_programs/execution_success/assert_statement_recursive'; const circuit_recursion = 'compiler/integration-tests/circuits/recursion'; async function getCircuit(projectPath: string) { @@ -48,15 +48,15 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const { witness: main_witnessUint8Array } = await new Noir(main_program).execute(main_inputs); - const main_proof = await main_backend.generateIntermediateProof(main_witnessUint8Array); - const main_verification = await main_backend.verifyIntermediateProof(main_proof); + const main_proof = await main_backend.generateProof(main_witnessUint8Array); + const main_verification = await main_backend.verifyProof(main_proof); logger.debug('main_verification', main_verification); expect(main_verification).to.be.true; const numPublicInputs = 1; - const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateIntermediateProofArtifacts( + const { proofAsFields, vkAsFields, vkHash } = await main_backend.generateRecursiveProofArtifacts( main_proof, numPublicInputs, ); @@ -76,20 +76,20 @@ describe('It compiles noir program code, receiving circuit bytes and abi object. const { witness: recursion_witnessUint8Array } = await new Noir(recursion_program).execute(recursion_inputs); - const recursion_proof = await recursion_backend.generateFinalProof(recursion_witnessUint8Array); + const recursion_proof = await recursion_backend.generateProof(recursion_witnessUint8Array); // Causes an "unreachable" error. // Due to the fact that it's a non-recursive proof? // // const recursion_numPublicInputs = 1; - // const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateIntermediateProofArtifacts( + // const { proofAsFields: recursion_proofAsFields } = await recursion_backend.generateRecursiveProofArtifacts( // recursion_proof, // recursion_numPublicInputs, // ); // // logger.debug('recursion_proofAsFields', recursion_proofAsFields); - const recursion_verification = await recursion_backend.verifyFinalProof(recursion_proof); + const recursion_verification = await recursion_backend.verifyProof(recursion_proof); logger.debug('recursion_verification', recursion_verification); diff --git a/noir/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts b/noir/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts index 9cdd80edc15..6147f770f16 100644 --- a/noir/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts +++ b/noir/compiler/integration-tests/test/node/onchain_recursive_verification.test.ts @@ -16,7 +16,7 @@ it(`smart contract can verify a recursive proof`, async () => { const fm = createFileManager(basePath); const innerCompilationResult = await compile( fm, - join(basePath, './test_programs/execution_success/assert_statement'), + join(basePath, './test_programs/execution_success/assert_statement_recursive'), ); if (!('program' in innerCompilationResult)) { throw new Error('Compilation failed'); @@ -38,17 +38,17 @@ it(`smart contract can verify a recursive proof`, async () => { const inner = new Noir(innerProgram); const inner_prover_toml = readFileSync( - join(basePath, `./test_programs/execution_success/assert_statement/Prover.toml`), + join(basePath, `./test_programs/execution_success/assert_statement_recursive/Prover.toml`), ).toString(); const inner_inputs = toml.parse(inner_prover_toml); const { witness: main_witness } = await inner.execute(inner_inputs); - const intermediate_proof = await inner_backend.generateIntermediateProof(main_witness); + const intermediate_proof = await inner_backend.generateProof(main_witness); - expect(await inner_backend.verifyIntermediateProof(intermediate_proof)).to.be.true; + expect(await inner_backend.verifyProof(intermediate_proof)).to.be.true; - const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateIntermediateProofArtifacts( + const { proofAsFields, vkAsFields, vkHash } = await inner_backend.generateRecursiveProofArtifacts( intermediate_proof, 1, // 1 public input ); @@ -65,8 +65,8 @@ it(`smart contract can verify a recursive proof`, async () => { key_hash: vkHash, }; - const recursion_proof = await recursion.generateFinalProof(recursion_inputs); - expect(await recursion.verifyFinalProof(recursion_proof)).to.be.true; + const recursion_proof = await recursion.generateProof(recursion_inputs); + expect(await recursion.verifyProof(recursion_proof)).to.be.true; // Smart contract verification diff --git a/noir/compiler/integration-tests/test/node/smart_contract_verifier.test.ts b/noir/compiler/integration-tests/test/node/smart_contract_verifier.test.ts index d870956ea7a..79a0520da32 100644 --- a/noir/compiler/integration-tests/test/node/smart_contract_verifier.test.ts +++ b/noir/compiler/integration-tests/test/node/smart_contract_verifier.test.ts @@ -46,11 +46,11 @@ test_cases.forEach((testInfo) => { const prover_toml = readFileSync(resolve(`${base_relative_path}/${test_case}/Prover.toml`)).toString(); const inputs = toml.parse(prover_toml); - const proofData = await program.generateFinalProof(inputs); + const proofData = await program.generateProof(inputs); // JS verification - const verified = await program.verifyFinalProof(proofData); + const verified = await program.verifyProof(proofData); expect(verified, 'Proof fails verification in JS').to.be.true; // Smart contract verification diff --git a/noir/compiler/noirc_evaluator/src/ssa.rs b/noir/compiler/noirc_evaluator/src/ssa.rs index 0e3076923e0..108737f1f75 100644 --- a/noir/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/compiler/noirc_evaluator/src/ssa.rs @@ -85,6 +85,7 @@ pub fn create_circuit( enable_brillig_logging: bool, ) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { let func_sig = program.main_function_signature.clone(); + let recursive = program.recursive; let mut generated_acir = optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?; let opcodes = generated_acir.take_opcodes(); @@ -111,6 +112,7 @@ pub fn create_circuit( public_parameters, return_values, assert_messages: assert_messages.into_iter().collect(), + recursive, }; // This converts each im::Vector in the BTreeMap to a Vec diff --git a/noir/compiler/noirc_frontend/src/ast/function.rs b/noir/compiler/noirc_frontend/src/ast/function.rs index f20fc54b101..46f0ac0fa0f 100644 --- a/noir/compiler/noirc_frontend/src/ast/function.rs +++ b/noir/compiler/noirc_frontend/src/ast/function.rs @@ -29,6 +29,7 @@ pub enum FunctionKind { Builtin, Normal, Oracle, + Recursive, } impl NoirFunction { @@ -106,6 +107,7 @@ impl From for NoirFunction { Some(FunctionAttribute::Foreign(_)) => FunctionKind::LowLevel, Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal, Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, + Some(FunctionAttribute::Recursive) => FunctionKind::Recursive, None => FunctionKind::Normal, }; diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs index 7bd4de77e84..0752838c82e 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,8 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, + #[error("#[recursive] attribute is only allowed on entry points to a program")] + MisplacedRecursiveAttribute { ident: Ident }, #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] LowLevelFunctionOutsideOfStdlib { ident: Ident }, } @@ -313,6 +315,18 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), + ResolverError::MisplacedRecursiveAttribute { ident } => { + let name = &ident.0.contents; + + let mut diag = Diagnostic::simple_error( + format!("misplaced #[recursive] attribute on function {name} rather than the main function"), + "misplaced #[recursive] attribute".to_string(), + ident.0.span(), + ); + + diag.add_note("The `#[recursive]` attribute specifies to the backend whether it should use a prover which generates proofs that are friendly for recursive verification in another circuit".to_owned()); + diag + } ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 8243b684c8a..f025f817b09 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -414,7 +414,7 @@ impl<'a> Resolver<'a> { FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => { HirFunction::empty() } - FunctionKind::Normal => { + FunctionKind::Normal | FunctionKind::Recursive => { let expr_id = self.intern_block(func.def.body); self.interner.push_expr_location(expr_id, func.def.span, self.file); HirFunction::unchecked_from_expr(expr_id) @@ -923,6 +923,12 @@ impl<'a> Resolver<'a> { { self.push_err(ResolverError::NecessaryPub { ident: func.name_ident().clone() }); } + // '#[recursive]' attribute is only allowed for entry point functions + if !self.is_entry_point_function(func) && func.kind == FunctionKind::Recursive { + self.push_err(ResolverError::MisplacedRecursiveAttribute { + ident: func.name_ident().clone(), + }); + } if !self.distinct_allowed(func) && func.def.return_distinctness != Distinctness::DuplicationAllowed diff --git a/noir/compiler/noirc_frontend/src/hir_def/function.rs b/noir/compiler/noirc_frontend/src/hir_def/function.rs index 9fff301f5f7..78f44696b72 100644 --- a/noir/compiler/noirc_frontend/src/hir_def/function.rs +++ b/noir/compiler/noirc_frontend/src/hir_def/function.rs @@ -127,7 +127,7 @@ impl FuncMeta { pub fn can_ignore_return_type(&self) -> bool { match self.kind { FunctionKind::LowLevel | FunctionKind::Builtin | FunctionKind::Oracle => true, - FunctionKind::Normal => false, + FunctionKind::Normal | FunctionKind::Recursive => false, } } diff --git a/noir/compiler/noirc_frontend/src/lexer/token.rs b/noir/compiler/noirc_frontend/src/lexer/token.rs index 835a0baae3f..5d08ab03ad3 100644 --- a/noir/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/compiler/noirc_frontend/src/lexer/token.rs @@ -491,6 +491,7 @@ impl Attribute { Attribute::Function(FunctionAttribute::Oracle(name.to_string())) } ["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)), + ["recursive"] => Attribute::Function(FunctionAttribute::Recursive), ["test", name] => { validate(name)?; let malformed_scope = @@ -541,6 +542,7 @@ pub enum FunctionAttribute { Builtin(String), Oracle(String), Test(TestScope), + Recursive, } impl FunctionAttribute { @@ -578,6 +580,7 @@ impl fmt::Display for FunctionAttribute { FunctionAttribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), + FunctionAttribute::Recursive => write!(f, "#[recursive]"), } } } @@ -621,6 +624,7 @@ impl AsRef for FunctionAttribute { FunctionAttribute::Builtin(string) => string, FunctionAttribute::Oracle(string) => string, FunctionAttribute::Test { .. } => "", + FunctionAttribute::Recursive => "", } } } diff --git a/noir/compiler/noirc_frontend/src/monomorphization/ast.rs b/noir/compiler/noirc_frontend/src/monomorphization/ast.rs index 42a618e7d77..515d9710882 100644 --- a/noir/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -246,6 +246,8 @@ pub struct Program { pub return_distinctness: Distinctness, pub return_location: Option, pub return_visibility: Visibility, + /// Indicates to a backend whether a SNARK-friendly prover should be used. + pub recursive: bool, } impl Program { @@ -255,6 +257,7 @@ impl Program { return_distinctness: Distinctness, return_location: Option, return_visibility: Visibility, + recursive: bool, ) -> Program { Program { functions, @@ -262,6 +265,7 @@ impl Program { return_distinctness, return_location, return_visibility, + recursive, } } diff --git a/noir/compiler/noirc_frontend/src/monomorphization/mod.rs b/noir/compiler/noirc_frontend/src/monomorphization/mod.rs index 67b246a02ce..0334e01af5d 100644 --- a/noir/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -117,6 +117,7 @@ pub fn monomorphize(main: node_interner::FuncId, interner: &NodeInterner) -> Pro meta.return_distinctness, monomorphizer.return_location, meta.return_visibility, + meta.kind == FunctionKind::Recursive, ) } @@ -195,6 +196,9 @@ impl<'interner> Monomorphizer<'interner> { _ => unreachable!("Oracle function must have an oracle attribute"), } } + FunctionKind::Recursive => { + unreachable!("Only main can be specified as recursive, which should already be checked"); + } } } } diff --git a/noir/test_programs/execution_success/assert_statement_recursive/Nargo.toml b/noir/test_programs/execution_success/assert_statement_recursive/Nargo.toml new file mode 100644 index 00000000000..2a5b02cad00 --- /dev/null +++ b/noir/test_programs/execution_success/assert_statement_recursive/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_statement_recursive" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/noir/test_programs/execution_success/assert_statement_recursive/Prover.toml b/noir/test_programs/execution_success/assert_statement_recursive/Prover.toml new file mode 100644 index 00000000000..5d1dc99124f --- /dev/null +++ b/noir/test_programs/execution_success/assert_statement_recursive/Prover.toml @@ -0,0 +1,2 @@ +x = "3" +y = "3" diff --git a/noir/test_programs/execution_success/assert_statement_recursive/src/main.nr b/noir/test_programs/execution_success/assert_statement_recursive/src/main.nr new file mode 100644 index 00000000000..687a0d324ba --- /dev/null +++ b/noir/test_programs/execution_success/assert_statement_recursive/src/main.nr @@ -0,0 +1,11 @@ +// Tests a very simple program. +// +// The features being tested is assertion +// This is the same as the `assert_statement` test except we specify +// that the backend should use a prover which will construct proofs +// friendly to recursive verification in another SNARK. +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); + assert_eq(x, y, "x and y are not equal"); +} \ No newline at end of file diff --git a/noir/tooling/backend_interface/src/cli/prove.rs b/noir/tooling/backend_interface/src/cli/prove.rs index c12e516db57..c63d8afab54 100644 --- a/noir/tooling/backend_interface/src/cli/prove.rs +++ b/noir/tooling/backend_interface/src/cli/prove.rs @@ -13,7 +13,6 @@ use super::string_from_stderr; /// The proof will be written to the specified output file. pub(crate) struct ProveCommand { pub(crate) crs_path: PathBuf, - pub(crate) is_recursive: bool, pub(crate) bytecode_path: PathBuf, pub(crate) witness_path: PathBuf, } @@ -33,10 +32,6 @@ impl ProveCommand { .arg("-o") .arg("-"); - if self.is_recursive { - command.arg("-r"); - } - let output = command.output()?; if output.status.success() { Ok(output.stdout) @@ -61,7 +56,7 @@ fn prove_command() -> Result<(), BackendError> { std::fs::File::create(&witness_path).expect("file should be created"); let crs_path = backend.backend_directory(); - let prove_command = ProveCommand { crs_path, bytecode_path, witness_path, is_recursive: false }; + let prove_command = ProveCommand { crs_path, bytecode_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; assert_eq!(proof, "proof".as_bytes()); diff --git a/noir/tooling/backend_interface/src/cli/verify.rs b/noir/tooling/backend_interface/src/cli/verify.rs index a31f476d84c..1a4ba50b7de 100644 --- a/noir/tooling/backend_interface/src/cli/verify.rs +++ b/noir/tooling/backend_interface/src/cli/verify.rs @@ -6,7 +6,6 @@ use crate::BackendError; /// to verify a proof pub(crate) struct VerifyCommand { pub(crate) crs_path: PathBuf, - pub(crate) is_recursive: bool, pub(crate) proof_path: PathBuf, pub(crate) vk_path: PathBuf, } @@ -24,10 +23,6 @@ impl VerifyCommand { .arg("-k") .arg(self.vk_path); - if self.is_recursive { - command.arg("-r"); - } - let output = command.output()?; // We currently do not distinguish between an invalid proof and an error inside the backend. @@ -64,18 +59,12 @@ fn verify_command() -> Result<(), BackendError> { write_vk_command.run(backend.binary_path())?; - let prove_command = ProveCommand { - crs_path: crs_path.clone(), - is_recursive: false, - bytecode_path, - witness_path, - }; + let prove_command = ProveCommand { crs_path: crs_path.clone(), bytecode_path, witness_path }; let proof = prove_command.run(backend.binary_path())?; write_to_file(&proof, &proof_path); - let verify_command = - VerifyCommand { crs_path, is_recursive: false, proof_path, vk_path: vk_path_output }; + let verify_command = VerifyCommand { crs_path, proof_path, vk_path: vk_path_output }; let verified = verify_command.run(backend.binary_path())?; assert!(verified); diff --git a/noir/tooling/backend_interface/src/proof_system.rs b/noir/tooling/backend_interface/src/proof_system.rs index 595cd7e2020..9369c91fa94 100644 --- a/noir/tooling/backend_interface/src/proof_system.rs +++ b/noir/tooling/backend_interface/src/proof_system.rs @@ -55,7 +55,6 @@ impl Backend { &self, circuit: &Circuit, witness_values: WitnessMap, - is_recursive: bool, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -76,13 +75,9 @@ impl Backend { write_to_file(&serialized_circuit, &bytecode_path); // Create proof and store it in the specified path - let proof_with_public_inputs = ProveCommand { - crs_path: self.crs_directory(), - is_recursive, - bytecode_path, - witness_path, - } - .run(binary_path)?; + let proof_with_public_inputs = + ProveCommand { crs_path: self.crs_directory(), bytecode_path, witness_path } + .run(binary_path)?; let proof = bb_abstraction_leaks::remove_public_inputs( circuit.public_inputs().0.len(), @@ -97,7 +92,6 @@ impl Backend { proof: &[u8], public_inputs: WitnessMap, circuit: &Circuit, - is_recursive: bool, ) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -127,8 +121,7 @@ impl Backend { .run(binary_path)?; // Verify the proof - VerifyCommand { crs_path: self.crs_directory(), is_recursive, proof_path, vk_path } - .run(binary_path) + VerifyCommand { crs_path: self.crs_directory(), proof_path, vk_path }.run(binary_path) } pub fn get_intermediate_proof_artifacts( diff --git a/noir/tooling/backend_interface/src/smart_contract.rs b/noir/tooling/backend_interface/src/smart_contract.rs index 2548079f8e3..524832c6308 100644 --- a/noir/tooling/backend_interface/src/smart_contract.rs +++ b/noir/tooling/backend_interface/src/smart_contract.rs @@ -56,6 +56,7 @@ mod tests { public_parameters: PublicInputs::default(), return_values: PublicInputs::default(), assert_messages: Default::default(), + recursive: false, }; let contract = get_mock_backend()?.eth_contract(&circuit)?; diff --git a/noir/tooling/nargo_cli/src/cli/prove_cmd.rs b/noir/tooling/nargo_cli/src/cli/prove_cmd.rs index 1d20e97af85..cc39b0535bc 100644 --- a/noir/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -138,12 +138,11 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.circuit, solved_witness, false)?; + let proof = backend.prove(&compiled_program.circuit, solved_witness)?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; - let valid_proof = - backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; if !valid_proof { return Err(CliError::InvalidProof("".into())); diff --git a/noir/tooling/nargo_cli/src/cli/verify_cmd.rs b/noir/tooling/nargo_cli/src/cli/verify_cmd.rs index ea4aaa051bb..66b88a22f2a 100644 --- a/noir/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -102,7 +102,7 @@ fn verify_package( let proof = load_hex_data(&proof_path)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit, false)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; if valid_proof { Ok(()) diff --git a/noir/tooling/noir_js/src/program.ts b/noir/tooling/noir_js/src/program.ts index 809943727eb..8d80ec3a247 100644 --- a/noir/tooling/noir_js/src/program.ts +++ b/noir/tooling/noir_js/src/program.ts @@ -67,13 +67,13 @@ export class Noir { * * @example * ```typescript - * async generateFinalProof(input) + * async generateProof(input) * ``` * */ - async generateFinalProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { + async generateProof(inputs: InputMap, foreignCallHandler?: ForeignCallHandler): Promise { const { witness } = await this.execute(inputs, foreignCallHandler); - return this.getBackend().generateFinalProof(witness); + return this.getBackend().generateProof(witness); } /** @@ -84,11 +84,11 @@ export class Noir { * * @example * ```typescript - * async verifyFinalProof(proof) + * async verifyProof(proof) * ``` * */ - async verifyFinalProof(proofData: ProofData): Promise { - return this.getBackend().verifyFinalProof(proofData); + async verifyProof(proofData: ProofData): Promise { + return this.getBackend().verifyProof(proofData); } } diff --git a/noir/tooling/noir_js/test/node/e2e.test.ts b/noir/tooling/noir_js/test/node/e2e.test.ts index 33d64377b06..8921314e8ea 100644 --- a/noir/tooling/noir_js/test/node/e2e.test.ts +++ b/noir/tooling/noir_js/test/node/e2e.test.ts @@ -21,10 +21,10 @@ it('end-to-end proof creation and verification (outer)', async () => { // // Proof creation const prover = new Backend(assert_lt_program); - const proof = await prover.generateFinalProof(witness); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await prover.verifyFinalProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); @@ -40,13 +40,14 @@ it('end-to-end proof creation and verification (outer) -- Program API', async () // Initialize program const program = new Noir(assert_lt_program, backend); // Generate proof - const proof = await program.generateFinalProof(inputs); + const proof = await program.generateProof(inputs); // Proof verification - const isValid = await program.verifyFinalProof(proof); + const isValid = await program.verifyProof(proof); expect(isValid).to.be.true; }); +// TODO: maybe switch to using assert_statement_recursive here to test both options it('end-to-end proof creation and verification (inner)', async () => { // Noir.Js part const inputs = { @@ -62,10 +63,10 @@ it('end-to-end proof creation and verification (inner)', async () => { // // Proof creation const prover = new Backend(assert_lt_program); - const proof = await prover.generateIntermediateProof(witness); + const proof = await prover.generateProof(witness); // Proof verification - const isValid = await prover.verifyIntermediateProof(proof); + const isValid = await prover.verifyProof(proof); expect(isValid).to.be.true; }); @@ -83,10 +84,10 @@ it('end-to-end proving and verification with different instances', async () => { // bb.js part const prover = new Backend(assert_lt_program); - const proof = await prover.generateFinalProof(witness); + const proof = await prover.generateProof(witness); const verifier = new Backend(assert_lt_program); - const proof_is_valid = await verifier.verifyFinalProof(proof); + const proof_is_valid = await verifier.verifyProof(proof); expect(proof_is_valid).to.be.true; }); @@ -115,14 +116,14 @@ it('[BUG] -- bb.js null function or function signature mismatch (outer-inner) ', const prover = new Backend(assert_lt_program); // Create a proof using both proving systems, the majority of the time // one would only use outer proofs. - const proofOuter = await prover.generateFinalProof(witness); - const _proofInner = await prover.generateIntermediateProof(witness); + const proofOuter = await prover.generateProof(witness); + const _proofInner = await prover.generateProof(witness); // Proof verification // - const isValidOuter = await prover.verifyFinalProof(proofOuter); + const isValidOuter = await prover.verifyProof(proofOuter); expect(isValidOuter).to.be.true; // We can also try verifying an inner proof and it will fail. - const isValidInner = await prover.verifyIntermediateProof(_proofInner); + const isValidInner = await prover.verifyProof(_proofInner); expect(isValidInner).to.be.true; }); diff --git a/noir/tooling/noir_js_backend_barretenberg/src/index.ts b/noir/tooling/noir_js_backend_barretenberg/src/index.ts index 61094a3451f..4d5b6389404 100644 --- a/noir/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/noir/tooling/noir_js_backend_barretenberg/src/index.ts @@ -47,47 +47,15 @@ export class BarretenbergBackend implements Backend { } } - /** - * Generate a final proof. This is the proof for the circuit which will verify - * intermediate proofs and or can be seen as the proof created for regular circuits. - */ - async generateFinalProof(decompressedWitness: Uint8Array): Promise { - // The settings for this proof are the same as the settings for a "normal" proof - // i.e. one that is not in the recursive setting. - const makeEasyToVerifyInCircuit = false; - return this.generateProof(decompressedWitness, makeEasyToVerifyInCircuit); - } - - /** - * Generates an intermediate proof. This is the proof that can be verified - * in another circuit. - * - * This is sometimes referred to as a recursive proof. - * We avoid this terminology as the only property of this proof - * that matters is the fact that it is easy to verify in another circuit. - * We _could_ choose to verify this proof outside of a circuit just as easily. - * - * @example - * ```typescript - * const intermediateProof = await backend.generateIntermediateProof(witness); - * ``` - */ - async generateIntermediateProof(witness: Uint8Array): Promise { - // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to - // generate the proof using components that will make the proof - // easier to verify in a circuit. - const makeEasyToVerifyInCircuit = true; - return this.generateProof(witness, makeEasyToVerifyInCircuit); - } - - /** @ignore */ - async generateProof(compressedWitness: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { + /** @description Generates a proof */ + async generateProof(compressedWitness: Uint8Array): Promise { await this.instantiate(); + // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag const proofWithPublicInputs = await this.api.acirCreateProof( this.acirComposer, this.acirUncompressedBytecode, gunzip(compressedWitness), - makeEasyToVerifyInCircuit, + false, ); const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; @@ -105,17 +73,17 @@ export class BarretenbergBackend implements Backend { * Instead of passing the proof and verification key as a byte array, we pass them * as fields which makes it cheaper to verify in a circuit. * - * The proof that is passed here will have been created using the `generateIntermediateProof` - * method. + * The proof that is passed here will have been created using a circuit + * that has the #[recursive] attribute on its `main` method. * * The number of public inputs denotes how many public inputs are in the inner proof. * * @example * ```typescript - * const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); + * const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); * ``` */ - async generateIntermediateProofArtifacts( + async generateRecursiveProofArtifacts( proofData: ProofData, numOfPublicInputs = 0, ): Promise<{ @@ -143,31 +111,13 @@ export class BarretenbergBackend implements Backend { }; } - async verifyFinalProof(proofData: ProofData): Promise { + /** @description Verifies a proof */ + async verifyProof(proofData: ProofData): Promise { const proof = reconstructProofWithPublicInputs(proofData); - const makeEasyToVerifyInCircuit = false; - const verified = await this.verifyProof(proof, makeEasyToVerifyInCircuit); - return verified; - } - - /** - * - * @example - * ```typescript - * const isValidIntermediate = await backend.verifyIntermediateProof(proof); - * ``` - */ - async verifyIntermediateProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - const makeEasyToVerifyInCircuit = true; - return this.verifyProof(proof, makeEasyToVerifyInCircuit); - } - - /** @ignore */ - async verifyProof(proof: Uint8Array, makeEasyToVerifyInCircuit: boolean): Promise { await this.instantiate(); await this.api.acirInitVerificationKey(this.acirComposer); - return await this.api.acirVerifyProof(this.acirComposer, proof, makeEasyToVerifyInCircuit); + // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag + return await this.api.acirVerifyProof(this.acirComposer, proof, false); } async destroy(): Promise { diff --git a/noir/tooling/noir_js_types/src/types.ts b/noir/tooling/noir_js_types/src/types.ts index ee4921bd606..84148c5d855 100644 --- a/noir/tooling/noir_js_types/src/types.ts +++ b/noir/tooling/noir_js_types/src/types.ts @@ -4,18 +4,14 @@ export { Abi, WitnessMap } from '@noir-lang/noirc_abi'; export interface Backend { /** - * @description Generates a final proof (not meant to be verified in another circuit) */ - generateFinalProof(decompressedWitness: Uint8Array): Promise; - - /** - * @description Generates an intermediate proof (meant to be verified in another circuit) */ - generateIntermediateProof(decompressedWitness: Uint8Array): Promise; + * @description Generates a proof */ + generateProof(decompressedWitness: Uint8Array): Promise; /** * * @description Retrieves the artifacts from a proof in the Field format */ - generateIntermediateProofArtifacts( + generateRecursiveProofArtifacts( proofData: ProofData, numOfPublicInputs: number, ): Promise<{ @@ -28,11 +24,8 @@ export interface Backend { }>; /** - * @description Verifies a final proof */ - verifyFinalProof(proofData: ProofData): Promise; - - /** @description Verifies an intermediate proof */ - verifyIntermediateProof(proofData: ProofData): Promise; + * @description Verifies a proof */ + verifyProof(proofData: ProofData): Promise; /** * @description Destroys the backend */