From fde0ac3e96fe6e2edcdb1e6919d372e96181eda5 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 19 Dec 2023 19:33:58 +0000 Subject: [PATCH] chore: Cleanup recursion interface (#3744) This is a recreation of this PR (https://github.com/AztecProtocol/aztec-packages/pull/3528) to handle PR https://github.com/AztecProtocol/aztec-packages/pull/3729 # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [ ] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [ ] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [ ] Every change is related to the PR description. - [ ] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --------- Co-authored-by: kevaundray --- .../dsl/acir_format/acir_format.cpp | 48 +++++++++++++--- .../dsl/acir_format/recursion_constraint.cpp | 52 ++++++++++++------ .../dsl/acir_format/recursion_constraint.hpp | 15 +++-- .../acir_format/recursion_constraint.test.cpp | 20 +++++-- .../dsl/acir_proofs/acir_composer.cpp | 12 ++++ .../circuit_builder/circuit_builder_base.hpp | 4 ++ .../double_verify_proof/target/acir.gz | Bin 0 -> 1186 bytes .../double_verify_proof/target/witness.gz | Bin 0 -> 8144 bytes .../double_verify_proof/Prover.toml | 9 ++- .../double_verify_proof/src/main.nr | 31 ++++++----- 10 files changed, 137 insertions(+), 54 deletions(-) create mode 100644 noir/test_programs/acir_artifacts/double_verify_proof/target/acir.gz create mode 100644 noir/test_programs/acir_artifacts/double_verify_proof/target/witness.gz diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 64411b6b2d0..6f66e74ce9f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -1,6 +1,7 @@ #include "acir_format.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/dsl/acir_format/pedersen.hpp" +#include "barretenberg/dsl/acir_format/recursion_constraint.hpp" #include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" namespace acir_format { @@ -149,23 +150,52 @@ void build_constraints(Builder& builder, acir_format const& constraint_system, b create_block_constraints(builder, constraint, has_valid_witness_assignments); } - // Add recursion constraints // TODO(https://github.com/AztecProtocol/barretenberg/issues/817): disable these for UGH for now since we're not yet // dealing with proper recursion if constexpr (IsGoblinBuilder) { info("WARNING: this circuit contains recursion_constraints!"); } else { + // These are set and modified whenever we encounter a recursion opcode + // + // These should not be set by the caller + // TODO: Check if this is always the case. ie I won't receive a proof that will set the first + // TODO input_aggregation_object to be non-zero. + // TODO: if not, we can add input_aggregation_object to the proof too for all recursive proofs + // TODO: This might be the case for proof trees where the proofs are created on different machines + std::array current_input_aggregation_object = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + std::array current_output_aggregation_object = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // Add recursion constraints for (size_t i = 0; i < constraint_system.recursion_constraints.size(); ++i) { auto& constraint = constraint_system.recursion_constraints[i]; - create_recursion_constraints(builder, constraint, has_valid_witness_assignments); - - // 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()); - builder.set_recursive_proof(proof_output_witness_indices); + current_output_aggregation_object = create_recursion_constraints(builder, + constraint, + current_input_aggregation_object, + constraint.nested_aggregation_object, + has_valid_witness_assignments); + current_input_aggregation_object = current_output_aggregation_object; + } + + // Now that the circuit has been completely built, we add the output aggregation as public + // inputs. + if (!constraint_system.recursion_constraints.empty()) { + + // First add the output aggregation object as public inputs + // Set the indices as public inputs because they are no longer being + // created in ACIR + for (const auto& idx : current_output_aggregation_object) { + builder.set_public_input(idx); } + + // Make sure the verification key records the public input indices of the + // final recursion output. + std::vector proof_output_witness_indices(current_output_aggregation_object.begin(), + current_output_aggregation_object.end()); + builder.set_recursive_proof(proof_output_witness_indices); } } } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp index 4118c3307f5..f01d4f60f66 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -3,6 +3,7 @@ #include "barretenberg/plonk/transcript/transcript_wrappers.hpp" #include "barretenberg/stdlib/recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/recursion/verifier/verifier.hpp" +#include namespace acir_format { @@ -28,12 +29,19 @@ 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 -void create_recursion_constraints(Builder& builder, - const RecursionConstraint& input, - bool has_valid_witness_assignments) +std::array create_recursion_constraints( + Builder& builder, + const RecursionConstraint& input, + std::array input_aggregation_object, + // TODO: does this need to be a part of the recursion opcode? + // TODO: or can we figure it out from the vk? + // TODO: either way we could probably have the user explicitly provide it + // TODO: in Noir. + // Note: this is not being used in Noir at the moment + std::array nested_aggregation_object, + bool has_valid_witness_assignments) { - const auto& nested_aggregation_indices = input.nested_aggregation_object; + const auto& nested_aggregation_indices = nested_aggregation_object; bool nested_aggregation_indices_all_zero = true; for (const auto& idx : nested_aggregation_indices) { nested_aggregation_indices_all_zero &= (idx == 0); @@ -47,8 +55,12 @@ void create_recursion_constraints(Builder& builder, const std::vector dummy_key = export_dummy_key_in_recursion_format( PolynomialManifest(Builder::CIRCUIT_TYPE), inner_proof_contains_recursive_proof); const auto manifest = Composer::create_manifest(input.public_inputs.size()); - const std::vector dummy_proof = + std::vector dummy_proof = export_dummy_transcript_in_recursion_format(manifest, inner_proof_contains_recursive_proof); + + // Remove the public inputs from the dummy proof + dummy_proof.erase(dummy_proof.begin(), + dummy_proof.begin() + static_cast(input.public_inputs.size())); for (size_t i = 0; i < input.proof.size(); ++i) { const auto proof_field_idx = input.proof[i]; // if we do NOT have a witness assignment (i.e. are just building the proving/verification keys), @@ -74,7 +86,7 @@ void create_recursion_constraints(Builder& builder, // Construct an in-circuit representation of the verification key. // For now, the v-key is a circuit constant and is fixed for the circuit. // (We may need a separate recursion opcode for this to vary, or add more config witnesses to this opcode) - const auto& aggregation_input = input.input_aggregation_object; + const auto& aggregation_input = input_aggregation_object; aggregation_state_ct previous_aggregation; // If we have previously recursively verified proofs, `is_aggregation_object_nonzero = true` @@ -113,7 +125,13 @@ void create_recursion_constraints(Builder& builder, } std::vector proof_fields; - proof_fields.reserve(input.proof.size()); + // Prepend the public inputs to the proof fields because this is how the + // core barretenberg library processes proofs (with the public inputs first and not separated) + proof_fields.reserve(input.proof.size() + input.public_inputs.size()); + for (const auto& idx : input.public_inputs) { + auto field = field_ct::from_witness_index(&builder, idx); + proof_fields.emplace_back(field); + } for (const auto& idx : input.proof) { auto field = field_ct::from_witness_index(&builder, idx); proof_fields.emplace_back(field); @@ -137,12 +155,14 @@ void create_recursion_constraints(Builder& builder, result.public_inputs[i].assert_equal(field_ct::from_witness_index(&builder, 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(&builder, result.proof_witness_indices[i]); - const auto rhs = field_ct::from_witness_index(&builder, input.output_aggregation_object[i]); - lhs.assert_equal(rhs); - } + // We want to return an array, so just copy the vector into the array + ASSERT(result.proof_witness_indices.size() == RecursionConstraint::AGGREGATION_OBJECT_SIZE); + std::array resulting_output_aggregation_object; + std::copy(result.proof_witness_indices.begin(), + result.proof_witness_indices.begin() + RecursionConstraint::AGGREGATION_OBJECT_SIZE, + resulting_output_aggregation_object.begin()); + + return resulting_output_aggregation_object; } /** @@ -350,8 +370,4 @@ G1AsFields export_g1_affine_element_as_fields(const barretenberg::g1::affine_ele return G1AsFields{ x_lo, x_hi, y_lo, y_hi }; } -template void create_recursion_constraints(UltraCircuitBuilder& builder, - const RecursionConstraint& input, - bool has_valid_witness_assignments); - } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 65574c68173..345b6744cd6 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -53,17 +53,24 @@ struct RecursionConstraint { std::vector proof; std::vector public_inputs; uint32_t key_hash; + // TODO:This is now unused, but we keep it here for backwards compatibility std::array input_aggregation_object; + // TODO: This is now unused, but we keep it here for backwards compatibility std::array output_aggregation_object; + // TODO: This is currently not being used on the Noir level at all + // TODO: we don't have a way to specify that the proof we are creating contains a + // TODO: aggregation object (ie it is also verifying a proof) std::array nested_aggregation_object; friend bool operator==(RecursionConstraint const& lhs, RecursionConstraint const& rhs) = default; }; -template -void create_recursion_constraints(Builder& builder, - const RecursionConstraint& input, - bool has_valid_witness_assignments = false); +std::array create_recursion_constraints( + Builder& builder, + const RecursionConstraint& input, + std::array input_aggregation_object, + std::array nested_aggregation_object, + bool has_valid_witness_assignments = false); std::vector export_key_in_recursion_format(std::shared_ptr const& vkey); std::vector export_dummy_key_in_recursion_format(const PolynomialManifest& polynomial_manifest, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index 550bd180cc3..b80d437e6d5 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -146,7 +146,15 @@ Builder create_outer_circuit(std::vector& inner_circuits) transcript::HashType::PedersenBlake3s, 16); - const std::vector proof_witnesses = export_transcript_in_recursion_format(transcript); + std::vector proof_witnesses = export_transcript_in_recursion_format(transcript); + // - Save the public inputs so that we can set their values. + // - Then truncate them from the proof because the ACIR API expects proofs without public inputs + + std::vector inner_public_input_values( + proof_witnesses.begin(), proof_witnesses.begin() + static_cast(num_inner_public_inputs)); + proof_witnesses.erase(proof_witnesses.begin(), + proof_witnesses.begin() + static_cast(num_inner_public_inputs)); + const std::vector key_witnesses = export_key_in_recursion_format(inner_verifier.key); const uint32_t key_hash_start_idx = static_cast(witness_offset); @@ -202,14 +210,18 @@ Builder create_outer_circuit(std::vector& inner_circuits) for (const auto& wit : key_witnesses) { witness.emplace_back(wit); } + // Set the values for the inner public inputs + // Note: this is confusing, but we minus one here due to the fact that the + // witness values have not taken into account that zero is taken up by the zero_idx + for (size_t i = 0; i < num_inner_public_inputs; ++i) { + witness[inner_public_inputs[i] - 1] = inner_public_input_values[i]; + } witness_offset = key_indices_start_idx + key_witnesses.size(); circuit_idx++; } - std::vector public_inputs(output_aggregation_object.begin(), output_aggregation_object.end()); - acir_format constraint_system{ .varnum = static_cast(witness.size() + 1), - .public_inputs = public_inputs, + .public_inputs = {}, .logic_constraints = {}, .range_constraints = {}, .sha256_constraints = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp index e9b2753ca5f..92b575c0097 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/acir_composer.cpp @@ -130,6 +130,16 @@ bool AcirComposer::verify_proof(std::vector const& proof, bool is_recur // Hack. Shouldn't need to do this. 2144 is size with no public inputs. builder_.public_inputs.resize((proof.size() - 2144) / 32); + // TODO: We could get rid of this, if we made the Noir program specify whether something should be + // TODO: created with the recursive setting or not. ie: + // + // #[recursive_friendly] + // fn main() {} + // would put in the ACIR that we want this to be recursion friendly with a flag maybe and the backend + // would set the is_recursive flag to be true. + // This would eliminate the need for nargo to have a --recursive flag + // + // End result is that we may just be able to get it off of builder_, like builder_.is_recursive_friendly if (is_recursive) { auto verifier = composer.create_verifier(builder_); return verifier.verify_proof({ proof }); @@ -152,6 +162,8 @@ std::string AcirComposer::get_solidity_verifier() } /** + * TODO: We should change this to return a proof without public inputs, since that is what std::verify_proof + * TODO: takes. * @brief Takes in a proof buffer and converts into a vector of field elements. * The Recursion opcode requires the proof serialized as a vector of witnesses. * Use this method to get the witness values! diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp index 140d98b234f..12bbc8be31a 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp @@ -347,11 +347,15 @@ template class CircuitBuilderBase { for (const auto& idx : proof_output_witness_indices) { set_public_input(idx); + // Why is it adding the size of the public input instead of the idx? recursive_proof_public_input_indices.push_back((uint32_t)(public_inputs.size() - 1)); } } /** + * TODO: We can remove this and use `add_recursive_proof` once my question has been addressed + * TODO: using `add_recursive_proof` also means that we will need to remove the cde which is + * TODO: adding the public_inputs * @brief Update recursive_proof_public_input_indices with existing public inputs that represent a recursive proof * * @param proof_output_witness_indices diff --git a/noir/test_programs/acir_artifacts/double_verify_proof/target/acir.gz b/noir/test_programs/acir_artifacts/double_verify_proof/target/acir.gz new file mode 100644 index 0000000000000000000000000000000000000000..31ff852f775fd90f01e16b25100e391e964dd19d GIT binary patch literal 1186 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 pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b: [Field; 93] +) { + // Create dummy aggregation object + // It is no longer needed but we don't want to make a breaking change to the serialization format + // just yet. The object can be anything as the backend is no longer checking it anyways. + let unused_input_aggregation_object = [verification_key[0]]; + + let _ = std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), key_hash, - input_aggregation_object + unused_input_aggregation_object ); - let output_aggregation_object = std::verify_proof( + let _ = std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), key_hash, - output_aggregation_object_a + unused_input_aggregation_object ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output }