From 85997818e42b9edc117ab83772767cc4a3e3e198 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Wed, 3 May 2023 19:32:19 +0000 Subject: [PATCH 01/14] merge conflicts and small updates to get acir_proofs test passing with arbitrary depth cahnges --- .../dsl/acir_format/acir_format.cpp | 74 ++++++- .../dsl/acir_format/recursion_constraint.cpp | 32 +-- .../dsl/acir_format/recursion_constraint.hpp | 21 +- .../acir_format/recursion_constraint.test.cpp | 188 ++++++++++++------ .../dsl/acir_proofs/acir_proofs.cpp | 7 +- .../dsl/acir_proofs/acir_proofs.test.cpp | 9 +- .../plonk/composer/composer_base.hpp | 19 ++ .../plonk/composer/ultra_composer.hpp | 17 ++ .../verification_key/verification_key.hpp | 12 +- 9 files changed, 279 insertions(+), 100 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index ee52dd0c87..73a506b6d3 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -86,8 +86,18 @@ void create_circuit(Composer& composer, const acir_format& constraint_system) } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } } @@ -170,8 +180,22 @@ Composer create_circuit(const acir_format& constraint_system, } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + for (size_t i = 0; i < 16; ++i) { + std::cout << "proof_output_witness_indices " << i << ": " << proof_output_witness_indices[i] + << std::endl; + } + composer.set_recursive_proof(proof_output_witness_indices); + } } return composer; @@ -259,8 +283,18 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } return composer; } @@ -344,8 +378,18 @@ Composer create_circuit_with_witness(const acir_format& constraint_system, std:: } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } return composer; } @@ -427,8 +471,18 @@ void create_circuit_with_witness(Composer& composer, const acir_format& constrai } // Add recursion constraints - for (const auto& constraint : constraint_system.recursion_constraints) { - create_recursion_constraints(composer, constraint); + for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + auto& constraint = constraint_system.recursion_constraints[i]; + create_recursion_constraints(composer, constraint); + + // make sure the verification key records the public input indices of the final recursion output + // (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public + // inputs!) + if (i == constraint_system.recursion_constraints.size() - 1) { + std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), + constraint.output_aggregation_object.end()); + composer.set_recursive_proof(proof_output_witness_indices); + } } } diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 441b47c2d8..a98b8c10dc 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -22,17 +22,22 @@ void generate_dummy_proof() {} * We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, * or we need non-witness data to be provided as metadata in the ACIR opcode */ -template +template void create_recursion_constraints(Composer& composer, const RecursionConstraint& input) { - + const auto& nested_aggregation_indices = input.nested_aggregation_object; + bool nested_aggregation_indices_all_zero = true; + for (const auto& idx : nested_aggregation_indices) { + nested_aggregation_indices_all_zero &= (idx == 0); + } + const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero; // If we do not have a witness, we must ensure that our dummy witness will not trigger // on-curve errors and inverting-zero errors { // get a fake key/proof that satisfies on-curve + inversion-zero checks const std::vector dummy_key = plonk::verification_key::export_dummy_key_in_recursion_format( PolynomialManifest(Composer::type), inner_proof_contains_recursive_proof); - const auto manifest = Composer::create_unrolled_manifest(1); + const auto manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); const std::vector dummy_proof = transcript::StandardTranscript::export_dummy_transcript_in_recursion_format(manifest); for (size_t i = 0; i < input.proof.size(); ++i) { @@ -88,7 +93,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& previous_aggregation.has_data = false; } - transcript::Manifest manifest = Composer::create_unrolled_manifest(1); + transcript::Manifest manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); std::vector key_fields; key_fields.reserve(input.key.size()); for (const auto& idx : input.key) { @@ -101,10 +106,10 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } // recursively verify the proof - std::shared_ptr vkey = - verification_key_ct::template from_field_pt_vector(&composer, key_fields); + std::shared_ptr vkey = verification_key_ct::from_field_pt_vector( + &composer, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_indices); vkey->program_width = noir_recursive_settings::program_width; - Transcript_ct transcript(&composer, manifest, proof_fields, 1); + Transcript_ct transcript(&composer, manifest, proof_fields, input.public_inputs.size()); aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof_( &composer, vkey, transcript, previous_aggregation); @@ -115,11 +120,12 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // G1 points) result.add_proof_outputs_as_public_inputs(); - ASSERT(result.public_inputs.size() == 1); + ASSERT(result.public_inputs.size() == input.public_inputs.size()); // Assign the `public_input` field to the public input of the inner proof - result.public_inputs[0].assert_equal(field_ct::from_witness_index(&composer, input.public_input)); - + for (size_t i = 0; i < input.public_inputs.size(); ++i) { + result.public_inputs[i].assert_equal(field_ct::from_witness_index(&composer, input.public_inputs[i])); + } // Assign the recursive proof outputs to `output_aggregation_object` for (size_t i = 0; i < result.proof_witness_indices.size(); ++i) { const auto lhs = field_ct::from_witness_index(&composer, result.proof_witness_indices[i]); @@ -128,9 +134,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } } -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); -template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); +template void create_recursion_constraints(Composer&, const RecursionConstraint&); } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index cfc0116349..d0b78af31b 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -25,39 +25,43 @@ namespace acir_format { * @param public_input The index of the single public input * @param input_aggregation_object Witness indices of pre-existing aggregation object (if it exists) * @param output_aggregation_object Witness indecies of the aggregation object produced by recursive verification + * @param nested_aggregation_object Public input indices of an aggregation object inside the proof. * * @note If input_aggregation_object witness indices are all zero, we interpret this to mean that the inner proof does * NOT contain + * @note nested_aggregation_object is used for cases where the proof being verified contains an aggregation object in + * its public inputs! If this is the case, we record the public input locations in `nested_aggregation_object`. If the + * inner proof is of a circuit that does not have a nested aggregation object, these values are all zero. */ struct RecursionConstraint { static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements std::vector key; std::vector proof; - uint32_t public_input; + std::vector public_inputs; uint32_t key_hash; std::array input_aggregation_object; std::array output_aggregation_object; + std::array nested_aggregation_object; friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default; }; -template +template void create_recursion_constraints(Composer& composer, const RecursionConstraint& input); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); -extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); +extern template void create_recursion_constraints(Composer&, const RecursionConstraint&); template inline void read(B& buf, RecursionConstraint& constraint) { using serialize::read; read(buf, constraint.key); read(buf, constraint.proof); - read(buf, constraint.public_input); + read(buf, constraint.public_inputs); read(buf, constraint.key_hash); read(buf, constraint.input_aggregation_object); read(buf, constraint.output_aggregation_object); + read(buf, constraint.nested_aggregation_object); } template inline void write(B& buf, RecursionConstraint const& constraint) @@ -65,10 +69,11 @@ template inline void write(B& buf, RecursionConstraint const& const using serialize::write; write(buf, constraint.key); write(buf, constraint.proof); - write(buf, constraint.public_input); + write(buf, constraint.public_inputs); write(buf, constraint.key_hash); write(buf, constraint.input_aggregation_object); write(buf, constraint.output_aggregation_object); + write(buf, constraint.nested_aggregation_object); } } // namespace acir_format diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 63b051c478..c64a1c90ca 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -75,13 +75,10 @@ acir_format::Composer create_inner_circuit() .q_o = 0, .q_c = 1, }; - // EXPR [ (1, _4, _5) (-1, _6) 0 ] - // EXPR [ (1, _4, _6) (-1, _4) 0 ] - // EXPR [ (-1, _6) 1 ] acir_format::acir_format constraint_system{ .varnum = 7, - .public_inputs = { 2 }, + .public_inputs = { 2, 3 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, @@ -110,67 +107,101 @@ acir_format::Composer create_inner_circuit() return composer; } -TEST(RecursionConstraint, TestRecursionConstraint) +/** + * @brief Create a circuit that recursively verifies one or more inner circuits + * + * @param inner_composers + * @return acir_format::Composer + */ +acir_format::Composer create_outer_circuit(std::vector& inner_composers) { - auto inner_composer = create_inner_circuit(); + std::vector recursion_constraints; - auto inner_prover = inner_composer.create_prover(); - auto inner_proof = inner_prover.construct_proof(); - auto inner_verifier = inner_composer.create_verifier(); + // witness count starts at 1 (Composer reserves 1st witness to be the zero-valued zero_idx) + size_t witness_offset = 1; + std::array output_aggregation_object; + std::vector witness; - // std::vector keybuf; - // write(keybuf, *(inner_verifier.key)); + for (size_t i = 0; i < inner_composers.size(); ++i) { + const bool has_input_aggregation_object = i > 0; - std::array output_vars; - for (size_t i = 0; i < 16; ++i) { - // variable idx 1 = public input - // variable idx 2-18 = output_vars - output_vars[i] = (static_cast(i + 3)); - } + auto& inner_composer = inner_composers[i]; + auto inner_prover = inner_composer.create_prover(); + auto inner_proof = inner_prover.construct_proof(); + auto inner_verifier = inner_composer.create_verifier(); - transcript::StandardTranscript transcript(inner_proof.proof_data, - acir_format::Composer::create_manifest(1), - transcript::HashType::PlookupPedersenBlake3s, - 16); + const bool has_nested_proof = inner_verifier.key->contains_recursive_proof; + const size_t num_inner_public_inputs = inner_composer.get_num_public_inputs(); - const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); - const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); + transcript::StandardTranscript transcript(inner_proof.proof_data, + acir_format::Composer::create_manifest(num_inner_public_inputs), + transcript::HashType::PlookupPedersenBlake3s, + 16); - std::vector proof_indices; - const size_t proof_size = proof_witnesses.size(); + const std::vector proof_witnesses = transcript.export_transcript_in_recursion_format(); + const std::vector key_witnesses = inner_verifier.key->export_key_in_recursion_format(); - for (size_t i = 0; i < proof_size; ++i) { - proof_indices.emplace_back(static_cast(i + 19)); - } + const uint32_t key_hash_start_idx = static_cast(witness_offset); + const uint32_t public_input_start_idx = key_hash_start_idx + 1; + const uint32_t output_aggregation_object_start_idx = + static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); + const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; + const uint32_t key_indices_start_idx = static_cast(proof_indices_start_idx + proof_witnesses.size()); - std::vector key_indices; - const size_t key_size = key_witnesses.size(); - for (size_t i = 0; i < key_size; ++i) { - key_indices.emplace_back(static_cast(i + 19 + proof_size)); - } - acir_format::RecursionConstraint recursion_constraint{ - .key = key_indices, - .proof = proof_indices, - .public_input = 1, - .key_hash = 2, - .input_aggregation_object = {}, - .output_aggregation_object = output_vars, - }; + std::vector proof_indices; + std::vector key_indices; + std::vector inner_public_inputs; + std::array input_aggregation_object = {}; + std::array nested_aggregation_object = {}; + if (has_input_aggregation_object) { + input_aggregation_object = output_aggregation_object; + } + for (size_t i = 0; i < 16; ++i) { + output_aggregation_object[i] = (static_cast(i + output_aggregation_object_start_idx)); + } + if (has_nested_proof) { + for (size_t i = 0; i < 16; ++i) { + nested_aggregation_object[i] = inner_composer.recursive_proof_public_input_indices[i]; + } + } + for (size_t i = 0; i < proof_witnesses.size(); ++i) { + proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); + } + const size_t key_size = key_witnesses.size(); + for (size_t i = 0; i < key_size; ++i) { + key_indices.emplace_back(static_cast(i + key_indices_start_idx)); + } + for (size_t i = 0; i < num_inner_public_inputs; ++i) { + inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); + } - std::vector witness; - for (size_t i = 0; i < 18; ++i) { - witness.emplace_back(0); - } - for (const auto& wit : proof_witnesses) { - witness.emplace_back(wit); - } - for (const auto& wit : key_witnesses) { - witness.emplace_back(wit); + acir_format::RecursionConstraint recursion_constraint{ + .key = key_indices, + .proof = proof_indices, + .public_inputs = inner_public_inputs, + .key_hash = key_hash_start_idx, + .input_aggregation_object = input_aggregation_object, + .output_aggregation_object = output_aggregation_object, + .nested_aggregation_object = nested_aggregation_object, + }; + recursion_constraints.push_back(recursion_constraint); + for (size_t i = 0; i < proof_indices_start_idx - witness_offset; ++i) { + witness.emplace_back(0); + } + for (const auto& wit : proof_witnesses) { + witness.emplace_back(wit); + } + for (const auto& wit : key_witnesses) { + witness.emplace_back(wit); + } + witness_offset = key_indices_start_idx + key_witnesses.size(); } + std::vector public_inputs(output_aggregation_object.begin(), output_aggregation_object.end()); + acir_format::acir_format constraint_system{ .varnum = static_cast(witness.size() + 1), - .public_inputs = { 1 }, + .public_inputs = public_inputs, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = {}, .range_constraints = {}, @@ -181,16 +212,61 @@ TEST(RecursionConstraint, TestRecursionConstraint) .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, - .recursion_constraints = { recursion_constraint }, + .recursion_constraints = recursion_constraints, .constraints = {}, }; auto composer = acir_format::create_circuit_with_witness(constraint_system, witness); - auto prover = composer.create_prover(); + return composer; +} + +TEST(RecursionConstraint, TestRecursionConstraints) +{ + /** + * We want to test the following: + * 1. circuit that verifies a proof of another circuit + * 2. the above, but the inner circuit contains a recursive proof output that we have to aggregate + * 3. the above, but the outer circuit verifies 2 proofs, the aggregation outputs from the 2 proofs (+ the recursive + * proof output from 2) are aggregated together + * + * A = basic circuit + * B = circuit that verifies proof of A + * C = circuit that verifies proof of B and a proof of A + * + * Layer 1 = proof of A + * Layer 2 = verifies proof of A and proof of B + * Layer 3 = verifies proof of C + * + * Attempt at a visual graphic + * =========================== + * + * C + * ^ + * | + * | - B + * ^ ^ + * | | + * | -A + * | + * - A + * + * =========================== + * + * Final aggregation object contains aggregated proofs for 2 instances of A and 1 instance of B + */ + std::vector layer_1_composers; + layer_1_composers.push_back(create_inner_circuit()); + + std::vector layer_2_composers; + + layer_2_composers.push_back(create_inner_circuit()); + layer_2_composers.push_back(create_outer_circuit(layer_1_composers)); + auto layer_3_composer = create_outer_circuit(layer_2_composers); + std::cout << "composer gates = " << layer_3_composer.get_num_gates() << std::endl; + auto prover = layer_3_composer.create_prover(); + std::cout << "prover gates = " << prover.circuit_size << std::endl; auto proof = prover.construct_proof(); - auto verifier = composer.create_verifier(); + auto verifier = layer_3_composer.create_verifier(); EXPECT_EQ(verifier.verify_proof(proof), true); - - EXPECT_EQ(composer.get_variable(1), 10); } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 527d944b61..975f0ceade 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -49,6 +49,7 @@ uint32_t get_total_circuit_size(uint8_t const* constraint_system_buf) size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk_buf) { + std::cout << "inside init_proving_key\n"; auto constraint_system = from_buffer(constraint_system_buf); // constraint_system.recursion_constraints[0]. @@ -57,6 +58,8 @@ size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk // Hacky, but, right now it needs *something*. auto crs_factory = std::make_unique(); auto composer = create_circuit(constraint_system, std::move(crs_factory)); + std::cout << "past create_circuit\n"; + auto proving_key = composer.compute_proving_key(); auto buffer = to_buffer(*proving_key); @@ -300,8 +303,10 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, // We currently only support RecursionConstraint where inner_proof_contains_recursive_proof = false. // We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, // or we need non-witness data to be provided as metadata in the ACIR opcode + // recursively verify the proof + // TODO: handle inner_proof_contains_recursive_proof and recursive_proof_public_input_indices std::shared_ptr vkey = - acir_format::verification_key_ct::template from_field_pt_vector(&composer, key_fields); + acir_format::verification_key_ct::from_field_pt_vector(&composer, key_fields); vkey->program_width = acir_format::noir_recursive_settings::program_width; acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, 1); acir_format::aggregation_state_ct result = diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index bba475b798..fa33d0da1f 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -200,6 +200,8 @@ TEST(AcirProofs, TestSerializationWithRecursion) } // outer circuit { + std::cout << "outer circuit\n"; + fr vk_hash_value; std::vector proof_witnesses(proof_fields_size / 32); std::vector key_witnesses(vk_fields_size / 32); @@ -227,16 +229,17 @@ TEST(AcirProofs, TestSerializationWithRecursion) std::array output_vars; for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { // variable idx 1 = public input - // variable idx 2-18 = output_vars + // variable idx 3-18 = output_vars output_vars[i] = (static_cast(i + 3)); } acir_format::RecursionConstraint recursion_constraint{ .key = key_indices, .proof = proof_indices, - .public_input = 1, + .public_inputs = { 1 }, .key_hash = 2, .input_aggregation_object = {}, .output_aggregation_object = output_vars, + .nested_aggregation_object = {}, }; // Add a constraint that fixes the vk hash to be the expected value! @@ -285,6 +288,7 @@ TEST(AcirProofs, TestSerializationWithRecursion) write(witness_buf, witness); uint8_t const* pk_buf = nullptr; acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + std::cout << "init_proving_key\n"; uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); @@ -306,6 +310,7 @@ TEST(AcirProofs, TestSerializationWithRecursion) size_t proof_length = acir_proofs::new_proof( pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); + std::cout << "new_proof\n"; bool verified = acir_proofs::verify_proof(&g2x_buffer[0], vk_buf, &constraint_system_buf[0], diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index 6c93da04b1..52deaed30b 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -186,6 +186,25 @@ class ComposerBase { barretenberg::fr get_public_input(const uint32_t index) const { return get_variable(public_inputs[index]); } + uint32_t get_public_input_index(const uint32_t witness_index) const + { + std::cout << "inside get_public_input_index\n"; + std::cout << "witness_index: " << witness_index << std::endl; + std::cout << "public_inputs.size(): " << public_inputs.size() << std::endl; + + bool found = false; + uint32_t result = static_cast(-1); + for (size_t i = 0; i < public_inputs.size(); ++i) { + if (real_variable_index[public_inputs[i]] == real_variable_index[witness_index]) { + found = true; + result = static_cast(i); + break; + } + } + ASSERT(found == true); + return result; + } + std::vector get_public_inputs() const { std::vector result; diff --git a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp index 6f22b40bb0..6a73f5f2bc 100644 --- a/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp +++ b/cpp/src/barretenberg/plonk/composer/ultra_composer.hpp @@ -261,6 +261,23 @@ class UltraComposer : public ComposerBase { } } + /** + * @brief Update recursive_proof_public_input_indices with existing public inputs that represent a recursive proof + * + * @param proof_output_witness_indices + */ + void set_recursive_proof(const std::vector& proof_output_witness_indices) + { + if (contains_recursive_proof) { + failure("added recursive proof when one already exists"); + } + contains_recursive_proof = true; + for (size_t i = 0; i < proof_output_witness_indices.size(); ++i) { + recursive_proof_public_input_indices.push_back( + get_public_input_index(real_variable_index[proof_output_witness_indices[i]])); + } + } + void create_new_range_constraint(const uint32_t variable_index, const uint64_t target_range, std::string const msg = "create_new_range_constraint"); diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index 2cd486bbe1..bcbb7b33b8 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -120,10 +120,10 @@ template struct evaluation_domain { template struct verification_key { using Composer = typename Curve::Composer; - template static std::shared_ptr from_field_pt_vector( Composer* ctx, const std::vector>& fields, + bool inner_proof_contains_recursive_proof = false, std::array recursive_proof_public_input_indices = {}) { std::vector fields_raw; @@ -138,15 +138,9 @@ template struct verification_key { // NOTE: For now `contains_recursive_proof` and `recursive_proof_public_input_indices` need to be circuit // constants! - key->contains_recursive_proof = static_cast(uint256_t(fields[5].get_value())); + key->contains_recursive_proof = inner_proof_contains_recursive_proof; for (size_t i = 0; i < 16; ++i) { - const uint32_t idx = static_cast(uint256_t(fields[6 + i].get_value())); - key->recursive_proof_public_input_indices.emplace_back(idx); - } - // Apply constraints to force the recursive proof information to be circuit constants - fields[5].assert_equal(inner_proof_contains_recursive_proof); - for (size_t i = 0; i < 16; ++i) { - fields[6 + i].assert_equal(recursive_proof_public_input_indices[i]); + key->recursive_proof_public_input_indices.emplace_back(recursive_proof_public_input_indices[i]); } size_t count = 22; From c1a4367fbdc201ff2e7210fac1d2c2bd02a7ee74 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 15:58:40 +0000 Subject: [PATCH 02/14] inline with mv/noir-recursion and working double recursion acir_proofs serialization test --- .../dsl/acir_format/acir_format.cpp | 5 +- .../dsl/acir_format/recursion_constraint.cpp | 10 +- .../acir_format/recursion_constraint.test.cpp | 33 +- .../dsl/acir_proofs/acir_proofs.cpp | 2 - .../dsl/acir_proofs/acir_proofs.test.cpp | 428 +++++++++++------- .../plonk/composer/composer_base.hpp | 6 +- .../verification_key/verification_key.cpp | 5 +- .../plonk/proof_system/verifier/verifier.cpp | 2 + .../verification_key/verification_key.hpp | 3 +- 9 files changed, 320 insertions(+), 174 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 73a506b6d3..d71b3f66e3 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -181,6 +181,7 @@ Composer create_circuit(const acir_format& constraint_system, // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { + std::cout << "recursion_constraint: " << i << std::endl; auto& constraint = constraint_system.recursion_constraints[i]; create_recursion_constraints(composer, constraint); @@ -190,10 +191,6 @@ Composer create_circuit(const acir_format& constraint_system, if (i == constraint_system.recursion_constraints.size() - 1) { std::vector proof_output_witness_indices(constraint.output_aggregation_object.begin(), constraint.output_aggregation_object.end()); - for (size_t i = 0; i < 16; ++i) { - std::cout << "proof_output_witness_indices " << i << ": " << proof_output_witness_indices[i] - << std::endl; - } composer.set_recursive_proof(proof_output_witness_indices); } } diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index a98b8c10dc..88e9f61207 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -31,6 +31,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& nested_aggregation_indices_all_zero &= (idx == 0); } const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero; + // If we do not have a witness, we must ensure that our dummy witness will not trigger // on-curve errors and inverting-zero errors { @@ -42,6 +43,8 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& transcript::StandardTranscript::export_dummy_transcript_in_recursion_format(manifest); for (size_t i = 0; i < input.proof.size(); ++i) { const auto proof_field_idx = input.proof[i]; + // std::cout << "proof_field_idx: " << proof_field_idx << std::endl; + // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), // we add our dummy proof values as Composer variables. // if we DO have a valid witness assignment, we use the real witness assignment @@ -56,6 +59,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } for (size_t i = 0; i < input.key.size(); ++i) { const auto key_field_idx = input.key[i]; + // std::cout << "key_field_idx: " << key_field_idx << std::endl; barretenberg::fr dummy_field = has_valid_witness_assignment ? composer.get_variable(key_field_idx) : dummy_key[i]; composer.assert_equal(composer.add_variable(dummy_field), key_field_idx); @@ -94,11 +98,13 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } transcript::Manifest manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); + std::vector key_fields; key_fields.reserve(input.key.size()); for (const auto& idx : input.key) { key_fields.emplace_back(field_ct::from_witness_index(&composer, idx)); } + std::vector proof_fields; proof_fields.reserve(input.proof.size()); for (const auto& idx : input.proof) { @@ -115,10 +121,11 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // Assign correct witness value to the verification key hash vkey->compress().assert_equal(field_ct::from_witness_index(&composer, input.key_hash)); + // std::cout << "compressed vkey\n"; // Assign the output aggregation object to the proof public inputs (16 field elements representing two // G1 points) - result.add_proof_outputs_as_public_inputs(); + // result.add_proof_outputs_as_public_inputs(); ASSERT(result.public_inputs.size() == input.public_inputs.size()); @@ -126,6 +133,7 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& for (size_t i = 0; i < input.public_inputs.size(); ++i) { result.public_inputs[i].assert_equal(field_ct::from_witness_index(&composer, input.public_inputs[i])); } + // Assign the recursive proof outputs to `output_aggregation_object` for (size_t i = 0; i < result.proof_witness_indices.size(); ++i) { const auto lhs = field_ct::from_witness_index(&composer, result.proof_witness_indices[i]); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index c64a1c90ca..2d0dfc1f7a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -78,7 +78,7 @@ acir_format::Composer create_inner_circuit() acir_format::acir_format constraint_system{ .varnum = 7, - .public_inputs = { 2, 3 }, + .public_inputs = { 2 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, @@ -221,7 +221,24 @@ acir_format::Composer create_outer_circuit(std::vector& i return composer; } -TEST(RecursionConstraint, TestRecursionConstraints) +TEST(RecursionConstraint, TestBasicDoubleRecursionConstraints) +{ + std::vector layer_1_composers; + layer_1_composers.push_back(create_inner_circuit()); + + layer_1_composers.push_back(create_inner_circuit()); + + auto layer_2_composer = create_outer_circuit(layer_1_composers); + + std::cout << "composer gates = " << layer_2_composer.get_num_gates() << std::endl; + auto prover = layer_2_composer.create_ultra_with_keccak_prover(); + std::cout << "prover gates = " << prover.circuit_size << std::endl; + auto proof = prover.construct_proof(); + auto verifier = layer_2_composer.create_ultra_with_keccak_verifier(); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +TEST(RecursionConstraint, TestFullRecursionConstraints) { /** * We want to test the following: @@ -257,16 +274,22 @@ TEST(RecursionConstraint, TestRecursionConstraints) */ std::vector layer_1_composers; layer_1_composers.push_back(create_inner_circuit()); - + std::cout << "created first inner circuit\n"; std::vector layer_2_composers; layer_2_composers.push_back(create_inner_circuit()); + std::cout << "created second inner circuit\n"; + layer_2_composers.push_back(create_outer_circuit(layer_1_composers)); + std::cout << "created first outer circuit\n"; + auto layer_3_composer = create_outer_circuit(layer_2_composers); + std::cout << "created second outer circuit\n"; + std::cout << "composer gates = " << layer_3_composer.get_num_gates() << std::endl; - auto prover = layer_3_composer.create_prover(); + auto prover = layer_3_composer.create_ultra_with_keccak_prover(); std::cout << "prover gates = " << prover.circuit_size << std::endl; auto proof = prover.construct_proof(); - auto verifier = layer_3_composer.create_verifier(); + auto verifier = layer_3_composer.create_ultra_with_keccak_verifier(); EXPECT_EQ(verifier.verify_proof(proof), true); } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 975f0ceade..7a9fcd4686 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -49,7 +49,6 @@ uint32_t get_total_circuit_size(uint8_t const* constraint_system_buf) size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk_buf) { - std::cout << "inside init_proving_key\n"; auto constraint_system = from_buffer(constraint_system_buf); // constraint_system.recursion_constraints[0]. @@ -58,7 +57,6 @@ size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk // Hacky, but, right now it needs *something*. auto crs_factory = std::make_unique(); auto composer = create_circuit(constraint_system, std::move(crs_factory)); - std::cout << "past create_circuit\n"; auto proving_key = composer.compute_proving_key(); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index fa33d0da1f..fdeb27e974 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -139,7 +139,13 @@ TEST(AcirProofs, TestSerialization) EXPECT_EQ(verified, true); } -TEST(AcirProofs, TestSerializationWithRecursion) +struct RecursiveCircuitData { + std::vector key_witnesses; + std::vector proof_witnesses; + size_t num_public_inputs; +}; + +RecursiveCircuitData fetch_recursive_circuit_data(acir_format::acir_format constraint_system, std::vector witness) { uint8_t* proof_data_fields = nullptr; uint8_t* vk_fields = nullptr; @@ -147,115 +153,149 @@ TEST(AcirProofs, TestSerializationWithRecursion) size_t proof_fields_size = 0; size_t vk_fields_size = 0; - // inner circuit - { - acir_format::acir_format constraint_system; - std::vector witness; - create_inner_circuit(constraint_system, witness); - - std::vector witness_buf; // = to_buffer(witness); - std::vector constraint_system_buf; // - write(constraint_system_buf, constraint_system); - write(witness_buf, witness); - - uint8_t const* pk_buf = nullptr; - acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); - - uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); - uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); - auto env_crs = std::make_unique(); - auto verifier_crs = env_crs->get_verifier_crs(); - barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); - - auto* pippenger_ptr_base = - new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); - void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); - - std::vector g2x_buffer(128); - io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); - - uint8_t const* vk_buf = nullptr; - acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); - - uint8_t* proof_data_buf = nullptr; - size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); - proof_fields_size = - acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); - vk_fields_size = acir_proofs::serialize_verification_key_into_field_elements( - &g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); - - bool verified = acir_proofs::verify_proof(&g2x_buffer[0], - vk_buf, - &constraint_system_buf[0], - proof_data_buf, - static_cast(proof_length), - true); - EXPECT_EQ(verified, true); - - delete pippenger_ptr_base; - free((void*)vk_buf); - free((void*)pk_buf); - free((void*)proof_data_buf); + std::vector witness_buf; // = to_buffer(witness); + std::vector constraint_system_buf; // + write(constraint_system_buf, constraint_system); + write(witness_buf, witness); + + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); + proof_fields_size = + acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); + vk_fields_size = + acir_proofs::serialize_verification_key_into_field_elements(&g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), true); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); + + fr vk_hash_value; + std::vector proof_witnesses(proof_fields_size / 32); + std::vector key_witnesses((vk_fields_size / 32) + 1); + for (size_t i = 0; i < proof_fields_size / 32; i++) { + proof_witnesses[i] = barretenberg::fr::serialize_from_buffer(&proof_data_fields[i * 32]); + } + for (size_t i = 0; i < vk_fields_size / 32; i++) { + key_witnesses[i] = barretenberg::fr::serialize_from_buffer(&vk_fields[i * 32]); } - // outer circuit - { - std::cout << "outer circuit\n"; - - fr vk_hash_value; - std::vector proof_witnesses(proof_fields_size / 32); - std::vector key_witnesses(vk_fields_size / 32); - for (size_t i = 0; i < proof_fields_size / 32; i++) { - proof_witnesses[i] = barretenberg::fr::serialize_from_buffer(&proof_data_fields[i * 32]); - } - for (size_t i = 0; i < vk_fields_size / 32; i++) { - key_witnesses[i] = barretenberg::fr::serialize_from_buffer(&vk_fields[i * 32]); - } - vk_hash_value = barretenberg::fr::serialize_from_buffer(vk_hash_buf); - std::vector proof_indices; + free((void*)proof_data_fields); + free((void*)vk_fields); - const size_t proof_size = proof_witnesses.size(); - for (size_t i = 0; i < proof_size; ++i) { - proof_indices.emplace_back(static_cast(i + 19)); - } + vk_hash_value = barretenberg::fr::serialize_from_buffer(vk_hash_buf); + key_witnesses[vk_fields_size / 32] = vk_hash_value; + auto inner_circuit = RecursiveCircuitData{ key_witnesses, proof_witnesses, constraint_system.public_inputs.size() }; + return inner_circuit; +} + +RecursiveCircuitData fetch_inner_circuit_data() +{ + acir_format::acir_format constraint_system; + std::vector witness; + create_inner_circuit(constraint_system, witness); + + return fetch_recursive_circuit_data(constraint_system, witness); +} + +/** + * @brief Create a circuit that recursively verifies one or more inner circuits + * + * @param inner_composers + * @return acir_format::Composer + */ +std::pair> create_outer_circuit( + std::vector& inner_circuits) +{ + std::vector recursion_constraints; + + // witness count starts at 1 (Composer reserves 1st witness to be the zero-valued zero_idx) + size_t witness_offset = 1; + std::array output_aggregation_object; + std::vector witness; + + for (size_t i = 0; i < inner_circuits.size(); ++i) { + const bool has_input_aggregation_object = i > 0; + auto& inner_circuit = inner_circuits[i]; + const std::vector proof_witnesses = inner_circuit.proof_witnesses; + const std::vector key_witnesses = inner_circuit.key_witnesses; + + const size_t num_inner_public_inputs = inner_circuit.num_public_inputs; + + const uint32_t key_hash_start_idx = static_cast(witness_offset); + const uint32_t public_input_start_idx = key_hash_start_idx + 1; + // TODO test nested proofs + + const uint32_t output_aggregation_object_start_idx = + static_cast(public_input_start_idx + num_inner_public_inputs); + // const uint32_t output_aggregation_object_start_idx = + // static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); + const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; + const uint32_t key_indices_start_idx = static_cast(proof_indices_start_idx + proof_witnesses.size()); + + std::vector proof_indices; std::vector key_indices; + std::vector inner_public_inputs; + std::array input_aggregation_object = {}; + std::array nested_aggregation_object = {}; + if (has_input_aggregation_object) { + input_aggregation_object = output_aggregation_object; + } + for (size_t i = 0; i < 16; ++i) { + output_aggregation_object[i] = (static_cast(i + output_aggregation_object_start_idx)); + } + // if (has_nested_proof) { + // std::cout << "has_nested_proof: " << has_nested_proof << std::endl; + // for (size_t i = 0; i < 16; ++i) { + // nested_aggregation_object[i] = inner_composer.recursive_proof_public_input_indices[i]; + // } + // } + for (size_t i = 0; i < proof_witnesses.size(); ++i) { + proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); + } const size_t key_size = key_witnesses.size(); for (size_t i = 0; i < key_size; ++i) { - key_indices.emplace_back(static_cast(i + 19 + proof_size)); + key_indices.emplace_back(static_cast(i + key_indices_start_idx)); } - - std::array output_vars; - for (size_t i = 0; i < acir_format::RecursionConstraint::AGGREGATION_OBJECT_SIZE; ++i) { - // variable idx 1 = public input - // variable idx 3-18 = output_vars - output_vars[i] = (static_cast(i + 3)); + for (size_t i = 0; i < num_inner_public_inputs; ++i) { + inner_public_inputs.push_back(static_cast(i + public_input_start_idx)); } + acir_format::RecursionConstraint recursion_constraint{ .key = key_indices, .proof = proof_indices, - .public_inputs = { 1 }, - .key_hash = 2, - .input_aggregation_object = {}, - .output_aggregation_object = output_vars, - .nested_aggregation_object = {}, - }; - - // Add a constraint that fixes the vk hash to be the expected value! - poly_triple vk_equality_constraint{ - .a = recursion_constraint.key_hash, - .b = 0, - .c = 0, - .q_m = 0, - .q_l = 1, - .q_r = 0, - .q_o = 0, - .q_c = -vk_hash_value, + .public_inputs = inner_public_inputs, + .key_hash = key_hash_start_idx, + .input_aggregation_object = input_aggregation_object, + .output_aggregation_object = output_aggregation_object, + .nested_aggregation_object = nested_aggregation_object, }; - - std::vector witness; - for (size_t i = 0; i < 18; ++i) { + recursion_constraints.push_back(recursion_constraint); + for (size_t i = 0; i < proof_indices_start_idx - witness_offset; ++i) { witness.emplace_back(0); } for (const auto& wit : proof_witnesses) { @@ -264,66 +304,140 @@ TEST(AcirProofs, TestSerializationWithRecursion) for (const auto& wit : key_witnesses) { witness.emplace_back(wit); } + witness_offset = key_indices_start_idx + key_witnesses.size(); + } - acir_format::acir_format constraint_system{ - .varnum = static_cast(witness.size() + 1), - .public_inputs = { 1 }, - .fixed_base_scalar_mul_constraints = {}, - .logic_constraints = {}, - .range_constraints = {}, - .schnorr_constraints = {}, - .ecdsa_constraints = {}, - .sha256_constraints = {}, - .blake2s_constraints = {}, - .hash_to_field_constraints = {}, - .pedersen_constraints = {}, - .compute_merkle_root_constraints = {}, - .recursion_constraints = { recursion_constraint }, - .constraints = { vk_equality_constraint }, - }; + std::vector public_inputs(output_aggregation_object.begin(), output_aggregation_object.end()); - std::vector witness_buf; - std::vector constraint_system_buf; - write(constraint_system_buf, constraint_system); - write(witness_buf, witness); - uint8_t const* pk_buf = nullptr; - acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); - std::cout << "init_proving_key\n"; - - uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); - uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); - auto env_crs = std::make_unique(); - auto verifier_crs = env_crs->get_verifier_crs(); - barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); - - auto* pippenger_ptr_base = - new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); - void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); - - std::vector g2x_buffer(128); - io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); - - uint8_t const* vk_buf = nullptr; - acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); - - uint8_t* proof_data_buf = nullptr; - size_t proof_length = acir_proofs::new_proof( - pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); - - std::cout << "new_proof\n"; - bool verified = acir_proofs::verify_proof(&g2x_buffer[0], - vk_buf, - &constraint_system_buf[0], - proof_data_buf, - static_cast(proof_length), - false); - EXPECT_EQ(verified, true); - - delete pippenger_ptr_base; - free((void*)vk_buf); - free((void*)pk_buf); - free((void*)proof_data_buf); - free((void*)proof_data_fields); - free((void*)vk_fields); - } + acir_format::acir_format constraint_system{ + .varnum = static_cast(witness.size() + 1), + .public_inputs = public_inputs, + .fixed_base_scalar_mul_constraints = {}, + .logic_constraints = {}, + .range_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_constraints = {}, + .sha256_constraints = {}, + .blake2s_constraints = {}, + .hash_to_field_constraints = {}, + .pedersen_constraints = {}, + .compute_merkle_root_constraints = {}, + .recursion_constraints = recursion_constraints, + .constraints = {}, + }; + + // NOTE: This passes, so the circuit is good with a witness but something is broken with the dummy key and dummy + // proof This NOTE was fixed by using g1 affine two for the dummy key commitments rather than affine one auto + // composer = acir_format::create_circuit_with_witness(constraint_system, witness); auto prover = + // composer.create_ultra_with_keccak_prover(); std::cout << "prover gates = " << prover.circuit_size << std::endl; + // auto proof = prover.construct_proof(); + // auto verifier = composer.create_ultra_with_keccak_verifier(); + // EXPECT_EQ(verifier.verify_proof(proof), true); + // std::cout << "done create_circuit_with_witness, composer.contains_recursive_proof: " << + // composer.contains_recursive_proof << std::endl; + + return std::make_pair(constraint_system, witness); +} + +// // Working double recursion test +TEST(AcirProofs, TestSerializationWithBasicDoubleRecursion) +{ + std::vector inner_circuits; + auto a = fetch_inner_circuit_data(); + inner_circuits.push_back(a); + + auto b = fetch_inner_circuit_data(); + inner_circuits.push_back(b); + + auto c = create_outer_circuit(inner_circuits); + + std::vector witness_buf; + std::vector constraint_system_buf; + write(constraint_system_buf, c.first); + write(witness_buf, c.second); + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), false); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); +} + +TEST(AcirProofs, TestSerializationWithFullRecursiveComposition) +{ + std::vector layer_1_circuits; + auto a_data_circuit = fetch_inner_circuit_data(); + layer_1_circuits.push_back(a_data_circuit); + + std::vector layer_2_circuits; + auto a2_circuit = fetch_inner_circuit_data(); + layer_2_circuits.push_back(a2_circuit); + + auto b_cs_and_witness = create_outer_circuit(layer_1_circuits); + auto b_circuit = fetch_recursive_circuit_data(b_cs_and_witness.first, b_cs_and_witness.second); + layer_2_circuits.push_back(b_circuit); + + // std::vector layer_3_circuits; + auto c_cs_and_witness = create_outer_circuit(layer_2_circuits); + // auto c_circuit = fetch_recursive_circuit_data(c_cs_and_witness.first, c_cs_and_witness.second); + // layer_3_circuits.push_back(c_circuit); + + std::vector witness_buf; + std::vector constraint_system_buf; + write(constraint_system_buf, c_cs_and_witness.first); + write(witness_buf, c_cs_and_witness.second); + uint8_t const* pk_buf = nullptr; + acir_proofs::init_proving_key(&constraint_system_buf[0], &pk_buf); + + uint32_t total_circuit_size = acir_proofs::get_total_circuit_size(&constraint_system_buf[0]); + uint32_t pow2_size = 1 << (numeric::get_msb(total_circuit_size) + 1); + auto env_crs = std::make_unique(); + auto verifier_crs = env_crs->get_verifier_crs(); + barretenberg::g2::affine_element g2x = verifier_crs->get_g2x(); + + auto* pippenger_ptr_base = new scalar_multiplication::Pippenger(env_load_prover_crs(pow2_size + 1), pow2_size + 1); + void* pippenger_ptr = reinterpret_cast(pippenger_ptr_base); + + std::vector g2x_buffer(128); + io::write_g2_elements_to_buffer(&g2x, (char*)(&g2x_buffer[0]), 1); + + uint8_t const* vk_buf = nullptr; + acir_proofs::init_verification_key(pippenger_ptr, &g2x_buffer[0], pk_buf, &vk_buf); + + uint8_t* proof_data_buf = nullptr; + size_t proof_length = acir_proofs::new_proof( + pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, false); + + bool verified = acir_proofs::verify_proof( + &g2x_buffer[0], vk_buf, &constraint_system_buf[0], proof_data_buf, static_cast(proof_length), false); + EXPECT_EQ(verified, true); + + delete pippenger_ptr_base; + free((void*)vk_buf); + free((void*)pk_buf); + free((void*)proof_data_buf); } diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index 52deaed30b..ed87cfd4a1 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -188,9 +188,9 @@ class ComposerBase { uint32_t get_public_input_index(const uint32_t witness_index) const { - std::cout << "inside get_public_input_index\n"; - std::cout << "witness_index: " << witness_index << std::endl; - std::cout << "public_inputs.size(): " << public_inputs.size() << std::endl; + // std::cout << "inside get_public_input_index\n"; + // std::cout << "witness_index: " << witness_index << std::endl; + // std::cout << "public_inputs.size(): " << public_inputs.size() << std::endl; bool found = false; uint32_t result = static_cast(-1); diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index 743f563770..ac553f1da1 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -258,7 +258,9 @@ std::vector verification_key::export_dummy_key_in_recursion_fo for (const auto& descriptor : polynomial_manifest.get()) { if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { - const auto element = barretenberg::g1::affine_one; + // The dummy commitment needs to be greater than one as we otherwise will get invert 0 errors for multiple + // recursive constraints + const auto element = barretenberg::g1::affine_one + barretenberg::g1::affine_one; const uint256_t x = element.x; const uint256_t y = element.y; const barretenberg::fr x_lo = x.slice(0, 136); @@ -271,6 +273,7 @@ std::vector verification_key::export_dummy_key_in_recursion_fo output.emplace_back(y_hi); } } + output.emplace_back(0); // key_hash return output; diff --git a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp index bee400fd36..2ad1aaf77a 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp @@ -185,6 +185,8 @@ template bool VerifierBase::verify P[1] = -(g1::element(PI_Z_OMEGA) * separator_challenge + PI_Z); if (key->contains_recursive_proof) { + std::cout << "key->recursive_proof_public_input_indices.size(): " + << key->recursive_proof_public_input_indices.size() << std::endl; ASSERT(key->recursive_proof_public_input_indices.size() == 16); const auto& inputs = transcript.get_field_element_vector("public_inputs"); const auto recover_fq_from_public_inputs = diff --git a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp index bcbb7b33b8..818f1f9d9a 100644 --- a/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp +++ b/cpp/src/barretenberg/stdlib/recursion/verification_key/verification_key.hpp @@ -140,7 +140,8 @@ template struct verification_key { // constants! key->contains_recursive_proof = inner_proof_contains_recursive_proof; for (size_t i = 0; i < 16; ++i) { - key->recursive_proof_public_input_indices.emplace_back(recursive_proof_public_input_indices[i]); + auto x = recursive_proof_public_input_indices[i]; + key->recursive_proof_public_input_indices.emplace_back(x); } size_t count = 22; From b434c3757ed03ae7c70b86db9b8f512c969238c6 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 16:27:20 +0000 Subject: [PATCH 03/14] cleanup and working towards supporting full nested proofs, still some bugs in acir_proofs test --- .../dsl/acir_proofs/acir_proofs.cpp | 20 ++++++++++++++++++ .../dsl/acir_proofs/acir_proofs.test.cpp | 21 ++++++++++--------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 7a9fcd4686..79c3fc9e5d 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -169,6 +169,26 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, return output_size_bytes; } +// size_t get_recursive_proof_public_input_indices(uint8_t const* g2x, +// uint8_t const* vk_buf, +// uint8_t** nested_aggregation_obj_buf) +// { +// auto crs = std::make_shared(g2x); +// plonk::verification_key_data vk_data; +// read(vk_buf, vk_data); +// auto vkey = std::make_shared(std::move(vk_data), crs); + +// std::vector nested_aggregation_object; +// for (size_t i = 0; i < 16; ++i) { +// if (vkey->recursive_proof_public_input_indices.size() > i) { +// nested_aggregation_object.emplace_back(vkey->recursive_proof_public_input_indices[i]); +// } else { +// nested_aggregation_object.emplace_back(0); +// ASSERT(vkey->contains_recursive_proof == false); +// } +// } +// } + size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index fdeb27e974..ff5910b380 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -244,16 +244,18 @@ std::pair> create_outer_circuit( const std::vector proof_witnesses = inner_circuit.proof_witnesses; const std::vector key_witnesses = inner_circuit.key_witnesses; + const bool has_nested_proof = uint32_t(key_witnesses[5]); + + std::cout << "contains_recursive_proof: " << has_nested_proof << std::endl; const size_t num_inner_public_inputs = inner_circuit.num_public_inputs; const uint32_t key_hash_start_idx = static_cast(witness_offset); const uint32_t public_input_start_idx = key_hash_start_idx + 1; // TODO test nested proofs - - const uint32_t output_aggregation_object_start_idx = - static_cast(public_input_start_idx + num_inner_public_inputs); // const uint32_t output_aggregation_object_start_idx = - // static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); + // static_cast(public_input_start_idx + num_inner_public_inputs); + const uint32_t output_aggregation_object_start_idx = + static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; const uint32_t key_indices_start_idx = static_cast(proof_indices_start_idx + proof_witnesses.size()); @@ -268,12 +270,11 @@ std::pair> create_outer_circuit( for (size_t i = 0; i < 16; ++i) { output_aggregation_object[i] = (static_cast(i + output_aggregation_object_start_idx)); } - // if (has_nested_proof) { - // std::cout << "has_nested_proof: " << has_nested_proof << std::endl; - // for (size_t i = 0; i < 16; ++i) { - // nested_aggregation_object[i] = inner_composer.recursive_proof_public_input_indices[i]; - // } - // } + if (has_nested_proof) { + for (size_t i = 6; i < 22; ++i) { + nested_aggregation_object[i] = uint32_t(key_witnesses[i]); + } + } for (size_t i = 0; i < proof_witnesses.size(); ++i) { proof_indices.emplace_back(static_cast(i + proof_indices_start_idx)); } From cd0c72642cd7ac26e0aff5f5190b0a2d43b195c0 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 17:00:27 +0000 Subject: [PATCH 04/14] full recursive composition test working in acir_proofs --- .../dsl/acir_format/acir_format.cpp | 1 - .../dsl/acir_proofs/acir_proofs.cpp | 9 ++++--- .../dsl/acir_proofs/acir_proofs.hpp | 3 ++- .../dsl/acir_proofs/acir_proofs.test.cpp | 27 +++++-------------- .../barretenberg/dsl/acir_proofs/c_bind.cpp | 5 ++-- .../barretenberg/dsl/acir_proofs/c_bind.hpp | 3 ++- 6 files changed, 19 insertions(+), 29 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index d71b3f66e3..1fcc0935bf 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -181,7 +181,6 @@ Composer create_circuit(const acir_format& constraint_system, // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { - std::cout << "recursion_constraint: " << i << std::endl; auto& constraint = constraint_system.recursion_constraints[i]; create_recursion_constraints(composer, constraint); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 79c3fc9e5d..ae2423a084 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -105,12 +105,15 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* */ size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length) + size_t proof_data_length, + size_t num_inner_public_inputs) { plonk::proof proof = { std::vector(proof_data_buf, &proof_data_buf[proof_data_length]) }; - transcript::StandardTranscript transcript( - proof.proof_data, acir_format::Composer::create_manifest(1), transcript::HashType::PlookupPedersenBlake3s, 16); + transcript::StandardTranscript transcript(proof.proof_data, + acir_format::Composer::create_manifest(num_inner_public_inputs), + transcript::HashType::PlookupPedersenBlake3s, + 16); std::vector output = transcript.export_transcript_in_recursion_format(); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index e37756a5d6..35c2d0ff15 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -14,7 +14,8 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, uint8_t** serialized_vk_hash_buf); size_t serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length); + size_t proof_data_length, + size_t num_inner_public_inputs); size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index ff5910b380..0dbb5cb3d5 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -179,8 +179,10 @@ RecursiveCircuitData fetch_recursive_circuit_data(acir_format::acir_format const uint8_t* proof_data_buf = nullptr; size_t proof_length = acir_proofs::new_proof( pippenger_ptr, &g2x_buffer[0], pk_buf, &constraint_system_buf[0], &witness_buf[0], &proof_data_buf, true); - proof_fields_size = - acir_proofs::serialize_proof_into_field_elements(proof_data_buf, &proof_data_fields, proof_length); + + auto num_public_inputs = constraint_system.public_inputs.size(); + proof_fields_size = acir_proofs::serialize_proof_into_field_elements( + proof_data_buf, &proof_data_fields, proof_length, num_public_inputs); vk_fields_size = acir_proofs::serialize_verification_key_into_field_elements(&g2x_buffer[0], vk_buf, &vk_fields, &vk_hash_buf); @@ -208,7 +210,7 @@ RecursiveCircuitData fetch_recursive_circuit_data(acir_format::acir_format const vk_hash_value = barretenberg::fr::serialize_from_buffer(vk_hash_buf); key_witnesses[vk_fields_size / 32] = vk_hash_value; - auto inner_circuit = RecursiveCircuitData{ key_witnesses, proof_witnesses, constraint_system.public_inputs.size() }; + auto inner_circuit = RecursiveCircuitData{ key_witnesses, proof_witnesses, num_public_inputs }; return inner_circuit; } @@ -246,14 +248,10 @@ std::pair> create_outer_circuit( const bool has_nested_proof = uint32_t(key_witnesses[5]); - std::cout << "contains_recursive_proof: " << has_nested_proof << std::endl; const size_t num_inner_public_inputs = inner_circuit.num_public_inputs; const uint32_t key_hash_start_idx = static_cast(witness_offset); const uint32_t public_input_start_idx = key_hash_start_idx + 1; - // TODO test nested proofs - // const uint32_t output_aggregation_object_start_idx = - // static_cast(public_input_start_idx + num_inner_public_inputs); const uint32_t output_aggregation_object_start_idx = static_cast(public_input_start_idx + num_inner_public_inputs + (has_nested_proof ? 16 : 0)); const uint32_t proof_indices_start_idx = output_aggregation_object_start_idx + 16; @@ -327,20 +325,10 @@ std::pair> create_outer_circuit( .constraints = {}, }; - // NOTE: This passes, so the circuit is good with a witness but something is broken with the dummy key and dummy - // proof This NOTE was fixed by using g1 affine two for the dummy key commitments rather than affine one auto - // composer = acir_format::create_circuit_with_witness(constraint_system, witness); auto prover = - // composer.create_ultra_with_keccak_prover(); std::cout << "prover gates = " << prover.circuit_size << std::endl; - // auto proof = prover.construct_proof(); - // auto verifier = composer.create_ultra_with_keccak_verifier(); - // EXPECT_EQ(verifier.verify_proof(proof), true); - // std::cout << "done create_circuit_with_witness, composer.contains_recursive_proof: " << - // composer.contains_recursive_proof << std::endl; - return std::make_pair(constraint_system, witness); } -// // Working double recursion test +// Working double recursion test TEST(AcirProofs, TestSerializationWithBasicDoubleRecursion) { std::vector inner_circuits; @@ -402,10 +390,7 @@ TEST(AcirProofs, TestSerializationWithFullRecursiveComposition) auto b_circuit = fetch_recursive_circuit_data(b_cs_and_witness.first, b_cs_and_witness.second); layer_2_circuits.push_back(b_circuit); - // std::vector layer_3_circuits; auto c_cs_and_witness = create_outer_circuit(layer_2_circuits); - // auto c_circuit = fetch_recursive_circuit_data(c_cs_and_witness.first, c_cs_and_witness.second); - // layer_3_circuits.push_back(c_circuit); std::vector witness_buf; std::vector constraint_system_buf; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 5ff7b29d96..91d5439058 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -45,10 +45,11 @@ WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t c } WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length) + size_t proof_data_length, + size_t num_inner_public_inputs) { return acir_proofs::serialize_proof_into_field_elements( - proof_data_buf, serialized_proof_data_buf, proof_data_length); + proof_data_buf, serialized_proof_data_buf, proof_data_length, num_inner_public_inputs); } WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 0db08d5573..0262e0f3ec 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -20,7 +20,8 @@ WASM_EXPORT size_t acir_serialize_verification_key_into_field_elements(uint8_t c uint8_t** serialized_vk_hash_buf); WASM_EXPORT size_t acir_serialize_proof_into_field_elements(uint8_t const* proof_data_buf, uint8_t** serialized_proof_data_buf, - size_t proof_data_length); + size_t proof_data_length, + size_t num_inner_public_inputs); WASM_EXPORT size_t acir_proofs_new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, From fe8cec506bf17a278428ba7583e6f0ddb045ea69 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 19:37:56 +0000 Subject: [PATCH 05/14] use two public inptus for inner circuit --- .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 2 +- cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 2d0dfc1f7a..269023b9bc 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -78,7 +78,7 @@ acir_format::Composer create_inner_circuit() acir_format::acir_format constraint_system{ .varnum = 7, - .public_inputs = { 2 }, + .public_inputs = { 2, 3 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 0dbb5cb3d5..59ba7cd92d 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -76,7 +76,7 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect constraint_system = acir_format::acir_format{ .varnum = 7, - .public_inputs = { 2 }, + .public_inputs = { 2, 3 }, .fixed_base_scalar_mul_constraints = {}, .logic_constraints = { logic_constraint }, .range_constraints = { range_a, range_b }, From ad598204b111dd17e9aeb487eb4ad70e88247a14 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 5 May 2023 20:09:09 +0000 Subject: [PATCH 06/14] delete commented out unnecessary acir proofs method --- .../dsl/acir_proofs/acir_proofs.cpp | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index ae2423a084..9449d5091e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -172,26 +172,6 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, return output_size_bytes; } -// size_t get_recursive_proof_public_input_indices(uint8_t const* g2x, -// uint8_t const* vk_buf, -// uint8_t** nested_aggregation_obj_buf) -// { -// auto crs = std::make_shared(g2x); -// plonk::verification_key_data vk_data; -// read(vk_buf, vk_data); -// auto vkey = std::make_shared(std::move(vk_data), crs); - -// std::vector nested_aggregation_object; -// for (size_t i = 0; i < 16; ++i) { -// if (vkey->recursive_proof_public_input_indices.size() > i) { -// nested_aggregation_object.emplace_back(vkey->recursive_proof_public_input_indices[i]); -// } else { -// nested_aggregation_object.emplace_back(0); -// ASSERT(vkey->contains_recursive_proof == false); -// } -// } -// } - size_t new_proof(void* pippenger, uint8_t const* g2x, uint8_t const* pk_buf, From e65950dca008fd78235182fa6d5143f31eff06b6 Mon Sep 17 00:00:00 2001 From: zac-williamson Date: Sun, 7 May 2023 17:20:39 +0200 Subject: [PATCH 07/14] made dummy transcript points unique to prevent point collisions --- .../verification_key/verification_key.cpp | 11 ++++++++--- .../barretenberg/transcript/transcript_wrappers.hpp | 9 ++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp index ac553f1da1..f99fb5aed7 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verification_key/verification_key.cpp @@ -258,9 +258,14 @@ std::vector verification_key::export_dummy_key_in_recursion_fo for (const auto& descriptor : polynomial_manifest.get()) { if (descriptor.source == PolynomialSource::SELECTOR || descriptor.source == PolynomialSource::PERMUTATION) { - // The dummy commitment needs to be greater than one as we otherwise will get invert 0 errors for multiple - // recursive constraints - const auto element = barretenberg::g1::affine_one + barretenberg::g1::affine_one; + // the std::biggroup class creates unsatisfiable constraints when identical points are added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not identical. + // And prover points should contain randomness for an honest Prover). + // This check can also trigger a runtime error due to causing 0 to be inverted. + // When creating dummy verification key points we must be mindful of the above and make sure that each + // transcript point is unique. + auto scalar = barretenberg::fr::random_element(); + const auto element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); const uint256_t x = element.x; const uint256_t y = element.y; const barretenberg::fr x_lo = x.slice(0, 136); diff --git a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp index ad849889cc..9336b9d39b 100644 --- a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp +++ b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp @@ -114,7 +114,14 @@ class StandardTranscript : public Transcript { if (manifest_element.num_bytes == 32 && manifest_element.name != "public_inputs") { fields.emplace_back(0); } else if (manifest_element.num_bytes == 64 && manifest_element.name != "public_inputs") { - const auto group_element = barretenberg::g1::affine_one; + // the std::biggroup class creates unsatisfiable constraints when identical points are + // added/subtracted. + // (when verifying zk proofs this is acceptable as we make sure verification key points are not + // identical. And prover points should contain randomness for an honest Prover). This check can + // also trigger a runtime error due to causing 0 to be inverted. When creating dummy proof + // points we must be mindful of the above and make sure that each point is unique. + auto scalar = barretenberg::fr::random_element(); + const auto group_element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar); const uint256_t x = group_element.x; const uint256_t y = group_element.y; const barretenberg::fr x_lo = x.slice(0, 136); From a523906fa87ddeb0df85b697df9116fce1a09ad3 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Fri, 12 May 2023 19:01:00 +0000 Subject: [PATCH 08/14] handle nested proofs when exporting dummy transcript, export recursive proof indices when init vkey, fix full recursive comp test in acir_proofs --- .../dsl/acir_format/recursion_constraint.cpp | 3 +- .../dsl/acir_proofs/acir_proofs.cpp | 15 ++++++++-- .../dsl/acir_proofs/acir_proofs.test.cpp | 2 +- .../plonk/proof_system/verifier/verifier.cpp | 2 -- .../transcript/transcript_wrappers.hpp | 29 +++++++++++++++++-- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 88e9f61207..2ec0c7031d 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -40,7 +40,8 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& PolynomialManifest(Composer::type), inner_proof_contains_recursive_proof); const auto manifest = Composer::create_unrolled_manifest(input.public_inputs.size()); const std::vector dummy_proof = - transcript::StandardTranscript::export_dummy_transcript_in_recursion_format(manifest); + transcript::StandardTranscript::export_dummy_transcript_in_recursion_format( + manifest, inner_proof_contains_recursive_proof); for (size_t i = 0; i < input.proof.size(); ++i) { const auto proof_field_idx = input.proof[i]; // std::cout << "proof_field_idx: " << proof_field_idx << std::endl; diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index 9449d5091e..a907890bc9 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -51,8 +51,6 @@ size_t init_proving_key(uint8_t const* constraint_system_buf, uint8_t const** pk { auto constraint_system = from_buffer(constraint_system_buf); - // constraint_system.recursion_constraints[0]. - // We know that we don't actually need any CRS to create a proving key, so just feed in a nothing. // Hacky, but, right now it needs *something*. auto crs_factory = std::make_unique(); @@ -87,6 +85,18 @@ size_t init_verification_key(void* pippenger, uint8_t const* g2x, uint8_t const* // construct the verification key so that we have the correct polynomial manifest verification_key->composer_type = proof_system::ComposerType::PLOOKUP; + // Set the recursive proof indices as this is not done in `compute_verification_key_base` + verification_key->contains_recursive_proof = proving_key->contains_recursive_proof; + for (size_t i = 0; i < 16; ++i) { + if (proving_key->recursive_proof_public_input_indices.size() > i) { + verification_key->recursive_proof_public_input_indices.emplace_back( + proving_key->recursive_proof_public_input_indices[i]); + } else { + verification_key->recursive_proof_public_input_indices.emplace_back(0); + ASSERT(verification_key->contains_recursive_proof == false); + } + } + auto buffer = to_buffer(*verification_key); auto raw_buf = (uint8_t*)malloc(buffer.size()); memcpy(raw_buf, (void*)buffer.data(), buffer.size()); @@ -147,7 +157,6 @@ size_t serialize_verification_key_into_field_elements(uint8_t const* g2x, plonk::verification_key_data vk_data; read(vk_buf, vk_data); auto vkey = std::make_shared(std::move(vk_data), crs); - std::vector output = vkey->export_key_in_recursion_format(); // NOTE: this output buffer will always have a fixed size! Maybe just precompute? diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 59ba7cd92d..9baf476dfc 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -270,7 +270,7 @@ std::pair> create_outer_circuit( } if (has_nested_proof) { for (size_t i = 6; i < 22; ++i) { - nested_aggregation_object[i] = uint32_t(key_witnesses[i]); + nested_aggregation_object[i - 6] = uint32_t(key_witnesses[i]); } } for (size_t i = 0; i < proof_witnesses.size(); ++i) { diff --git a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp index 2ad1aaf77a..bee400fd36 100644 --- a/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp +++ b/cpp/src/barretenberg/plonk/proof_system/verifier/verifier.cpp @@ -185,8 +185,6 @@ template bool VerifierBase::verify P[1] = -(g1::element(PI_Z_OMEGA) * separator_challenge + PI_Z); if (key->contains_recursive_proof) { - std::cout << "key->recursive_proof_public_input_indices.size(): " - << key->recursive_proof_public_input_indices.size() << std::endl; ASSERT(key->recursive_proof_public_input_indices.size() == 16); const auto& inputs = transcript.get_field_element_vector("public_inputs"); const auto recover_fq_from_public_inputs = diff --git a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp index 9336b9d39b..2fe0830c37 100644 --- a/cpp/src/barretenberg/transcript/transcript_wrappers.hpp +++ b/cpp/src/barretenberg/transcript/transcript_wrappers.hpp @@ -104,7 +104,8 @@ class StandardTranscript : public Transcript { * @param manifest * @return std::vector */ - static std::vector export_dummy_transcript_in_recursion_format(const Manifest& manifest) + static std::vector export_dummy_transcript_in_recursion_format( + const Manifest& manifest, const bool contains_recursive_proof) { std::vector fields; const auto num_rounds = manifest.get_num_rounds(); @@ -135,8 +136,30 @@ class StandardTranscript : public Transcript { } else { ASSERT(manifest_element.name == "public_inputs"); const size_t num_public_inputs = manifest_element.num_bytes / 32; - for (size_t j = 0; j < num_public_inputs; ++j) { - fields.emplace_back(0); + // If we have a recursive proofs the public inputs must describe an aggregation object that + // is composed of two valid G1 points on the curve. Without this conditional we will get a + // runtime error that we are attempting to invert 0. + if (contains_recursive_proof) { + ASSERT(num_public_inputs == 16); + for (size_t k = 0; k < num_public_inputs / 4; ++k) { + auto scalar = barretenberg::fr::random_element(); + const auto group_element = + barretenberg::g1::affine_element(barretenberg::g1::one * scalar); + const uint256_t x = group_element.x; + const uint256_t y = group_element.y; + const barretenberg::fr x_lo = x.slice(0, 136); + const barretenberg::fr x_hi = x.slice(136, 272); + const barretenberg::fr y_lo = y.slice(0, 136); + const barretenberg::fr y_hi = y.slice(136, 272); + fields.emplace_back(x_lo); + fields.emplace_back(x_hi); + fields.emplace_back(y_lo); + fields.emplace_back(y_hi); + } + } else { + for (size_t j = 0; j < num_public_inputs; ++j) { + fields.emplace_back(0); + } } } } From dcfcbd94266234b90592605521b9b360f7028671 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 15 May 2023 12:25:32 -0400 Subject: [PATCH 09/14] update bindings on verify_recursive_proof to accept num public inputs --- .../dsl/acir_format/recursion_constraint.cpp | 1 - .../dsl/acir_proofs/acir_proofs.cpp | 53 ++++++++++++++----- .../dsl/acir_proofs/acir_proofs.hpp | 2 +- .../barretenberg/dsl/acir_proofs/c_bind.cpp | 4 +- .../barretenberg/dsl/acir_proofs/c_bind.hpp | 2 +- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 2ec0c7031d..c8a14c49f6 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -122,7 +122,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // Assign correct witness value to the verification key hash vkey->compress().assert_equal(field_ct::from_witness_index(&composer, input.key_hash)); - // std::cout << "compressed vkey\n"; // Assign the output aggregation object to the proof public inputs (16 field elements representing two // G1 points) diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp index a907890bc9..4310342f7e 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.cpp @@ -285,18 +285,36 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf) { - // TODO: not doing anything with public_inputs_buf right now because we only have one layer of recursion - // and the previous aggregation state will be empty. When arbitrary depth recursion is available we will have to - // construct the correct input aggregation_state_ct - (void)public_inputs_buf; - (void)input_aggregation_obj_buf; + + bool inner_aggregation_all_zero = true; + std::vector aggregation_input(16); + for (size_t i = 0; i < 16; i++) { + aggregation_input[i] = barretenberg::fr::serialize_from_buffer(&input_aggregation_obj_buf[i * 32]); + inner_aggregation_all_zero &= (aggregation_input[i].is_zero()); + } acir_format::aggregation_state_ct previous_aggregation; - previous_aggregation.has_data = false; + if (!inner_aggregation_all_zero) { + std::array aggregation_elements; + for (size_t i = 0; i < 4; ++i) { + aggregation_elements[i] = acir_format::bn254::fq_ct(acir_format::field_ct(aggregation_input[4 * i]), + acir_format::field_ct(aggregation_input[4 * i + 1]), + acir_format::field_ct(aggregation_input[4 * i + 2]), + acir_format::field_ct(aggregation_input[4 * i + 3])); + aggregation_elements[i].assert_is_in_field(); + } + // If we have a previous aggregation object, assign it to `previous_aggregation` so that it is included + // in stdlib::recursion::verify_proof + previous_aggregation.P0 = acir_format::bn254::g1_ct(aggregation_elements[0], aggregation_elements[1]); + previous_aggregation.P1 = acir_format::bn254::g1_ct(aggregation_elements[2], aggregation_elements[3]); + previous_aggregation.has_data = true; + } else { + previous_aggregation.has_data = false; + } std::vector proof_fields(proof_length / 32); std::vector key_fields(vk_length / 32); @@ -309,21 +327,30 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, acir_format::Composer composer; - transcript::Manifest manifest = acir_format::Composer::create_unrolled_manifest(1); + transcript::Manifest manifest = acir_format::Composer::create_unrolled_manifest(num_public_inputs); // We currently only support RecursionConstraint where inner_proof_contains_recursive_proof = false. // We would either need a separate ACIR opcode where inner_proof_contains_recursive_proof = true, // or we need non-witness data to be provided as metadata in the ACIR opcode // recursively verify the proof - // TODO: handle inner_proof_contains_recursive_proof and recursive_proof_public_input_indices - std::shared_ptr vkey = - acir_format::verification_key_ct::from_field_pt_vector(&composer, key_fields); + std::array nested_aggregation_object = {}; + for (size_t i = 6; i < 22; ++i) { + nested_aggregation_object[i - 6] = uint32_t(key_fields[i].get_value()); + } + bool nested_aggregation_indices_all_zero = true; + for (const auto& idx : nested_aggregation_object) { + nested_aggregation_indices_all_zero &= (idx == 0); + } + const bool inner_proof_contains_recursive_proof = !nested_aggregation_indices_all_zero; + std::shared_ptr vkey = acir_format::verification_key_ct::from_field_pt_vector( + &composer, key_fields, inner_proof_contains_recursive_proof, nested_aggregation_object); vkey->program_width = acir_format::noir_recursive_settings::program_width; - acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, 1); + acir_format::Transcript_ct transcript(&composer, manifest, proof_fields, num_public_inputs); acir_format::aggregation_state_ct result = proof_system::plonk::stdlib::recursion::verify_proof_( &composer, vkey, transcript, previous_aggregation); - // just writing the output aggregation G1 elements, and no public inputs, proof witnesses, or any other data + // Just write the output aggregation G1 elements, and no public inputs, proof witnesses, or any other data + // as this should all be available elsewhere const size_t output_size_bytes = 16 * sizeof(barretenberg::fr); auto raw_buf = (uint8_t*)malloc(output_size_bytes); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp index 35c2d0ff15..cad0d1a79f 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.hpp @@ -33,7 +33,7 @@ size_t verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf); diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 91d5439058..62621d00be 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -77,7 +77,7 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf) { @@ -85,7 +85,7 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, proof_length, vk_buf, vk_length, - public_inputs_buf, + num_public_inputs, input_aggregation_obj_buf, output_aggregation_obj_buf); } diff --git a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 0262e0f3ec..452f41d1ca 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -39,7 +39,7 @@ WASM_EXPORT size_t acir_proofs_verify_recursive_proof(uint8_t const* proof_buf, uint32_t proof_length, uint8_t const* vk_buf, uint32_t vk_length, - uint8_t const* public_inputs_buf, + uint32_t num_public_inputs, uint8_t const* input_aggregation_obj_buf, uint8_t** output_aggregation_obj_buf); } From 231c0be418b17a16fed071563483088d6ecc1639 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 15 May 2023 13:15:27 -0400 Subject: [PATCH 10/14] missing acir_format field in tests --- cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp | 1 + cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp | 2 ++ .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 1 + cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 13833e0003..ad8506481a 100644 --- a/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -28,6 +28,7 @@ TEST(acir_format, test_a_single_constraint_no_pub_inputs) .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, diff --git a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 9045bab14b..57ce16d248 100644 --- a/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -88,6 +88,7 @@ TEST(ECDSASecp256k1, TestECDSAConstraintSucceed) .ecdsa_constraints = { ecdsa_constraint }, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, @@ -127,6 +128,7 @@ TEST(ECDSASecp256k1, TestECDSAConstraintFail) .ecdsa_constraints = { ecdsa_constraint }, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 269023b9bc..7551695b72 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -86,6 +86,7 @@ acir_format::Composer create_inner_circuit() .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, diff --git a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp index 9baf476dfc..196890eca6 100644 --- a/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_proofs/acir_proofs.test.cpp @@ -84,6 +84,7 @@ void create_inner_circuit(acir_format::acir_format& constraint_system, std::vect .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, @@ -318,6 +319,7 @@ std::pair> create_outer_circuit( .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, From bc6e5c73df0960952cb29f13eec1f33e13062f14 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 15 May 2023 13:28:07 -0400 Subject: [PATCH 11/14] missing one more correct acir_format struct in recursion constraint test --- .../barretenberg/dsl/acir_format/recursion_constraint.test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 7551695b72..61f08f83ef 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -210,6 +210,7 @@ acir_format::Composer create_outer_circuit(std::vector& i .ecdsa_constraints = {}, .sha256_constraints = {}, .blake2s_constraints = {}, + .keccak_constraints = {}, .hash_to_field_constraints = {}, .pedersen_constraints = {}, .compute_merkle_root_constraints = {}, From 82347ced50df4e2828ea325252c691d65306ab91 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 15 May 2023 19:13:28 +0000 Subject: [PATCH 12/14] cleanup and additional comment for recursion_constraint --- .../dsl/acir_format/recursion_constraint.cpp | 3 --- .../dsl/acir_format/recursion_constraint.hpp | 12 ++++++++++-- .../barretenberg/plonk/composer/composer_base.hpp | 4 ---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index c8a14c49f6..5dc012634e 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -44,8 +44,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& manifest, inner_proof_contains_recursive_proof); for (size_t i = 0; i < input.proof.size(); ++i) { const auto proof_field_idx = input.proof[i]; - // std::cout << "proof_field_idx: " << proof_field_idx << std::endl; - // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), // we add our dummy proof values as Composer variables. // if we DO have a valid witness assignment, we use the real witness assignment @@ -60,7 +58,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& } for (size_t i = 0; i < input.key.size(); ++i) { const auto key_field_idx = input.key[i]; - // std::cout << "key_field_idx: " << key_field_idx << std::endl; barretenberg::fr dummy_field = has_valid_witness_assignment ? composer.get_variable(key_field_idx) : dummy_key[i]; composer.assert_equal(composer.add_variable(dummy_field), key_field_idx); diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index d0b78af31b..75462b9aa4 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -24,14 +24,22 @@ namespace acir_format { * (and therefore an aggregation object is present) * @param public_input The index of the single public input * @param input_aggregation_object Witness indices of pre-existing aggregation object (if it exists) - * @param output_aggregation_object Witness indecies of the aggregation object produced by recursive verification + * @param output_aggregation_object Witness indices of the aggregation object produced by recursive verification * @param nested_aggregation_object Public input indices of an aggregation object inside the proof. * * @note If input_aggregation_object witness indices are all zero, we interpret this to mean that the inner proof does - * NOT contain + * NOT contain a previously recursively verified proof * @note nested_aggregation_object is used for cases where the proof being verified contains an aggregation object in * its public inputs! If this is the case, we record the public input locations in `nested_aggregation_object`. If the * inner proof is of a circuit that does not have a nested aggregation object, these values are all zero. + * + * To outline the interaction between the input_aggergation_object and the nested_aggregation_object take the follow + * example: If we have a circuit that verifies 2 proofs A and B, the recursion constraint for B will have an + * input_aggregation_object that points to the agg output produced by verifying A. If circuit B also verifies a proof, + * in the above example the recursion constraint for verifying B will have a nested object that describes the + * aggregation object in B’s public inputs as well as an input aggregation object that points to the object produced by + * the previous recursion constraint in the circuit (the one that verifies A) + * */ struct RecursionConstraint { static constexpr size_t AGGREGATION_OBJECT_SIZE = 16; // 16 field elements diff --git a/cpp/src/barretenberg/plonk/composer/composer_base.hpp b/cpp/src/barretenberg/plonk/composer/composer_base.hpp index 037bc125f1..b1109d2d4d 100644 --- a/cpp/src/barretenberg/plonk/composer/composer_base.hpp +++ b/cpp/src/barretenberg/plonk/composer/composer_base.hpp @@ -188,10 +188,6 @@ class ComposerBase { uint32_t get_public_input_index(const uint32_t witness_index) const { - // std::cout << "inside get_public_input_index\n"; - // std::cout << "witness_index: " << witness_index << std::endl; - // std::cout << "public_inputs.size(): " << public_inputs.size() << std::endl; - bool found = false; uint32_t result = static_cast(-1); for (size_t i = 0; i < public_inputs.size(); ++i) { From 9753e4405e19ff16f0242b0c95e1fbe47882a979 Mon Sep 17 00:00:00 2001 From: vezenovm Date: Mon, 15 May 2023 19:14:25 +0000 Subject: [PATCH 13/14] fix up comment in recursion_constraint --- .../barretenberg/dsl/acir_format/recursion_constraint.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 75462b9aa4..0a0ca53d12 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -33,10 +33,10 @@ namespace acir_format { * its public inputs! If this is the case, we record the public input locations in `nested_aggregation_object`. If the * inner proof is of a circuit that does not have a nested aggregation object, these values are all zero. * - * To outline the interaction between the input_aggergation_object and the nested_aggregation_object take the follow + * To outline the interaction between the input_aggergation_object and the nested_aggregation_object take the following * example: If we have a circuit that verifies 2 proofs A and B, the recursion constraint for B will have an - * input_aggregation_object that points to the agg output produced by verifying A. If circuit B also verifies a proof, - * in the above example the recursion constraint for verifying B will have a nested object that describes the + * input_aggregation_object that points to the aggregation output produced by verifying A. If circuit B also verifies a + * proof, in the above example the recursion constraint for verifying B will have a nested object that describes the * aggregation object in B’s public inputs as well as an input aggregation object that points to the object produced by * the previous recursion constraint in the circuit (the one that verifies A) * From bdbafc567496187eb9871de9c192cbd230d652a7 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 16 May 2023 12:41:03 -0400 Subject: [PATCH 14/14] remove unnecesary comments when we were including proof outputs as public inputs in the recursion constraint --- cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 5dc012634e..1fcf95f594 100644 --- a/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -120,10 +120,6 @@ void create_recursion_constraints(Composer& composer, const RecursionConstraint& // Assign correct witness value to the verification key hash vkey->compress().assert_equal(field_ct::from_witness_index(&composer, input.key_hash)); - // Assign the output aggregation object to the proof public inputs (16 field elements representing two - // G1 points) - // result.add_proof_outputs_as_public_inputs(); - ASSERT(result.public_inputs.size() == input.public_inputs.size()); // Assign the `public_input` field to the public input of the inner proof