diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp index 87947d8edd6e..1513dec8a83f 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp @@ -77,6 +77,10 @@ void AztecIVC::execute_accumulation_prover(ClientCircuit& circuit, MergeProof merge_proof = goblin.prove_merge(circuit); merge_verification_queue.emplace_back(merge_proof); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Do proper aggregation with merge recursive + // verifier. + circuit.add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices(circuit)); + // Construct the prover instance for circuit auto prover_instance = std::make_shared(circuit, trace_structure); diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 135bef634034..9772d63c2ba0 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -6,6 +6,7 @@ #include "barretenberg/dsl/acir_proofs/honk_contract.hpp" #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/plonk/proof_system/proving_key/serialize.hpp" +#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp" #include "barretenberg/serialize/cbind.hpp" #include "barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" @@ -573,7 +574,7 @@ void prove_tube(const std::string& output_path) // these public inputs by turning proof into witnesses and call // set_public on each witness auto num_public_inputs = static_cast(static_cast(proof.folding_proof[1])); - for (size_t i = 0; i < num_public_inputs; i++) { + for (size_t i = 0; i < num_public_inputs - bb::AGGREGATION_OBJECT_SIZE; i++) { // We offset 3 builder->add_public_variable(proof.folding_proof[i + 3]); } @@ -581,33 +582,12 @@ void prove_tube(const std::string& output_path) verifier.verify(proof); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted from a valid - // proof. This is a workaround because we can't represent the point at infinity in biggroup yet. - AggregationObjectIndices current_aggregation_object = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf"); - fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4"); - fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38"); - fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f"); - std::vector aggregation_object_fq_values = { x0, y0, x1, y1 }; - size_t agg_obj_indices_idx = 0; - for (fq val : aggregation_object_fq_values) { - const uint256_t x = val; - std::array val_limbs = { - x.slice(0, acir_format::fq_ct::NUM_LIMB_BITS), - x.slice(acir_format::fq_ct::NUM_LIMB_BITS, acir_format::fq_ct::NUM_LIMB_BITS * 2), - x.slice(acir_format::fq_ct::NUM_LIMB_BITS * 2, acir_format::fq_ct::NUM_LIMB_BITS * 3), - x.slice(acir_format::fq_ct::NUM_LIMB_BITS * 3, stdlib::field_conversion::TOTAL_BITS) - }; - for (size_t i = 0; i < acir_format::fq_ct::NUM_LIMBS; ++i) { - uint32_t idx = builder->add_variable(val_limbs[i]); - builder->set_public_input(idx); - current_aggregation_object[agg_obj_indices_idx] = idx; - agg_obj_indices_idx++; - } - } - // Make sure the verification key records the public input indices of the - // final recursion output. - builder->set_recursive_proof(current_aggregation_object); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1069): Add aggregation to goblin recursive verifiers. + // This is currently just setting the aggregation object to the default one. + AggregationObjectIndices current_aggregation_object = + stdlib::recursion::init_default_agg_obj_indices(*builder); + + builder->add_recursive_proof(current_aggregation_object); info("num gates in tube circuit: ", builder->get_num_gates()); using Prover = UltraProver_; @@ -1346,6 +1326,7 @@ int main(int argc, char* argv[]) } std::string command = args[0]; + vinfo("bb command is: ", command); std::string bytecode_path = get_option(args, "-b", "./target/program.json"); std::string witness_path = get_option(args, "-w", "./target/witness.gz"); std::string proof_path = get_option(args, "-p", "./proofs/proof"); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/simulator_bench/simulator.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/simulator_bench/simulator.bench.cpp index 135f8b46ca07..a91a265c29a5 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/simulator_bench/simulator.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/simulator_bench/simulator.bench.cpp @@ -100,7 +100,10 @@ BENCHMARK_TEMPLATE_F(SimulatorFixture, GoblinSimulated, bb::MegaRecursiveFlavor_ for (auto _ : state) { CircuitSimulator simulator; SimulatingVerifier ultra_verifier{ &simulator, verifier_input.verification_key }; - ultra_verifier.verify_proof((verifier_input.proof)); + ultra_verifier.verify_proof(verifier_input.proof, + stdlib::recursion::init_default_aggregation_state< + CircuitSimulator, + bb::MegaRecursiveFlavor_::Curve>(simulator)); } } @@ -121,7 +124,10 @@ BENCHMARK_TEMPLATE_F(SimulatorFixture, UltraSimulated, bb::UltraRecursiveFlavor_ for (auto _ : state) { CircuitSimulator simulator; SimulatingVerifier ultra_verifier{ &simulator, verifier_input.verification_key }; - ultra_verifier.verify_proof((verifier_input.proof)); + ultra_verifier.verify_proof(verifier_input.proof, + stdlib::recursion::init_default_aggregation_state< + CircuitSimulator, + bb::UltraRecursiveFlavor_::Curve>(simulator)); } } diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 942614151dec..209cbdee0e3c 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -26,6 +26,10 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr(circuit)); + // Construct the prover instance for circuit prover_instance = std::make_shared(circuit, trace_structure); diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp index 5a977dd75bfe..ae656e6f6995 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp @@ -50,7 +50,7 @@ class ClientIVCTests : public ::testing::Test { * polynomials will bump size to next power of 2) * */ - static Builder create_mock_circuit(ClientIVC& ivc, size_t log2_num_gates = 15) + static Builder create_mock_circuit(ClientIVC& ivc, size_t log2_num_gates = 16) { Builder circuit{ ivc.goblin.op_queue }; MockCircuits::construct_arithmetic_circuit(circuit, log2_num_gates); 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 8fb66b1b5224..4837aec030e3 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/common/throw_or_abort.hpp" +#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib/primitives/field/field_conversion.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" @@ -322,12 +323,8 @@ void build_constraints(Builder& builder, info("WARNING: this circuit contains honk_recursion_constraints!"); } } else { - // These are set and modified whenever we encounter a recursion opcode - // - // These should not be set by the caller - // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): this usage of all zeros is a hack and could - // use types or enums to properly fix. - AggregationObjectIndices current_aggregation_object = {}; + AggregationObjectIndices current_aggregation_object = + stdlib::recursion::init_default_agg_obj_indices(builder); // Add recursion constraints for (size_t i = 0; i < constraint_system.honk_recursion_constraints.size(); ++i) { @@ -336,13 +333,10 @@ void build_constraints(Builder& builder, // aggregation object itself. The verifier circuit requires that the indices to a nested proof aggregation // state are a circuit constant. The user tells us they how they want these constants set by keeping the // nested aggregation object attached to the proof as public inputs. - AggregationObjectIndices nested_aggregation_object = {}; for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; ++i) { - // Set the nested aggregation object indices to witness indices from the proof - nested_aggregation_object[i] = - static_cast(constraint.proof[HonkRecursionConstraint::inner_public_input_offset + i]); // Adding the nested aggregation object to the constraint's public inputs - constraint.public_inputs.emplace_back(nested_aggregation_object[i]); + constraint.public_inputs.emplace_back( + constraint.proof[HonkRecursionConstraint::inner_public_input_offset + i]); } // Remove the aggregation object so that they can be handled as normal public inputs // in they way that the recursion constraint expects @@ -350,11 +344,8 @@ void build_constraints(Builder& builder, constraint.proof.begin() + static_cast(HonkRecursionConstraint::inner_public_input_offset + bb::AGGREGATION_OBJECT_SIZE)); - current_aggregation_object = create_honk_recursion_constraints(builder, - constraint, - current_aggregation_object, - nested_aggregation_object, - has_valid_witness_assignments); + current_aggregation_object = create_honk_recursion_constraints( + builder, constraint, current_aggregation_object, has_valid_witness_assignments); track_gate_diff(constraint_system.gates_per_opcode, constraint_system.original_opcode_indices.honk_recursion_constraints.at(i)); } @@ -375,34 +366,11 @@ void build_constraints(Builder& builder, builder.set_recursive_proof(current_aggregation_object); } else if (honk_recursion && builder.is_recursive_circuit) { // Set a default aggregation object if we don't have one. - // TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted - // from a valid proof. This is a workaround because we can't represent the point at infinity in biggroup - // yet. - fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf"); - fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4"); - - fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38"); - fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f"); - std::vector aggregation_object_fq_values = { x0, y0, x1, y1 }; - size_t agg_obj_indices_idx = 0; - for (fq val : aggregation_object_fq_values) { - const uint256_t x = val; - std::array val_limbs = { - x.slice(0, fq_ct::NUM_LIMB_BITS), - x.slice(fq_ct::NUM_LIMB_BITS, fq_ct::NUM_LIMB_BITS * 2), - x.slice(fq_ct::NUM_LIMB_BITS * 2, fq_ct::NUM_LIMB_BITS * 3), - x.slice(fq_ct::NUM_LIMB_BITS * 3, stdlib::field_conversion::TOTAL_BITS) - }; - for (size_t i = 0; i < fq_ct::NUM_LIMBS; ++i) { - uint32_t idx = builder.add_variable(val_limbs[i]); - builder.set_public_input(idx); - current_aggregation_object[agg_obj_indices_idx] = idx; - agg_obj_indices_idx++; - } - } + AggregationObjectIndices current_aggregation_object = + stdlib::recursion::init_default_agg_obj_indices(builder); // Make sure the verification key records the public input indices of the // final recursion output. - builder.set_recursive_proof(current_aggregation_object); + builder.add_recursive_proof(current_aggregation_object); } } } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp index 195f4f47eebf..da877ebb2d20 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp @@ -15,21 +15,127 @@ using field_ct = stdlib::field_t; using bn254 = stdlib::bn254; using aggregation_state_ct = bb::stdlib::recursion::aggregation_state; -std::array agg_points_from_witness_indicies(Builder& builder, - const AggregationObjectIndices& obj_witness_indices) +/** + * @brief Creates a dummy vkey and proof object. + * @details Populates the key and proof vectors with dummy values in the write_vk case when we don't have a valid + * witness. The bulk of the logic is setting up certain values correctly like the circuit size, number of public inputs, + * aggregation object, and commitments. + * + * @param builder + * @param input + * @param key_fields + * @param proof_fields + */ +void create_dummy_vkey_and_proof(Builder& builder, + const HonkRecursionConstraint& input, + std::vector& key_fields, + std::vector& proof_fields) { - std::array aggregation_elements; - for (size_t i = 0; i < 4; ++i) { - aggregation_elements[i] = - bn254::BaseField(field_ct::from_witness_index(&builder, obj_witness_indices[4 * i]), - field_ct::from_witness_index(&builder, obj_witness_indices[4 * i + 1]), - field_ct::from_witness_index(&builder, obj_witness_indices[4 * i + 2]), - field_ct::from_witness_index(&builder, obj_witness_indices[4 * i + 3])); - aggregation_elements[i].assert_is_in_field(); + using Flavor = UltraRecursiveFlavor_; + + // Set vkey->circuit_size correctly based on the proof size + size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs(); + size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs(); + assert((input.proof.size() - HonkRecursionConstraint::inner_public_input_offset - + UltraFlavor::NUM_WITNESS_ENTITIES * num_frs_comm - UltraFlavor::NUM_ALL_ENTITIES * num_frs_fr - + 2 * num_frs_comm) % + (num_frs_comm + num_frs_fr * UltraFlavor::BATCHED_RELATION_PARTIAL_LENGTH) == + 0); + // Note: this computation should always result in log_circuit_size = CONST_PROOF_SIZE_LOG_N + auto log_circuit_size = (input.proof.size() - HonkRecursionConstraint::inner_public_input_offset - + UltraFlavor::NUM_WITNESS_ENTITIES * num_frs_comm - + UltraFlavor::NUM_ALL_ENTITIES * num_frs_fr - 2 * num_frs_comm) / + (num_frs_comm + num_frs_fr * UltraFlavor::BATCHED_RELATION_PARTIAL_LENGTH); + // First key field is circuit size + builder.assert_equal(builder.add_variable(1 << log_circuit_size), key_fields[0].witness_index); + // Second key field is number of public inputs + builder.assert_equal(builder.add_variable(input.public_inputs.size()), key_fields[1].witness_index); + // Third key field is the pub inputs offset + builder.assert_equal(builder.add_variable(UltraFlavor::has_zero_row ? 1 : 0), key_fields[2].witness_index); + // Fourth key field is the whether the proof contains an aggregation object. + builder.assert_equal(builder.add_variable(1), key_fields[4].witness_index); + uint32_t offset = 4; + size_t num_inner_public_inputs = input.public_inputs.size() - bb::AGGREGATION_OBJECT_SIZE; + + // We are making the assumption that the aggregation object are behind all the inner public inputs + for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; i++) { + builder.assert_equal(builder.add_variable(num_inner_public_inputs + i), key_fields[offset].witness_index); + offset++; + } + + for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) { + auto comm = curve::BN254::AffineElement::one() * fr::random_element(); + auto frs = field_conversion::convert_to_bn254_frs(comm); + builder.assert_equal(builder.add_variable(frs[0]), key_fields[offset].witness_index); + builder.assert_equal(builder.add_variable(frs[1]), key_fields[offset + 1].witness_index); + builder.assert_equal(builder.add_variable(frs[2]), key_fields[offset + 2].witness_index); + builder.assert_equal(builder.add_variable(frs[3]), key_fields[offset + 3].witness_index); + offset += 4; + } + + offset = HonkRecursionConstraint::inner_public_input_offset; + // first 3 things + builder.assert_equal(builder.add_variable(1 << log_circuit_size), proof_fields[0].witness_index); + builder.assert_equal(builder.add_variable(input.public_inputs.size()), proof_fields[1].witness_index); + builder.assert_equal(builder.add_variable(UltraFlavor::has_zero_row ? 1 : 0), proof_fields[2].witness_index); + + // the inner public inputs + for (size_t i = 0; i < num_inner_public_inputs; i++) { + builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index); + offset++; + } + // The aggregation object + AggregationObjectIndices agg_obj = stdlib::recursion::init_default_agg_obj_indices(builder); + for (auto idx : agg_obj) { + builder.assert_equal(idx, proof_fields[offset].witness_index); + offset++; + } + + // first 7 commitments + for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; i++) { + auto comm = curve::BN254::AffineElement::one() * fr::random_element(); + auto frs = field_conversion::convert_to_bn254_frs(comm); + builder.assert_equal(builder.add_variable(frs[0]), proof_fields[offset].witness_index); + builder.assert_equal(builder.add_variable(frs[1]), proof_fields[offset + 1].witness_index); + builder.assert_equal(builder.add_variable(frs[2]), proof_fields[offset + 2].witness_index); + builder.assert_equal(builder.add_variable(frs[3]), proof_fields[offset + 3].witness_index); + offset += 4; + } + + // now the univariates, which can just be 0s (7*CONST_PROOF_SIZE_LOG_N Frs) + for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N * Flavor::BATCHED_RELATION_PARTIAL_LENGTH; i++) { + builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index); + offset++; + } + + // now the sumcheck evalutions, which is just 43 0s + for (size_t i = 0; i < Flavor::NUM_ALL_ENTITIES; i++) { + builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index); + offset++; + } + + // now the zeromorph commitments, which are CONST_PROOF_SIZE_LOG_N comms + for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { + auto comm = curve::BN254::AffineElement::one() * fr::random_element(); + auto frs = field_conversion::convert_to_bn254_frs(comm); + builder.assert_equal(builder.add_variable(frs[0]), proof_fields[offset].witness_index); + builder.assert_equal(builder.add_variable(frs[1]), proof_fields[offset + 1].witness_index); + builder.assert_equal(builder.add_variable(frs[2]), proof_fields[offset + 2].witness_index); + builder.assert_equal(builder.add_variable(frs[3]), proof_fields[offset + 3].witness_index); + offset += 4; } - return { bn254::Group(aggregation_elements[0], aggregation_elements[1]), - bn254::Group(aggregation_elements[2], aggregation_elements[3]) }; + // lastly the 2 commitments + for (size_t i = 0; i < 2; i++) { + auto comm = curve::BN254::AffineElement::one() * fr::random_element(); + auto frs = field_conversion::convert_to_bn254_frs(comm); + builder.assert_equal(builder.add_variable(frs[0]), proof_fields[offset].witness_index); + builder.assert_equal(builder.add_variable(frs[1]), proof_fields[offset + 1].witness_index); + builder.assert_equal(builder.add_variable(frs[2]), proof_fields[offset + 2].witness_index); + builder.assert_equal(builder.add_variable(frs[3]), proof_fields[offset + 3].witness_index); + offset += 4; + } + ASSERT(offset == input.proof.size() + input.public_inputs.size()); } /** @@ -37,8 +143,7 @@ std::array agg_points_from_witness_indicies(Builder& builder, * * @param builder * @param input - * @param input_aggregation_object. The aggregation object coming from previous Honk recursion constraints. - * @param nested_aggregation_object. The aggregation object coming from the inner proof. + * @param input_aggregation_object_indices. The aggregation object coming from previous Honk recursion constraints. * @param has_valid_witness_assignment. Do we have witnesses or are we just generating keys? * * @note We currently only support HonkRecursionConstraint where inner_proof_contains_recursive_proof = false. @@ -47,56 +152,16 @@ std::array agg_points_from_witness_indicies(Builder& builder, */ AggregationObjectIndices create_honk_recursion_constraints(Builder& builder, const HonkRecursionConstraint& input, - AggregationObjectIndices input_aggregation_object, - AggregationObjectIndices nested_aggregation_object, + AggregationObjectIndices input_aggregation_object_indices, bool has_valid_witness_assignments) { using Flavor = UltraRecursiveFlavor_; using RecursiveVerificationKey = Flavor::VerificationKey; using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1059): Handle aggregation - - static_cast(input_aggregation_object); - static_cast(nested_aggregation_object); - // Construct aggregation points from the nested aggregation witness indices - // std::array nested_aggregation_points = - // agg_points_from_witness_indicies(builder, nested_aggregation_object); - // 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_aggregation_object; - // aggregation_state_ct cur_aggregation_object; - // cur_aggregation_object.P0 = nested_aggregation_points[0]; - // cur_aggregation_object.P1 = nested_aggregation_points[1]; - // cur_aggregation_object.has_data = true; // the nested aggregation object always exists - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. - // field_ct recursion_separator = bb::stdlib::witness_t(&builder, 2); - - // If we have previously recursively verified proofs, `previous_aggregation_object_nonzero = true` - // For now this is a complile-time constant i.e. whether this is true/false is fixed for the circuit! - // bool previous_aggregation_indices_all_zero = true; - // for (const auto& idx : aggregation_input) { - // previous_aggregation_indices_all_zero &= (idx == 0); - // } - - // // Aggregate the aggregation object if it exists. It exists if we have previously verified proofs, i.e. if this - // is - // // not the first recursion constraint. - // if (!previous_aggregation_indices_all_zero) { - // std::array inner_agg_points = agg_points_from_witness_indicies(builder, aggregation_input); - // // If we have a previous aggregation object, aggregate it into the current aggregation object. - // // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): Verify that using challenge and challenge - // // squared is safe. - // cur_aggregation_object.P0 += inner_agg_points[0] * recursion_separator; - // cur_aggregation_object.P1 += inner_agg_points[1] * recursion_separator; - // recursion_separator = - // recursion_separator * - // recursion_separator; // update the challenge to be challenge squared for the next aggregation - // } - std::vector key_fields; key_fields.reserve(input.key.size()); for (const auto& idx : input.key) { @@ -121,137 +186,21 @@ AggregationObjectIndices create_honk_recursion_constraints(Builder& builder, } } } - + // Populate the key fields and proof fields with dummy values to prevent issues (usually with points not being on + // the curve). if (!has_valid_witness_assignments) { - // Set vkey->circuit_size correctly based on the proof size - size_t num_frs_comm = bb::field_conversion::calc_num_bn254_frs(); - size_t num_frs_fr = bb::field_conversion::calc_num_bn254_frs(); - assert((input.proof.size() - HonkRecursionConstraint::inner_public_input_offset - - UltraFlavor::NUM_WITNESS_ENTITIES * num_frs_comm - UltraFlavor::NUM_ALL_ENTITIES * num_frs_fr - - 2 * num_frs_comm) % - (num_frs_comm + num_frs_fr * UltraFlavor::BATCHED_RELATION_PARTIAL_LENGTH) == - 0); - // Note: this computation should always result in log_circuit_size = CONST_PROOF_SIZE_LOG_N - auto log_circuit_size = (input.proof.size() - HonkRecursionConstraint::inner_public_input_offset - - UltraFlavor::NUM_WITNESS_ENTITIES * num_frs_comm - - UltraFlavor::NUM_ALL_ENTITIES * num_frs_fr - 2 * num_frs_comm) / - (num_frs_comm + num_frs_fr * UltraFlavor::BATCHED_RELATION_PARTIAL_LENGTH); - builder.assert_equal(builder.add_variable(1 << log_circuit_size), key_fields[0].witness_index); - builder.assert_equal(builder.add_variable(input.public_inputs.size()), key_fields[1].witness_index); - builder.assert_equal(builder.add_variable(UltraFlavor::has_zero_row ? 1 : 0), key_fields[2].witness_index); - builder.assert_equal(builder.add_variable(0), key_fields[4].witness_index); - uint32_t offset = 4; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1059): Properly set this to a default agg obj. - for (size_t i = 0; i < bb::AGGREGATION_OBJECT_SIZE; i++) { - builder.assert_equal(builder.add_variable(0), key_fields[offset].witness_index); - offset++; - } - - for (size_t i = 0; i < Flavor::NUM_PRECOMPUTED_ENTITIES; ++i) { - auto comm = curve::BN254::AffineElement::one() * fr::random_element(); - auto frs = field_conversion::convert_to_bn254_frs(comm); - builder.assert_equal(builder.add_variable(frs[0]), key_fields[offset].witness_index); - builder.assert_equal(builder.add_variable(frs[1]), key_fields[offset + 1].witness_index); - builder.assert_equal(builder.add_variable(frs[2]), key_fields[offset + 2].witness_index); - builder.assert_equal(builder.add_variable(frs[3]), key_fields[offset + 3].witness_index); - offset += 4; - } - - offset = HonkRecursionConstraint::inner_public_input_offset; - // first 3 things - builder.assert_equal(builder.add_variable(1 << log_circuit_size), proof_fields[0].witness_index); - builder.assert_equal(builder.add_variable(input.public_inputs.size()), proof_fields[1].witness_index); - builder.assert_equal(builder.add_variable(UltraFlavor::has_zero_row ? 1 : 0), proof_fields[2].witness_index); - - // the public inputs - for (size_t i = 0; i < input.public_inputs.size(); i++) { - builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index); - offset++; - } - - // first 7 commitments - for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; i++) { - auto comm = curve::BN254::AffineElement::one() * fr::random_element(); - auto frs = field_conversion::convert_to_bn254_frs(comm); - builder.assert_equal(builder.add_variable(frs[0]), proof_fields[offset].witness_index); - builder.assert_equal(builder.add_variable(frs[1]), proof_fields[offset + 1].witness_index); - builder.assert_equal(builder.add_variable(frs[2]), proof_fields[offset + 2].witness_index); - builder.assert_equal(builder.add_variable(frs[3]), proof_fields[offset + 3].witness_index); - offset += 4; - } - - // now the univariates, which can just be 0s (7*CONST_PROOF_SIZE_LOG_N Frs) - for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N * Flavor::BATCHED_RELATION_PARTIAL_LENGTH; i++) { - builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index); - offset++; - } - - // now the sumcheck evalutions, which is just 43 0s - for (size_t i = 0; i < Flavor::NUM_ALL_ENTITIES; i++) { - builder.assert_equal(builder.add_variable(fr::random_element()), proof_fields[offset].witness_index); - offset++; - } - - // now the zeromorph commitments, which are CONST_PROOF_SIZE_LOG_N comms - for (size_t i = 0; i < CONST_PROOF_SIZE_LOG_N; i++) { - auto comm = curve::BN254::AffineElement::one() * fr::random_element(); - auto frs = field_conversion::convert_to_bn254_frs(comm); - builder.assert_equal(builder.add_variable(frs[0]), proof_fields[offset].witness_index); - builder.assert_equal(builder.add_variable(frs[1]), proof_fields[offset + 1].witness_index); - builder.assert_equal(builder.add_variable(frs[2]), proof_fields[offset + 2].witness_index); - builder.assert_equal(builder.add_variable(frs[3]), proof_fields[offset + 3].witness_index); - offset += 4; - } - - // lastly the 2 commitments - for (size_t i = 0; i < 2; i++) { - auto comm = curve::BN254::AffineElement::one() * fr::random_element(); - auto frs = field_conversion::convert_to_bn254_frs(comm); - builder.assert_equal(builder.add_variable(frs[0]), proof_fields[offset].witness_index); - builder.assert_equal(builder.add_variable(frs[1]), proof_fields[offset + 1].witness_index); - builder.assert_equal(builder.add_variable(frs[2]), proof_fields[offset + 2].witness_index); - builder.assert_equal(builder.add_variable(frs[3]), proof_fields[offset + 3].witness_index); - offset += 4; - } - ASSERT(offset == input.proof.size() + input.public_inputs.size()); + create_dummy_vkey_and_proof(builder, input, key_fields, proof_fields); } // Recursively verify the proof auto vkey = std::make_shared(builder, key_fields); RecursiveVerifier verifier(&builder, vkey); - std::array pairing_points = verifier.verify_proof(proof_fields); - - // Aggregate the current aggregation object with these pairing points from verify_proof - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1059): Handle aggregation - aggregation_state_ct cur_aggregation_object; - cur_aggregation_object.P0 = pairing_points[0]; // * recursion_separator; - cur_aggregation_object.P1 = pairing_points[1]; // * recursion_separator; - - AggregationObjectIndices proof_witness_indices = { - cur_aggregation_object.P0.x.binary_basis_limbs[0].element.normalize().witness_index, - cur_aggregation_object.P0.x.binary_basis_limbs[1].element.normalize().witness_index, - cur_aggregation_object.P0.x.binary_basis_limbs[2].element.normalize().witness_index, - cur_aggregation_object.P0.x.binary_basis_limbs[3].element.normalize().witness_index, - cur_aggregation_object.P0.y.binary_basis_limbs[0].element.normalize().witness_index, - cur_aggregation_object.P0.y.binary_basis_limbs[1].element.normalize().witness_index, - cur_aggregation_object.P0.y.binary_basis_limbs[2].element.normalize().witness_index, - cur_aggregation_object.P0.y.binary_basis_limbs[3].element.normalize().witness_index, - cur_aggregation_object.P1.x.binary_basis_limbs[0].element.normalize().witness_index, - cur_aggregation_object.P1.x.binary_basis_limbs[1].element.normalize().witness_index, - cur_aggregation_object.P1.x.binary_basis_limbs[2].element.normalize().witness_index, - cur_aggregation_object.P1.x.binary_basis_limbs[3].element.normalize().witness_index, - cur_aggregation_object.P1.y.binary_basis_limbs[0].element.normalize().witness_index, - cur_aggregation_object.P1.y.binary_basis_limbs[1].element.normalize().witness_index, - cur_aggregation_object.P1.y.binary_basis_limbs[2].element.normalize().witness_index, - cur_aggregation_object.P1.y.binary_basis_limbs[3].element.normalize().witness_index, - }; - auto result = cur_aggregation_object; - result.proof_witness_indices = proof_witness_indices; - + aggregation_state_ct input_agg_obj = bb::stdlib::recursion::convert_witness_indices_to_agg_obj( + builder, input_aggregation_object_indices); + aggregation_state_ct output_agg_object = verifier.verify_proof(proof_fields, input_agg_obj); // TODO(https://github.com/AztecProtocol/barretenberg/issues/996): investigate whether assert_equal on public inputs // is important, like what the plonk recursion constraint does. - ASSERT(result.proof_witness_indices.size() == bb::AGGREGATION_OBJECT_SIZE); - return result.proof_witness_indices; + return output_agg_object.get_witness_indices(); } } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp index 6186fcde26f5..96736f339e0f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.hpp @@ -6,7 +6,6 @@ namespace acir_format { using Builder = bb::UltraCircuitBuilder; using namespace bb; -using fq_ct = stdlib::bigfield; /** * @brief HonkRecursionConstraint struct contains information required to recursively verify a proof! @@ -58,7 +57,6 @@ struct HonkRecursionConstraint { AggregationObjectIndices create_honk_recursion_constraints(Builder& builder, const HonkRecursionConstraint& input, AggregationObjectIndices input_aggregation_object, - AggregationObjectIndices nested_aggregation_object, bool has_valid_witness_assignments = false); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp index b87bd4c9e398..5f89eb0fccd9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp @@ -293,6 +293,24 @@ class AcirHonkRecursionConstraint : public ::testing::Test { static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } }; +TEST_F(AcirHonkRecursionConstraint, TestBasicSingleHonkRecursionConstraint) +{ + std::vector layer_1_circuits; + layer_1_circuits.push_back(create_inner_circuit()); + + auto layer_2_circuit = create_outer_circuit(layer_1_circuits); + + info("circuit gates = ", layer_2_circuit.get_num_gates()); + + auto instance = std::make_shared(layer_2_circuit); + Prover prover(instance); + info("prover gates = ", instance->proving_key.circuit_size); + auto proof = prover.construct_proof(); + auto verification_key = std::make_shared(instance->proving_key); + Verifier verifier(verification_key); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + TEST_F(AcirHonkRecursionConstraint, TestBasicDoubleHonkRecursionConstraints) { std::vector layer_1_circuits; 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 f5c91525a1fd..f0f1fe1bff22 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.cpp @@ -164,21 +164,7 @@ AggregationObjectIndices create_recursion_constraints(Builder& builder, // Assign correct witness value to the verification key hash vkey->hash().assert_equal(field_ct::from_witness_index(&builder, input.key_hash)); - ASSERT(result.public_inputs.size() == input.public_inputs.size()); - - // Assign the `public_input` field to the public input of the inner proof - for (size_t i = 0; i < input.public_inputs.size(); ++i) { - result.public_inputs[i].assert_equal(field_ct::from_witness_index(&builder, input.public_inputs[i])); - } - - // We want to return an array, so just copy the vector into the array - ASSERT(result.proof_witness_indices.size() == bb::AGGREGATION_OBJECT_SIZE); - AggregationObjectIndices resulting_output_aggregation_object; - std::copy(result.proof_witness_indices.begin(), - result.proof_witness_indices.begin() + bb::AGGREGATION_OBJECT_SIZE, - resulting_output_aggregation_object.begin()); - - return resulting_output_aggregation_object; + return result.get_witness_indices(); } /** diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin_recursion.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/goblin_recursion.test.cpp index 760575a687c5..a344536b1362 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin_recursion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin_recursion.test.cpp @@ -53,6 +53,7 @@ TEST_F(GoblinRecursionTests, Vanilla) MockCircuits::construct_arithmetic_circuit(function_circuit, /*target_log2_dyadic_size=*/8); MockCircuits::construct_goblin_ecc_op_circuit(function_circuit); goblin.merge(function_circuit); + function_circuit.add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices(function_circuit)); auto function_accum = construct_accumulator(function_circuit); // Construct and accumulate the mock kernel circuit (no kernel accum in first round) @@ -61,6 +62,7 @@ TEST_F(GoblinRecursionTests, Vanilla) { function_accum.proof, function_accum.verification_key }, { kernel_accum.proof, kernel_accum.verification_key }); goblin.merge(kernel_circuit); + kernel_circuit.add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices(kernel_circuit)); kernel_accum = construct_accumulator(kernel_circuit); } diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index e64847a7a0ec..fa4c5bc730a5 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -196,12 +196,16 @@ class GoblinMockCircuits { { // Execute recursive aggregation of function proof RecursiveVerifier verifier1{ &builder, function_accum.verification_key }; - verifier1.verify_proof(function_accum.proof); + verifier1.verify_proof( + function_accum.proof, + stdlib::recursion::init_default_aggregation_state(builder)); // Execute recursive aggregation of previous kernel proof if one exists if (!prev_kernel_accum.proof.empty()) { RecursiveVerifier verifier2{ &builder, prev_kernel_accum.verification_key }; - verifier2.verify_proof(prev_kernel_accum.proof); + verifier2.verify_proof( + prev_kernel_accum.proof, + stdlib::recursion::init_default_aggregation_state(builder)); } } }; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp index 4be2ca259603..f0394c526e95 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/client_ivc_recursive_verifier.test.cpp @@ -39,6 +39,7 @@ class ClientIVCRecursionTests : public testing::Test { for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { Builder circuit{ ivc.goblin.op_queue }; GoblinMockCircuits::construct_mock_function_circuit(circuit); + circuit.add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices(circuit)); ivc.accumulate(circuit); } @@ -95,4 +96,43 @@ TEST_F(ClientIVCRecursionTests, Basic) EXPECT_TRUE(CircuitChecker::check(*builder)); } +TEST_F(ClientIVCRecursionTests, ClientTubeBase) +{ + // Generate a genuine ClientIVC prover output + ClientIVC ivc; + auto [proof, verifier_input] = construct_client_ivc_prover_output(ivc); + + // Construct the ClientIVC recursive verifier + auto tube_builder = std::make_shared(); + ClientIVCVerifier verifier{ tube_builder, verifier_input }; + + // Generate the recursive verification circuit + verifier.verify(proof); + + tube_builder->add_recursive_proof(stdlib::recursion::init_default_agg_obj_indices(*tube_builder)); + + info("ClientIVC Recursive Verifier: num prefinalized gates = ", tube_builder->num_gates); + + EXPECT_EQ(tube_builder->failed(), false) << tube_builder->err(); + + // EXPECT_TRUE(CircuitChecker::check(*tube_builder)); + + // Construct and verify a proof for the ClientIVC Recursive Verifier circuit + auto inner_instance = std::make_shared>(*tube_builder); + auto vk = std::make_shared(inner_instance->proving_key); + UltraProver_ tube_prover{ inner_instance }; + auto tube_proof = tube_prover.construct_proof(); + + Builder base_builder; + UltraRecursiveVerifier_> base_verifier{ &base_builder, vk }; + base_verifier.verify_proof( + tube_proof, + stdlib::recursion::init_default_aggregation_state::Curve>( + base_builder)); + info("UH Recursive Verifier: num prefinalized gates = ", base_builder.num_gates); + + EXPECT_EQ(base_builder.failed(), false) << base_builder.err(); + EXPECT_TRUE(CircuitChecker::check(base_builder)); +} + } // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp index 88ae06482220..de5111c83c35 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.cpp @@ -21,22 +21,23 @@ UltraRecursiveVerifier_::UltraRecursiveVerifier_(Builder* builder, const /** * @brief This function constructs a recursive verifier circuit for a native Ultra Honk proof of a given flavor. - * + * @return Output aggregation object */ template -std::array UltraRecursiveVerifier_::verify_proof(const HonkProof& proof) +UltraRecursiveVerifier_::AggregationObject UltraRecursiveVerifier_::verify_proof( + const HonkProof& proof, aggregation_state agg_obj) { StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); - return verify_proof(stdlib_proof); + return verify_proof(stdlib_proof, agg_obj); } /** * @brief This function constructs a recursive verifier circuit for a native Ultra Honk proof of a given flavor. - * + * @return Output aggregation object */ template -std::array UltraRecursiveVerifier_::verify_proof( - const StdlibProof& proof) +UltraRecursiveVerifier_::AggregationObject UltraRecursiveVerifier_::verify_proof( + const StdlibProof& proof, aggregation_state agg_obj) { using Sumcheck = ::bb::SumcheckVerifier; using PCS = typename Flavor::PCS; @@ -47,6 +48,7 @@ std::array UltraRecursiveVerifier_::ve using RelationParams = ::bb::RelationParameters; using Transcript = typename Flavor::Transcript; + info("in honk recursive verifier"); transcript = std::make_shared(proof); RelationParams relation_parameters; @@ -67,6 +69,30 @@ std::array UltraRecursiveVerifier_::ve for (size_t i = 0; i < key->num_public_inputs; ++i) { public_inputs.emplace_back(transcript->template receive_from_prover("public_input_" + std::to_string(i))); } + // Parse out the aggregation object using the key->recursive_proof_public_inputs_indices + aggregation_state nested_agg_obj; + size_t idx = 0; + std::array nested_pairing_points; + for (size_t i = 0; i < 2; i++) { + std::array base_field_vals; + for (size_t j = 0; j < 2; j++) { + std::array bigfield_limbs; + for (size_t k = 0; k < 4; k++) { + bigfield_limbs[k] = public_inputs[key->recursive_proof_public_input_indices[idx]]; + idx++; + } + base_field_vals[j] = + typename Curve::BaseField(bigfield_limbs[0], bigfield_limbs[1], bigfield_limbs[2], bigfield_limbs[3]); + } + nested_pairing_points[i] = typename Curve::Group(base_field_vals[0], base_field_vals[1]); + } + + nested_agg_obj.P0 = nested_pairing_points[0]; + nested_agg_obj.P1 = nested_pairing_points[1]; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate this challenge properly. + typename Curve::ScalarField recursion_separator = + Curve::ScalarField::from_witness_index(builder, builder->add_variable(42)); + agg_obj.aggregate(nested_agg_obj, recursion_separator); // Get commitments to first three wire polynomials commitments.w_l = transcript->template receive_from_prover(commitment_labels.w_l); @@ -134,9 +160,9 @@ std::array UltraRecursiveVerifier_::ve alpha[idx] = transcript->template get_challenge("alpha_" + std::to_string(idx)); } - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1041): Once hashing produces constraints for Ultra in - // the transcript, a fixed number of gate_challenges must be generated by the prover/verifier in order to achieve a - // verification circuit that is independent of proof size. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1041): Once hashing produces constraints for Ultra + // in the transcript, a fixed number of gate_challenges must be generated by the prover/verifier in order to + // achieve a verification circuit that is independent of proof size. auto gate_challenges = std::vector(log_circuit_size); for (size_t idx = 0; idx < log_circuit_size; idx++) { gate_challenges[idx] = transcript->template get_challenge("Sumcheck:gate_challenge_" + std::to_string(idx)); @@ -155,7 +181,12 @@ std::array UltraRecursiveVerifier_::ve transcript); auto pairing_points = PCS::reduce_verify(opening_claim, transcript); - return pairing_points; + pairing_points[0] = pairing_points[0].normalize(); + pairing_points[1] = pairing_points[1].normalize(); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/995): generate recursion separator challenge + // properly. + agg_obj.aggregate(pairing_points, recursion_separator); + return agg_obj; } template class UltraRecursiveVerifier_>; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.hpp index 1994da14b6db..94ede01e462b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/ultra_recursive_verifier.hpp @@ -1,6 +1,7 @@ #pragma once #include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/stdlib/honk_recursion/transcript/transcript.hpp" +#include "barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp" #include "barretenberg/stdlib_circuit_builders/mega_recursive_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_recursive_flavor.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -16,17 +17,16 @@ template class UltraRecursiveVerifier_ { using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; using Builder = typename Flavor::CircuitBuilder; using RelationSeparator = typename Flavor::RelationSeparator; - using PairingPoints = std::array; + using AggregationObject = aggregation_state; using Transcript = bb::BaseTranscript>; explicit UltraRecursiveVerifier_(Builder* builder, const std::shared_ptr& native_verifier_key); explicit UltraRecursiveVerifier_(Builder* builder, const std::shared_ptr& vkey); - // TODO(luke): Eventually this will return something like aggregation_state but I'm simplifying for now until we - // determine the exact interface. Simply returns the two pairing points. - PairingPoints verify_proof(const HonkProof& proof); - PairingPoints verify_proof(const StdlibProof& proof); + AggregationObject verify_proof(const HonkProof& proof, aggregation_state previous_output); + AggregationObject verify_proof(const StdlibProof& proof, + aggregation_state previous_output); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp index c1d2acecf794..c072ad013ee0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/verifier.test.cpp @@ -69,7 +69,8 @@ template class RecursiveVerifierTest : public testing builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); } - + AggregationObjectIndices agg_obj_indices = stdlib::recursion::init_default_agg_obj_indices(builder); + builder.add_recursive_proof(agg_obj_indices); return builder; }; @@ -131,7 +132,9 @@ template class RecursiveVerifierTest : public testing // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; RecursiveVerifier verifier{ &outer_circuit, verification_key }; - [[maybe_unused]] auto pairing_points = verifier.verify_proof(inner_proof); + [[maybe_unused]] auto pairing_points = verifier.verify_proof( + inner_proof, + init_default_aggregation_state(outer_circuit)); return outer_circuit.blocks; }; @@ -180,7 +183,9 @@ template class RecursiveVerifierTest : public testing // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; RecursiveVerifier verifier{ &outer_circuit, verification_key }; - auto pairing_points = verifier.verify_proof(inner_proof); + aggregation_state agg_obj = + init_default_aggregation_state(outer_circuit); + auto pairing_points = verifier.verify_proof(inner_proof, agg_obj); info("Recursive Verifier: num gates = ", outer_circuit.num_gates); // Check for a failure flag in the recursive verifier circuit @@ -190,8 +195,12 @@ template class RecursiveVerifierTest : public testing // verifier and check that the result agrees. InnerVerifier native_verifier(verification_key); auto native_result = native_verifier.verify_proof(inner_proof); - auto recursive_result = native_verifier.key->pcs_verification_key->pairing_check(pairing_points[0].get_value(), - pairing_points[1].get_value()); + using VerifierCommitmentKey = typename InnerFlavor::VerifierCommitmentKey; + auto pcs_verification_key = std::make_shared(); + bool result = pcs_verification_key->pairing_check(pairing_points.P0.get_value(), pairing_points.P1.get_value()); + info("input pairing points result: ", result); + auto recursive_result = native_verifier.key->pcs_verification_key->pairing_check(pairing_points.P0.get_value(), + pairing_points.P1.get_value()); EXPECT_EQ(recursive_result, native_result); // Check 2: Ensure that the underlying native and recursive verification algorithms agree by ensuring @@ -243,7 +252,8 @@ template class RecursiveVerifierTest : public testing // Create a recursive verification circuit for the proof of the inner circuit OuterBuilder outer_circuit; RecursiveVerifier verifier{ &outer_circuit, inner_verification_key }; - verifier.verify_proof(inner_proof); + verifier.verify_proof( + inner_proof, init_default_aggregation_state(outer_circuit)); // We expect the circuit check to fail due to the bad proof EXPECT_FALSE(CircuitChecker::check(outer_circuit)); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp index 38f6eedfade0..52bbdd10dab1 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/aggregation_state/aggregation_state.hpp @@ -1,54 +1,41 @@ #pragma once #include "../../primitives/field/field.hpp" #include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/plonk_honk_shared/types/aggregation_object_type.hpp" namespace bb::stdlib::recursion { /** * Aggregation state contains the following: * (P0, P1): the aggregated elements storing the verification results of proofs in the past - * proof_witness_indices: witness indices that point to (P0, P1) - * public_inputs: the public inputs of the inner proof, these become the private inputs to the recursive circuit * has_data: indicates if this aggregation state contain past (P0, P1) */ template struct aggregation_state { typename Curve::Group P0; typename Curve::Group P1; - // The public inputs of the inner circuit are now private inputs of the outer circuit! - std::vector public_inputs; - AggregationObjectIndices proof_witness_indices; bool has_data = false; typename Curve::bool_ct operator==(aggregation_state const& other) const { - return P0 == other.P0 && P1 == other.P1 && public_inputs == other.public_inputs && - proof_witness_indices == other.proof_witness_indices; - // has_data == other.has_data; can't compare as native + return P0 == other.P0 && P1 == other.P1; }; - /** - * @brief TODO(@dbanks12 please migrate A3 circuits to using `assign_object_to_proof_outputs`. Much safer to not - * independently track `proof_witness_indices` and whether object has been assigned to public inputs) - * - */ - void add_proof_outputs_as_public_inputs() + void aggregate(aggregation_state const& other, typename Curve::ScalarField recursion_separator) { - auto* context = P0.get_context(); - context->add_recursive_proof(proof_witness_indices); + P0 += other.P0 * recursion_separator; + P1 += other.P1 * recursion_separator; } - void assign_object_to_proof_outputs() + void aggregate(std::array const& other, typename Curve::ScalarField recursion_separator) { - if (proof_witness_indices.size() == 0) { - std::cerr << "warning. calling `assign_object_to_proof_outputs`, but aggregation object already has " - "assigned proof outputs to public inputs."; - return; - } + P0 += other[0] * recursion_separator; + P1 += other[1] * recursion_separator; + } - P0 = P0.reduce(); - P1 = P1.reduce(); - proof_witness_indices = { + AggregationObjectIndices get_witness_indices() + { + AggregationObjectIndices witness_indices = { P0.x.binary_basis_limbs[0].element.normalize().witness_index, P0.x.binary_basis_limbs[1].element.normalize().witness_index, P0.x.binary_basis_limbs[2].element.normalize().witness_index, @@ -66,11 +53,17 @@ template struct aggregation_state { P1.y.binary_basis_limbs[2].element.normalize().witness_index, P1.y.binary_basis_limbs[3].element.normalize().witness_index, }; + return witness_indices; + } + void assign_object_to_proof_outputs() + { + P0 = P0.reduce(); + P1 = P1.reduce(); + AggregationObjectIndices proof_witness_indices = get_witness_indices(); auto* context = P0.get_context(); CircuitChecker::check(*context); - info("checked circuit before add_recursive_proof"); context->add_recursive_proof(proof_witness_indices); } }; @@ -81,8 +74,6 @@ template void read(uint8_t const*& it, aggregation_state read(it, as.P0); read(it, as.P1); - read(it, as.public_inputs); - read(it, as.proof_witness_indices); read(it, as.has_data); }; @@ -92,8 +83,6 @@ template void write(std::vector& buf, aggregation_stat write(buf, as.P0); write(buf, as.P1); - write(buf, as.public_inputs); - write(buf, as.proof_witness_indices); write(buf, as.has_data); }; @@ -101,9 +90,115 @@ template std::ostream& operator<<(std::ostream& os, aggregation_s { return os << "P0: " << as.P0 << "\n" << "P1: " << as.P1 << "\n" - << "public_inputs: " << as.public_inputs << "\n" - << "proof_witness_indices: " << as.proof_witness_indices << "\n" << "has_data: " << as.has_data << "\n"; } +/** + * @brief Converts a list of 16 witness indices corresponding to the bigfield limbs of an aggregation object to an + * aggregation_state. + * + * @tparam Builder + * @tparam Curve + * @param builder + * @param witness_indices + * @return aggregation_state + */ +template +aggregation_state convert_witness_indices_to_agg_obj(Builder& builder, + const AggregationObjectIndices& witness_indices) +{ + std::array aggregation_elements; + for (size_t i = 0; i < 4; ++i) { + aggregation_elements[i] = + typename Curve::BaseField(Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i]), + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 1]), + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 2]), + Curve::ScalarField::from_witness_index(&builder, witness_indices[4 * i + 3])); + aggregation_elements[i].assert_is_in_field(); + } + + return { typename Curve::Group(aggregation_elements[0], aggregation_elements[1]), + typename Curve::Group(aggregation_elements[2], aggregation_elements[3]), + /*has_data=*/true }; +} + +/** + * @brief Creates the 16 witness indices for a default aggregation_state object. + * + * @tparam Builder + * @param builder + * @return AggregationObjectIndices + */ +template AggregationObjectIndices init_default_agg_obj_indices(Builder& builder) +{ + constexpr uint32_t NUM_LIMBS = 4; + constexpr uint32_t NUM_LIMB_BITS = 68; + constexpr uint32_t TOTAL_BITS = 254; + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted from a valid + // proof. This is a workaround because we can't represent the point at infinity in biggroup yet. + AggregationObjectIndices agg_obj_indices = {}; + fq x0("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf"); + fq y0("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4"); + fq x1("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38"); + fq y1("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f"); + std::vector aggregation_object_fq_values = { x0, y0, x1, y1 }; + size_t agg_obj_indices_idx = 0; + for (fq val : aggregation_object_fq_values) { + const uint256_t x = val; + std::array val_limbs = { x.slice(0, NUM_LIMB_BITS), + x.slice(NUM_LIMB_BITS, NUM_LIMB_BITS * 2), + x.slice(NUM_LIMB_BITS * 2, NUM_LIMB_BITS * 3), + x.slice(NUM_LIMB_BITS * 3, TOTAL_BITS) }; + for (size_t i = 0; i < NUM_LIMBS; ++i) { + uint32_t idx = builder.add_variable(val_limbs[i]); + agg_obj_indices[agg_obj_indices_idx] = idx; + agg_obj_indices_idx++; + } + } + return agg_obj_indices; +} + +/** + * @brief Creates a default aggregation_state object. + * + * @tparam Builder + * @tparam Curve + */ +template +aggregation_state init_default_aggregation_state(Builder& builder) + requires(!IsSimulator) +{ + AggregationObjectIndices agg_obj_indices = init_default_agg_obj_indices(builder); + return convert_witness_indices_to_agg_obj(builder, agg_obj_indices); +} + +/** + * @brief Creates a default aggregation_state object for the Simulator. + * + * @tparam Builder + * @tparam Curve + * @param builder + * @return aggregation_state + */ +template +aggregation_state init_default_aggregation_state(Builder& builder) + requires IsSimulator +{ + // TODO(https://github.com/AztecProtocol/barretenberg/issues/911): These are pairing points extracted from a valid + // proof. This is a workaround because we can't represent the point at infinity in biggroup yet. + uint256_t x0_val("0x031e97a575e9d05a107acb64952ecab75c020998797da7842ab5d6d1986846cf"); + uint256_t y0_val("0x178cbf4206471d722669117f9758a4c410db10a01750aebb5666547acf8bd5a4"); + uint256_t x1_val("0x0f94656a2ca489889939f81e9c74027fd51009034b3357f0e91b8a11e7842c38"); + uint256_t y1_val("0x1b52c2020d7464a0c80c0da527a08193fe27776f50224bd6fb128b46c1ddb67f"); + typename Curve::BaseField x0(&builder, x0_val); + typename Curve::BaseField y0(&builder, y0_val); + typename Curve::BaseField x1(&builder, x1_val); + typename Curve::BaseField y1(&builder, y1_val); + aggregation_state agg_obj; + agg_obj.P0 = typename Curve::Group(x0, y0); + agg_obj.P1 = typename Curve::Group(x1, y1); + return agg_obj; +} + } // namespace bb::stdlib::recursion diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp index 5ed915c85a89..df0bffaf55e3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.hpp @@ -386,28 +386,7 @@ aggregation_state verify_proof_(typename Curve::Builder* context, rhs = (-rhs) - PI_Z; - // TODO(zac): remove this once a3-packages has migrated to calling `assign_object_to_proof_outputs`) - AggregationObjectIndices proof_witness_indices = { - opening_result.x.binary_basis_limbs[0].element.normalize().witness_index, - opening_result.x.binary_basis_limbs[1].element.normalize().witness_index, - opening_result.x.binary_basis_limbs[2].element.normalize().witness_index, - opening_result.x.binary_basis_limbs[3].element.normalize().witness_index, - opening_result.y.binary_basis_limbs[0].element.normalize().witness_index, - opening_result.y.binary_basis_limbs[1].element.normalize().witness_index, - opening_result.y.binary_basis_limbs[2].element.normalize().witness_index, - opening_result.y.binary_basis_limbs[3].element.normalize().witness_index, - rhs.x.binary_basis_limbs[0].element.normalize().witness_index, - rhs.x.binary_basis_limbs[1].element.normalize().witness_index, - rhs.x.binary_basis_limbs[2].element.normalize().witness_index, - rhs.x.binary_basis_limbs[3].element.normalize().witness_index, - rhs.y.binary_basis_limbs[0].element.normalize().witness_index, - rhs.y.binary_basis_limbs[1].element.normalize().witness_index, - rhs.y.binary_basis_limbs[2].element.normalize().witness_index, - rhs.y.binary_basis_limbs[3].element.normalize().witness_index, - }; - auto result = aggregation_state{ - opening_result, rhs, transcript.get_field_element_vector("public_inputs"), proof_witness_indices, true - }; + auto result = aggregation_state{ opening_result, rhs, true }; return result; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.test.cpp index 40cf174b0785..033939939785 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/plonk_recursion/verifier/verifier.test.cpp @@ -391,8 +391,6 @@ template class stdlib_verifier : public testing::Test { create_inner_circuit(inner_circuit, inner_public_inputs); auto circuit_output = create_outer_circuit(inner_circuit, outer_circuit); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_public_inputs[0]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_public_inputs[1]); circuit_output.aggregation_state.assign_object_to_proof_outputs(); EXPECT_EQ(outer_circuit.failed(), false); @@ -456,8 +454,6 @@ template class stdlib_verifier : public testing::Test { auto circuit_output = create_double_outer_circuit(mid_circuit_a, mid_circuit_b, outer_circuit); circuit_output.aggregation_state.assign_object_to_proof_outputs(); - EXPECT_EQ(circuit_output_a.aggregation_state.public_inputs[0].get_value(), inner_inputs[0]); - EXPECT_EQ(circuit_output_a.aggregation_state.public_inputs[1].get_value(), inner_inputs[1]); check_pairing(circuit_output); check_recursive_verification_circuit(outer_circuit, true); @@ -484,9 +480,6 @@ template class stdlib_verifier : public testing::Test { auto circuit_output = create_outer_circuit_with_variable_inner_circuit(inner_circuit_a, inner_circuit_b, outer_circuit, true); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[2].get_value(), inner_inputs_a[2]); check_pairing(circuit_output); check_recursive_verification_circuit(outer_circuit, true); @@ -513,9 +506,6 @@ template class stdlib_verifier : public testing::Test { auto circuit_output = create_outer_circuit_with_variable_inner_circuit(inner_circuit_a, inner_circuit_b, outer_circuit, false); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_b[0]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_b[1]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[2].get_value(), inner_inputs_b[2]); check_pairing(circuit_output); check_recursive_verification_circuit(outer_circuit, true); @@ -540,9 +530,6 @@ template class stdlib_verifier : public testing::Test { auto circuit_output = create_outer_circuit_with_variable_inner_circuit( inner_circuit_a, inner_circuit_b, outer_circuit, true, true); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[2].get_value(), inner_inputs_a[2]); check_pairing(circuit_output); check_recursive_verification_circuit(outer_circuit, false); @@ -567,9 +554,6 @@ template class stdlib_verifier : public testing::Test { auto circuit_output = create_outer_circuit_with_variable_inner_circuit( inner_circuit_a, inner_circuit_b, outer_circuit, true, false, true); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[0].get_value(), inner_inputs_a[0]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[1].get_value(), inner_inputs_a[1]); - EXPECT_EQ(circuit_output.aggregation_state.public_inputs[2].get_value(), inner_inputs_a[2]); check_pairing(circuit_output); check_recursive_verification_circuit(outer_circuit, true); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base_impl.hpp index ed7c157ad3b5..9e83ae4b3de0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base_impl.hpp @@ -251,6 +251,7 @@ void CircuitBuilderBase::set_recursive_proof(const AggregationObjectIndices { if (contains_recursive_proof) { failure("added recursive proof when one already exists"); + ASSERT(0); } contains_recursive_proof = true; for (size_t i = 0; i < proof_output_witness_indices.size(); ++i) { diff --git a/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts b/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts index 5f57b1952d24..80dc0e49bfc1 100644 --- a/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_private_kernel_prover.ts @@ -352,7 +352,6 @@ export class BBNativePrivateKernelProver implements PrivateKernelProver { const json = JSON.parse(proofString); const fields = json.map(Fr.fromString); const numPublicInputs = vkData.numPublicInputs - AGGREGATION_OBJECT_LENGTH; - const fieldsWithoutPublicInputs = fields.slice(numPublicInputs); this.log.info( `Circuit type: ${circuitType}, complete proof length: ${fields.length}, without public inputs: ${fieldsWithoutPublicInputs.length}, num public inputs: ${numPublicInputs}, circuit size: ${vkData.circuitSize}, is recursive: ${vkData.isRecursive}, raw length: ${binaryProof.length}`, diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index 95378c4ca28d..1e1325fa4a3b 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -895,7 +895,6 @@ export class BBNativeRollupProver implements ServerCircuitProver { throw new Error(`Invalid verification key for ${circuitType}`); } const numPublicInputs = vkData.numPublicInputs - AGGREGATION_OBJECT_LENGTH; - const fieldsWithoutPublicInputs = json .slice(0, 3) .map(Fr.fromString)