From 446e48a830c48d1dd7335ddc8509cc9d28e80da9 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:43:22 +0000 Subject: [PATCH 01/11] Tagging mechanism --- .../stdlib/primitives/pairing_points.hpp | 20 ++++- .../stdlib/primitives/pairing_points.test.cpp | 80 +++++++++++++++++++ .../circuit_builder_base.hpp | 74 +++++++++++++++++ .../ultra_honk/prover_instance.hpp | 9 +++ 4 files changed, 181 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp index c408a3001184..cdf0bb7813dd 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp @@ -40,6 +40,7 @@ template struct PairingPoints { Group P1; bool has_data = false; + uint32_t tag = 0; // Tag for tracking pairing point aggregation // Number of bb::fr field elements used to represent a goblin element in the public inputs static constexpr size_t PUBLIC_INPUTS_SIZE = PAIRING_POINTS_SIZE; @@ -50,7 +51,13 @@ template struct PairingPoints { : P0(P0) , P1(P1) , has_data(true) - {} + { + // Get the builder from the group elements and assign a new tag + Builder* builder = P0.get_context(); + if (builder != nullptr) { + tag = builder->create_pairing_point_tag(); + } + } PairingPoints(std::array const& points) : PairingPoints(points[0], points[1]) @@ -135,6 +142,12 @@ template struct PairingPoints { point_to_aggregate = other.P1.scalar_mul(recursion_separator, 128); P1 += point_to_aggregate; } + + // Merge the tags in the builder + Builder* builder = P0.get_context(); + if (builder != nullptr) { + builder->merge_pairing_point_tags(this->tag, other.tag); + } } /** @@ -236,6 +249,7 @@ template void read(uint8_t const*& it, PairingPoints read(it, as.P0); read(it, as.P1); read(it, as.has_data); + read(it, as.tag); }; template void write(std::vector& buf, PairingPoints const& as) @@ -245,13 +259,15 @@ template void write(std::vector& buf, PairingPoints< write(buf, as.P0); write(buf, as.P1); write(buf, as.has_data); + write(buf, as.tag); }; template std::ostream& operator<<(std::ostream& os, PairingPoints const& as) { return os << "P0: " << as.P0 << "\n" << "P1: " << as.P1 << "\n" - << "has_data: " << as.has_data << "\n"; + << "has_data: " << as.has_data << "\n" + << "tag: " << as.tag << "\n"; } } // namespace bb::stdlib::recursion diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp index 04c60e55acd9..d93c70de0117 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp @@ -1,6 +1,7 @@ #include "barretenberg/stdlib/primitives/pairing_points.hpp" #include "barretenberg/commitment_schemes/pairing_points.hpp" #include "barretenberg/srs/global_crs.hpp" +#include "barretenberg/ultra_honk/prover_instance.hpp" #include namespace bb::stdlib::recursion { @@ -48,4 +49,83 @@ TYPED_TEST(PairingPointsTests, TestDefault) bb::PairingPoints native_pp(P0.get_value(), P1.get_value()); EXPECT_TRUE(native_pp.check()) << "Default PairingPoints are not valid pairing points."; } + +TYPED_TEST(PairingPointsTests, TaggingMechanismWorks) +{ + using Builder = TypeParam; + using PairingPoints = PairingPoints; + using Group = PairingPoints::Group; + using Fr = PairingPoints::Curve::ScalarField; + using NativeFr = PairingPoints::Curve::ScalarFieldNative; + + Builder builder; + + Fr scalar_one = Fr::from_witness(&builder, NativeFr::random_element()); + Fr scalar_two = Fr::from_witness(&builder, NativeFr::random_element()); + Group P0 = Group::batch_mul({ Group::one(&builder) }, { scalar_one }); + Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two }); + + PairingPoints pp_one = { P0, P1 }; + PairingPoints pp_two = { P0, P1 }; + + // Check the tags + BB_ASSERT_EQ(pp_one.tag, 0U); + BB_ASSERT_EQ(pp_two.tag, 1U); + + // Check that there are two different pairing points in the builder + EXPECT_FALSE(builder.has_single_pairing_point_tag()); + + // Merge the tags + pp_one.aggregate(pp_two); + + // Check that the tags have been merged + EXPECT_TRUE(builder.has_single_pairing_point_tag()); +} + +TYPED_TEST(PairingPointsTests, TaggingMechanismFails) +{ + BB_DISABLE_ASSERTS(); + + using Builder = TypeParam; + using PairingPoints = PairingPoints; + using Group = PairingPoints::Group; + using Fr = PairingPoints::Curve::ScalarField; + using NativeFr = PairingPoints::Curve::ScalarFieldNative; + using Flavor = std::conditional_t, MegaFlavor, UltraFlavor>; + using ProverInstance = ProverInstance_; + + Builder builder; + + Fr scalar_one = Fr::from_witness(&builder, NativeFr::random_element()); + Fr scalar_two = Fr::from_witness(&builder, NativeFr::random_element()); + Group P0 = Group::batch_mul({ Group::one(&builder) }, { scalar_one }); + Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two }); + + PairingPoints pp_one = { P0, P1 }; + PairingPoints pp_two = { P0, P1 }; + PairingPoints pp_three = { P0, P1 }; + + // Check the tags + BB_ASSERT_EQ(pp_one.tag, 0U); + BB_ASSERT_EQ(pp_two.tag, 1U); + BB_ASSERT_EQ(pp_three.tag, 2U); + + // Check that there are different pairing points in the builder + EXPECT_FALSE(builder.has_single_pairing_point_tag()); + + // Merge the tags + pp_one.aggregate(pp_two); + + // Check that the tags have not been merged + EXPECT_FALSE(builder.has_single_pairing_point_tag()); + + // Create a ProverInstance, expect failure + try { + ProverInstance prover_instance(builder); + } catch (std::exception& e) { + BB_ASSERT_EQ(e.what(), + "Pairing points must all be aggregated together. Either no pairing points should be created, or " + "all created pairing points must be aggregated into a single pairing point."); + } +} } // namespace bb::stdlib::recursion diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index e9424068495f..c1af9159e71a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -14,6 +14,7 @@ #include "barretenberg/serialize/msgpack.hpp" #include +#include #include namespace bb { @@ -86,6 +87,11 @@ template class CircuitBuilderBase { */ std::unordered_map _tau; + // Pairing point tag tracking (union-find structure) + mutable std::vector> pairing_points_tags; + mutable uint32_t next_pairing_point_tag = 0; + mutable bool has_pairing_points = false; + public: /** * @brief Map from witness index to real variable index @@ -235,6 +241,74 @@ template class CircuitBuilderBase { const std::string& err() const; void failure(std::string msg); + + /** + * @brief Pairing point tag management methods + * @details These methods implement a union-find structure to track pairing point tag equivalences + */ + + /** + * @brief Create a new unique pairing point tag + * @return The new tag + */ + uint32_t create_pairing_point_tag() const + { + has_pairing_points = true; + uint32_t new_tag = next_pairing_point_tag++; + pairing_points_tags.emplace_back( + std::make_pair(new_tag, new_tag)); // Each PairingPoint tag starts as a couple (tag, tag) + return new_tag; + } + + /** + * @brief Merge two pairing point tags + * @param tag1 First tag + * @param tag2 Second tag + */ + void merge_pairing_point_tags(uint32_t tag1, uint32_t tag2) const + { + uint32_t root1 = pairing_points_tags[tag1].first; + uint32_t root2 = pairing_points_tags[tag2].first; + if (root1 != root2) { + for (auto& tag : pairing_points_tags) { + if (tag.first == root2) { + tag.first = root1; + } + } + } + } + + /** + * @brief Check if all pairing point tags belong to a single equivalence class + * @return true if there's only one equivalence class (or no tags at all) + */ + bool has_single_pairing_point_tag() const + { + if (!has_pairing_points) { + return true; // No pairing points created + } + // Check that there is only one tag + uint32_t unique_tag = pairing_points_tags[0].first; + return std::ranges::all_of(pairing_points_tags, + [unique_tag](auto const& tag) { return tag.first == unique_tag; }); + } + + /** + * @brief Return the number of unique pairing point tags + */ + uint32_t unique_pairing_points() const + { + std::vector unique_tags; + unique_tags.resize(pairing_points_tags.size()); + for (auto const& tag : pairing_points_tags) { + unique_tags[tag.first] = 1; + } + uint32_t sum = 0; + for (auto v : unique_tags) { + sum += v; + } + return sum; + } }; /** diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp index cbc468e21a25..372f9ced42e6 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp @@ -134,6 +134,15 @@ template class ProverInstance_ { BB_BENCH_NAME("ProverInstance(Circuit&)"); vinfo("Constructing ProverInstance"); auto start = std::chrono::steady_clock::now(); + + // Check pairing point tagging invariant: either no pairing points were created, + // or all pairing points have been aggregated into a single equivalence class + BB_ASSERT(circuit.has_single_pairing_point_tag(), + "Pairing points must all be aggregated together. Either no pairing points should be created, or " + "all created pairing points must be aggregated into a single pairing point. Found ", + circuit.unique_pairing_points(), + " different pairing points."); + // Decider proving keys can be constructed multiple times, hence, we check whether the circuit has been // finalized if (!circuit.circuit_finalized) { From 57e0b4b62a34baab0d33cdb4b3b3dae786bc0b48 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:27:42 +0000 Subject: [PATCH 02/11] Construction of pairing points --- .../commitment_schemes_recursion/shplemini.test.cpp | 6 ++++-- .../barretenberg/hypernova/hypernova_decider_verifier.cpp | 4 ++-- .../stdlib/honk_verifier/decider_recursive_verifier.cpp | 4 ++-- .../stdlib/honk_verifier/ultra_recursive_verifier.cpp | 3 ++- .../stdlib/merge_verifier/merge_recursive_verifier.cpp | 2 +- .../translator_recursive_verifier.cpp | 4 ++-- .../vm2/constraining/recursion/recursive_verifier.cpp | 2 +- 7 files changed, 14 insertions(+), 11 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp index e7b43b304bdd..bb31f92bce08 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes_recursion/shplemini.test.cpp @@ -10,6 +10,7 @@ #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/primitives/curves/grumpkin.hpp" #include "barretenberg/stdlib/primitives/padding_indicator_array/padding_indicator_array.hpp" +#include "barretenberg/stdlib/primitives/pairing_points.hpp" #include "barretenberg/stdlib/proof/proof.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" #include @@ -136,11 +137,12 @@ TEST(ShpleminiRecursionTest, ProveAndVerifySingle) u_challenge_in_circuit, Commitment::one(&builder), stdlib_verifier_transcript); - auto pairing_points = KZG::reduce_verify_batch_opening_claim(opening_claim, stdlib_verifier_transcript); + stdlib::recursion::PairingPoints pairing_points( + KZG::reduce_verify_batch_opening_claim(opening_claim, stdlib_verifier_transcript)); EXPECT_TRUE(CircuitChecker::check(builder)); VerifierCommitmentKey vk; - EXPECT_EQ(vk.pairing_check(pairing_points[0].get_value(), pairing_points[1].get_value()), true); + EXPECT_EQ(vk.pairing_check(pairing_points.P0.get_value(), pairing_points.P1.get_value()), true); // Return finalized number of gates; return builder.num_gates(); diff --git a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_decider_verifier.cpp b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_decider_verifier.cpp index 1380d1c253a9..91c3e42cf16e 100644 --- a/barretenberg/cpp/src/barretenberg/hypernova/hypernova_decider_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/hypernova/hypernova_decider_verifier.cpp @@ -32,11 +32,11 @@ HypernovaDeciderVerifier::PairingPoints HypernovaDeciderVerifier const auto opening_claim = ShpleminiVerifier::compute_batch_opening_claim( padding_indicator_array, claim_batcher, accumulator.challenge, generator, transcript); - auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); - if constexpr (IsRecursiveFlavor) { + PairingPoints pairing_points(PCS::reduce_verify_batch_opening_claim(opening_claim, transcript)); return pairing_points; } else { + auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); // Native pairing points contain affine elements return { typename Curve::AffineElement(pairing_points[0]), typename Curve::AffineElement(pairing_points[1]) }; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp index 2728044d985c..04415f9aa235 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/decider_recursive_verifier.cpp @@ -63,9 +63,9 @@ DeciderRecursiveVerifier_::PairingPoints DeciderRecursiveVerifier_>; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp index 5e93b1d61e57..a543dc3cc22e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/ultra_recursive_verifier.cpp @@ -120,7 +120,8 @@ UltraRecursiveVerifier_::Output UltraRecursiveVerifier_::verify_ libra_commitments, sumcheck_output.claimed_libra_evaluation); - auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); + PairingPoints pairing_points( + PCS::reduce_verify_batch_opening_claim(opening_claim, transcript)); // Reconstruct the public inputs IO inputs; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/merge_verifier/merge_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/merge_verifier/merge_recursive_verifier.cpp index 50f3720b5449..469f62c73fa6 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/merge_verifier/merge_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/merge_verifier/merge_recursive_verifier.cpp @@ -154,7 +154,7 @@ MergeRecursiveVerifier_::verify_proof(const stdlib::Proof& translation_evaluations, diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp index 37fa618387da..0cd714e44719 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.cpp @@ -189,7 +189,7 @@ AvmRecursiveVerifier::PairingPoints AvmRecursiveVerifier::verify_proof( const BatchOpeningClaim opening_claim = Shplemini::compute_batch_opening_claim( padding_indicator_array, claim_batcher, output.challenge, Commitment::one(&builder), transcript); - auto pairing_points = PCS::reduce_verify_batch_opening_claim(opening_claim, transcript); + PairingPoints pairing_points(PCS::reduce_verify_batch_opening_claim(opening_claim, transcript)); if (builder.failed()) { info("AVM Recursive verifier builder failed with error: ", builder.err()); From dd56caa3281143039d72e1f2cff9d08fb69a827a Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:33:04 +0000 Subject: [PATCH 03/11] Simplify tagging mechanism --- .../circuit_builder_base.hpp | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index c1af9159e71a..a39b0c3124e8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -87,10 +87,10 @@ template class CircuitBuilderBase { */ std::unordered_map _tau; - // Pairing point tag tracking (union-find structure) - mutable std::vector> pairing_points_tags; - mutable uint32_t next_pairing_point_tag = 0; - mutable bool has_pairing_points = false; + // Pairing point tag tracking + mutable std::vector _pairing_points_tags; + mutable uint32_t _next_pairing_point_tag = 0; + mutable bool _has_pairing_points = false; public: /** @@ -242,21 +242,15 @@ template class CircuitBuilderBase { void failure(std::string msg); - /** - * @brief Pairing point tag management methods - * @details These methods implement a union-find structure to track pairing point tag equivalences - */ - /** * @brief Create a new unique pairing point tag - * @return The new tag */ uint32_t create_pairing_point_tag() const { - has_pairing_points = true; - uint32_t new_tag = next_pairing_point_tag++; - pairing_points_tags.emplace_back( - std::make_pair(new_tag, new_tag)); // Each PairingPoint tag starts as a couple (tag, tag) + _has_pairing_points = true; + uint32_t new_tag = _next_pairing_point_tag++; + _pairing_points_tags.emplace_back( + new_tag); // Each PairingPoints starts with tag equal to the number of PairingPoints created before it return new_tag; } @@ -267,13 +261,10 @@ template class CircuitBuilderBase { */ void merge_pairing_point_tags(uint32_t tag1, uint32_t tag2) const { - uint32_t root1 = pairing_points_tags[tag1].first; - uint32_t root2 = pairing_points_tags[tag2].first; - if (root1 != root2) { - for (auto& tag : pairing_points_tags) { - if (tag.first == root2) { - tag.first = root1; - } + // If different tags, override tag2 with tag1 + if (tag1 != tag2) { + for (auto& tag : _pairing_points_tags) { + tag = tag == tag2 ? tag1 : tag; } } } @@ -284,13 +275,12 @@ template class CircuitBuilderBase { */ bool has_single_pairing_point_tag() const { - if (!has_pairing_points) { + if (!_has_pairing_points) { return true; // No pairing points created } // Check that there is only one tag - uint32_t unique_tag = pairing_points_tags[0].first; - return std::ranges::all_of(pairing_points_tags, - [unique_tag](auto const& tag) { return tag.first == unique_tag; }); + uint32_t unique_tag = _pairing_points_tags[0]; + return std::ranges::all_of(_pairing_points_tags, [unique_tag](auto const& tag) { return tag == unique_tag; }); } /** @@ -299,9 +289,9 @@ template class CircuitBuilderBase { uint32_t unique_pairing_points() const { std::vector unique_tags; - unique_tags.resize(pairing_points_tags.size()); - for (auto const& tag : pairing_points_tags) { - unique_tags[tag.first] = 1; + unique_tags.resize(_pairing_points_tags.size()); + for (auto const& tag : _pairing_points_tags) { + unique_tags[tag] = 1; } uint32_t sum = 0; for (auto v : unique_tags) { From e16a3ade8fd722a02bcad0298107ab3e64c96d7e Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:38:08 +0000 Subject: [PATCH 04/11] Add test check --- .../src/barretenberg/stdlib/primitives/pairing_points.test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp index d93c70de0117..0b9273e07b61 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp @@ -65,6 +65,9 @@ TYPED_TEST(PairingPointsTests, TaggingMechanismWorks) Group P0 = Group::batch_mul({ Group::one(&builder) }, { scalar_one }); Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two }); + // Check that no pairing points exist + EXPECT_TRUE(builder.has_single_pairing_point_tag()); + PairingPoints pp_one = { P0, P1 }; PairingPoints pp_two = { P0, P1 }; From fd84ac947ba170cc542d286647fdbe958aaab12c Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:42:21 +0000 Subject: [PATCH 05/11] Move tagging mechanism to its own file --- .../circuit_builder_base.hpp | 47 +------- .../pairing_points_tagging.hpp | 110 ++++++++++++++++++ .../ultra_honk/prover_instance.hpp | 2 +- 3 files changed, 117 insertions(+), 42 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index a39b0c3124e8..8e34311bd70f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -12,6 +12,7 @@ #include "barretenberg/honk/execution_trace/gate_data.hpp" #include "barretenberg/public_input_component/public_component_key.hpp" #include "barretenberg/serialize/msgpack.hpp" +#include "pairing_points_tagging.hpp" #include #include @@ -88,9 +89,7 @@ template class CircuitBuilderBase { std::unordered_map _tau; // Pairing point tag tracking - mutable std::vector _pairing_points_tags; - mutable uint32_t _next_pairing_point_tag = 0; - mutable bool _has_pairing_points = false; + mutable PairingPointsTagging pairing_points_tagging; public: /** @@ -245,60 +244,26 @@ template class CircuitBuilderBase { /** * @brief Create a new unique pairing point tag */ - uint32_t create_pairing_point_tag() const - { - _has_pairing_points = true; - uint32_t new_tag = _next_pairing_point_tag++; - _pairing_points_tags.emplace_back( - new_tag); // Each PairingPoints starts with tag equal to the number of PairingPoints created before it - return new_tag; - } + uint32_t create_pairing_point_tag() const { return pairing_points_tagging.create_pairing_point_tag(); } /** * @brief Merge two pairing point tags - * @param tag1 First tag - * @param tag2 Second tag */ void merge_pairing_point_tags(uint32_t tag1, uint32_t tag2) const { - // If different tags, override tag2 with tag1 - if (tag1 != tag2) { - for (auto& tag : _pairing_points_tags) { - tag = tag == tag2 ? tag1 : tag; - } - } + pairing_points_tagging.merge_pairing_point_tags(tag1, tag2); } /** * @brief Check if all pairing point tags belong to a single equivalence class * @return true if there's only one equivalence class (or no tags at all) */ - bool has_single_pairing_point_tag() const - { - if (!_has_pairing_points) { - return true; // No pairing points created - } - // Check that there is only one tag - uint32_t unique_tag = _pairing_points_tags[0]; - return std::ranges::all_of(_pairing_points_tags, [unique_tag](auto const& tag) { return tag == unique_tag; }); - } + bool has_single_pairing_point_tag() const { return pairing_points_tagging.has_single_pairing_point_tag(); } /** * @brief Return the number of unique pairing point tags */ - uint32_t unique_pairing_points() const - { - std::vector unique_tags; - unique_tags.resize(_pairing_points_tags.size()); - for (auto const& tag : _pairing_points_tags) { - unique_tags[tag] = 1; - } - uint32_t sum = 0; - for (auto v : unique_tags) { - sum += v; - } - return sum; - } + uint32_t num_unique_pairing_points() const { return pairing_points_tagging.num_unique_pairing_points(); } }; /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp new file mode 100644 index 000000000000..f3a700e2d97c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp @@ -0,0 +1,110 @@ +// === AUDIT STATUS === +// internal: { status: not started, auditors: [], date: YYYY-MM-DD } +// external_1: { status: not started, auditors: [], date: YYYY-MM-DD } +// external_2: { status: not started, auditors: [], date: YYYY-MM-DD } +// ===================== + +#pragma once +#include +#include +#include + +namespace bb { + +/** + * @brief Class to manage pairing point tagging + * @details This class tracks pairing points and their tags, providing functionality + * to create new tags, merge tags, and query tag properties. Tags are used to track + * which pairing points can be safely merged together. + */ +class PairingPointsTagging { + private: + std::vector pairing_points_tags_; + uint32_t next_pairing_point_tag_ = 0; + bool has_pairing_points_ = false; + + public: + PairingPointsTagging() = default; + PairingPointsTagging(const PairingPointsTagging& other) = default; + PairingPointsTagging(PairingPointsTagging&& other) noexcept = default; + PairingPointsTagging& operator=(const PairingPointsTagging& other) = default; + PairingPointsTagging& operator=(PairingPointsTagging&& other) noexcept = default; + ~PairingPointsTagging() = default; + + bool operator==(const PairingPointsTagging& other) const = default; + + /** + * @brief Create a new unique pairing point tag + * @return The new tag value + */ + uint32_t create_pairing_point_tag() + { + has_pairing_points_ = true; + uint32_t new_tag = next_pairing_point_tag_++; + pairing_points_tags_.emplace_back( + new_tag); // Each PairingPoints starts with tag equal to the number of PairingPoints created before it + return new_tag; + } + + /** + * @brief Merge two pairing point tags + * @param tag1 First tag + * @param tag2 Second tag + * @details If the tags are different, all instances of tag2 are replaced with tag1 + */ + void merge_pairing_point_tags(uint32_t tag1, uint32_t tag2) + { + // If different tags, override tag2 with tag1 + if (tag1 != tag2) { + for (auto& tag : pairing_points_tags_) { + tag = tag == tag2 ? tag1 : tag; + } + } + } + + /** + * @brief Check if all pairing point tags belong to a single equivalence class + * @return true if there's only one equivalence class (or no tags at all) + */ + bool has_single_pairing_point_tag() const + { + if (!has_pairing_points_) { + return true; // No pairing points created + } + // Check that there is only one tag + uint32_t unique_tag = pairing_points_tags_[0]; + return std::ranges::all_of(pairing_points_tags_, [unique_tag](auto const& tag) { return tag == unique_tag; }); + } + + /** + * @brief Return the number of unique pairing point tags + * @return The count of unique tags + */ + uint32_t num_unique_pairing_points() const + { + std::vector unique_tags; + unique_tags.resize(pairing_points_tags_.size()); + for (auto const& tag : pairing_points_tags_) { + unique_tags[tag] = 1; + } + uint32_t sum = 0; + for (auto v : unique_tags) { + sum += v; + } + return sum; + } + + /** + * @brief Check if any pairing points have been created + * @return true if pairing points have been created + */ + bool has_pairing_points() const { return has_pairing_points_; } + + /** + * @brief Get the pairing points tags vector + * @return const reference to the tags vector + */ + const std::vector& get_pairing_points_tags() const { return pairing_points_tags_; } +}; + +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp index 372f9ced42e6..94756659b3b4 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp @@ -140,7 +140,7 @@ template class ProverInstance_ { BB_ASSERT(circuit.has_single_pairing_point_tag(), "Pairing points must all be aggregated together. Either no pairing points should be created, or " "all created pairing points must be aggregated into a single pairing point. Found ", - circuit.unique_pairing_points(), + circuit.num_unique_pairing_points(), " different pairing points."); // Decider proving keys can be constructed multiple times, hence, we check whether the circuit has been From 653721fe7b3e8996f6209a73551721dbfe69df16 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:01:56 +0000 Subject: [PATCH 06/11] PP stores index --- .../stdlib/primitives/pairing_points.hpp | 18 +++++++--- .../stdlib/primitives/pairing_points.test.cpp | 34 ++++++++++++++++--- .../circuit_builder_base.hpp | 9 +++-- .../pairing_points_tagging.hpp | 10 +++--- 4 files changed, 56 insertions(+), 15 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp index cdf0bb7813dd..bc566d1bc2dd 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp @@ -40,7 +40,7 @@ template struct PairingPoints { Group P1; bool has_data = false; - uint32_t tag = 0; // Tag for tracking pairing point aggregation + uint32_t tag_index = 0; // Index of the tag for tracking pairing point aggregation // Number of bb::fr field elements used to represent a goblin element in the public inputs static constexpr size_t PUBLIC_INPUTS_SIZE = PAIRING_POINTS_SIZE; @@ -55,7 +55,7 @@ template struct PairingPoints { // Get the builder from the group elements and assign a new tag Builder* builder = P0.get_context(); if (builder != nullptr) { - tag = builder->create_pairing_point_tag(); + tag_index = builder->create_pairing_point_tag(); } } @@ -102,7 +102,17 @@ template struct PairingPoints { auto P0 = Group::batch_mul(first_components, challenges); auto P1 = Group::batch_mul(second_components, challenges); - return { P0, P1 }; + PairingPoints aggregated_points(P0, P1); + + // Merge tags + Builder* builder = P0.get_context(); + if (builder != nullptr) { + for (const auto& points : pairing_points) { + builder->merge_pairing_point_tags(aggregated_points.tag_index, points.tag_index); + } + } + + return aggregated_points; } /** @@ -146,7 +156,7 @@ template struct PairingPoints { // Merge the tags in the builder Builder* builder = P0.get_context(); if (builder != nullptr) { - builder->merge_pairing_point_tags(this->tag, other.tag); + builder->merge_pairing_point_tags(this->tag_index, other.tag_index); } } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp index 0b9273e07b61..13da26beb313 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp @@ -72,8 +72,8 @@ TYPED_TEST(PairingPointsTests, TaggingMechanismWorks) PairingPoints pp_two = { P0, P1 }; // Check the tags - BB_ASSERT_EQ(pp_one.tag, 0U); - BB_ASSERT_EQ(pp_two.tag, 1U); + BB_ASSERT_EQ(builder.get_tag(pp_one.tag_index), 0U); + BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 1U); // Check that there are two different pairing points in the builder EXPECT_FALSE(builder.has_single_pairing_point_tag()); @@ -82,6 +82,30 @@ TYPED_TEST(PairingPointsTests, TaggingMechanismWorks) pp_one.aggregate(pp_two); // Check that the tags have been merged + BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 0U); + EXPECT_TRUE(builder.has_single_pairing_point_tag()); + + // Create two new pairing points and aggregate with aggregate_multiple + PairingPoints pp_three = { P0, P1 }; + PairingPoints pp_four = { P0, P1 }; + + // Check the tags + BB_ASSERT_EQ(builder.get_tag(pp_three.tag_index), 2U); + BB_ASSERT_EQ(builder.get_tag(pp_four.tag_index), 3U); + + // Check that there are two different pairing points in the builder + EXPECT_FALSE(builder.has_single_pairing_point_tag()); + + // Merge the tags + std::vector pp_to_be_aggregated = { pp_one, pp_three, pp_four }; + PairingPoints aggregated_pp = PairingPoints::aggregate_multiple(pp_to_be_aggregated); + + // Check that the tags have been merged + BB_ASSERT_EQ(builder.get_tag(pp_one.tag_index), 4U); + BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 4U); + BB_ASSERT_EQ(builder.get_tag(pp_three.tag_index), 4U); + BB_ASSERT_EQ(builder.get_tag(pp_four.tag_index), 4U); + BB_ASSERT_EQ(builder.get_tag(aggregated_pp.tag_index), 4U); EXPECT_TRUE(builder.has_single_pairing_point_tag()); } @@ -109,9 +133,9 @@ TYPED_TEST(PairingPointsTests, TaggingMechanismFails) PairingPoints pp_three = { P0, P1 }; // Check the tags - BB_ASSERT_EQ(pp_one.tag, 0U); - BB_ASSERT_EQ(pp_two.tag, 1U); - BB_ASSERT_EQ(pp_three.tag, 2U); + BB_ASSERT_EQ(builder.get_tag(pp_one.tag_index), 0U); + BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 1U); + BB_ASSERT_EQ(builder.get_tag(pp_three.tag_index), 2U); // Check that there are different pairing points in the builder EXPECT_FALSE(builder.has_single_pairing_point_tag()); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index 8e34311bd70f..aab3b967caa7 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -249,9 +249,9 @@ template class CircuitBuilderBase { /** * @brief Merge two pairing point tags */ - void merge_pairing_point_tags(uint32_t tag1, uint32_t tag2) const + void merge_pairing_point_tags(uint32_t tag1_index, uint32_t tag2_index) const { - pairing_points_tagging.merge_pairing_point_tags(tag1, tag2); + pairing_points_tagging.merge_pairing_point_tags(tag1_index, tag2_index); } /** @@ -264,6 +264,11 @@ template class CircuitBuilderBase { * @brief Return the number of unique pairing point tags */ uint32_t num_unique_pairing_points() const { return pairing_points_tagging.num_unique_pairing_points(); } + + /** + * @brief Get the pairing points tagging object + */ + uint32_t get_tag(uint32_t tag_index) const { return pairing_points_tagging.get_tag(tag_index); } }; /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp index f3a700e2d97c..60511e037b3c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp @@ -52,9 +52,12 @@ class PairingPointsTagging { * @param tag2 Second tag * @details If the tags are different, all instances of tag2 are replaced with tag1 */ - void merge_pairing_point_tags(uint32_t tag1, uint32_t tag2) + void merge_pairing_point_tags(uint32_t tag1_index, uint32_t tag2_index) { // If different tags, override tag2 with tag1 + uint32_t tag1 = pairing_points_tags_[tag1_index]; + uint32_t tag2 = pairing_points_tags_[tag2_index]; + if (tag1 != tag2) { for (auto& tag : pairing_points_tags_) { tag = tag == tag2 ? tag1 : tag; @@ -101,10 +104,9 @@ class PairingPointsTagging { bool has_pairing_points() const { return has_pairing_points_; } /** - * @brief Get the pairing points tags vector - * @return const reference to the tags vector + * @brief Get the tag for a specific pairing point index */ - const std::vector& get_pairing_points_tags() const { return pairing_points_tags_; } + uint32_t get_tag(uint32_t tag_index) const { return pairing_points_tags_.at(tag_index); } }; } // namespace bb From 32659b3e3802c65d2f20adb10ced29675a17e685 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:25:55 +0000 Subject: [PATCH 07/11] Address comments --- .../stdlib/primitives/pairing_points.hpp | 28 ++----------- .../stdlib/primitives/pairing_points.test.cpp | 40 +++++++++---------- .../special_public_inputs.hpp | 13 +++++- .../circuit_builder_base.hpp | 38 +++--------------- .../pairing_points_tagging.hpp | 38 ++++++++++++++---- .../ultra_honk/prover_instance.hpp | 9 +++-- 6 files changed, 78 insertions(+), 88 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp index bc566d1bc2dd..a8ae3049e966 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.hpp @@ -55,7 +55,7 @@ template struct PairingPoints { // Get the builder from the group elements and assign a new tag Builder* builder = P0.get_context(); if (builder != nullptr) { - tag_index = builder->create_pairing_point_tag(); + tag_index = builder->pairing_points_tagging.create_pairing_point_tag(); } } @@ -108,7 +108,7 @@ template struct PairingPoints { Builder* builder = P0.get_context(); if (builder != nullptr) { for (const auto& points : pairing_points) { - builder->merge_pairing_point_tags(aggregated_points.tag_index, points.tag_index); + builder->pairing_points_tagging.merge_pairing_point_tags(aggregated_points.tag_index, points.tag_index); } } @@ -156,7 +156,7 @@ template struct PairingPoints { // Merge the tags in the builder Builder* builder = P0.get_context(); if (builder != nullptr) { - builder->merge_pairing_point_tags(this->tag_index, other.tag_index); + builder->pairing_points_tagging.merge_pairing_point_tags(this->tag_index, other.tag_index); } } @@ -252,32 +252,12 @@ template struct PairingPoints { } }; -template void read(uint8_t const*& it, PairingPoints& as) -{ - using serialize::read; - - read(it, as.P0); - read(it, as.P1); - read(it, as.has_data); - read(it, as.tag); -}; - -template void write(std::vector& buf, PairingPoints const& as) -{ - using serialize::write; - - write(buf, as.P0); - write(buf, as.P1); - write(buf, as.has_data); - write(buf, as.tag); -}; - template std::ostream& operator<<(std::ostream& os, PairingPoints const& as) { return os << "P0: " << as.P0 << "\n" << "P1: " << as.P1 << "\n" << "has_data: " << as.has_data << "\n" - << "tag: " << as.tag << "\n"; + << "tag_index: " << as.tag_index << "\n"; } } // namespace bb::stdlib::recursion diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp index 13da26beb313..3825ab675b02 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/pairing_points.test.cpp @@ -66,47 +66,47 @@ TYPED_TEST(PairingPointsTests, TaggingMechanismWorks) Group P1 = Group::batch_mul({ Group::one(&builder) }, { scalar_two }); // Check that no pairing points exist - EXPECT_TRUE(builder.has_single_pairing_point_tag()); + EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag()); PairingPoints pp_one = { P0, P1 }; PairingPoints pp_two = { P0, P1 }; // Check the tags - BB_ASSERT_EQ(builder.get_tag(pp_one.tag_index), 0U); - BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 1U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_one.tag_index), 0U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 1U); // Check that there are two different pairing points in the builder - EXPECT_FALSE(builder.has_single_pairing_point_tag()); + EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag()); // Merge the tags pp_one.aggregate(pp_two); // Check that the tags have been merged - BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 0U); - EXPECT_TRUE(builder.has_single_pairing_point_tag()); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 0U); + EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag()); // Create two new pairing points and aggregate with aggregate_multiple PairingPoints pp_three = { P0, P1 }; PairingPoints pp_four = { P0, P1 }; // Check the tags - BB_ASSERT_EQ(builder.get_tag(pp_three.tag_index), 2U); - BB_ASSERT_EQ(builder.get_tag(pp_four.tag_index), 3U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_three.tag_index), 2U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_four.tag_index), 3U); // Check that there are two different pairing points in the builder - EXPECT_FALSE(builder.has_single_pairing_point_tag()); + EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag()); // Merge the tags std::vector pp_to_be_aggregated = { pp_one, pp_three, pp_four }; PairingPoints aggregated_pp = PairingPoints::aggregate_multiple(pp_to_be_aggregated); // Check that the tags have been merged - BB_ASSERT_EQ(builder.get_tag(pp_one.tag_index), 4U); - BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 4U); - BB_ASSERT_EQ(builder.get_tag(pp_three.tag_index), 4U); - BB_ASSERT_EQ(builder.get_tag(pp_four.tag_index), 4U); - BB_ASSERT_EQ(builder.get_tag(aggregated_pp.tag_index), 4U); - EXPECT_TRUE(builder.has_single_pairing_point_tag()); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_one.tag_index), 4U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 4U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_three.tag_index), 4U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_four.tag_index), 4U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(aggregated_pp.tag_index), 4U); + EXPECT_TRUE(builder.pairing_points_tagging.has_single_pairing_point_tag()); } TYPED_TEST(PairingPointsTests, TaggingMechanismFails) @@ -133,18 +133,18 @@ TYPED_TEST(PairingPointsTests, TaggingMechanismFails) PairingPoints pp_three = { P0, P1 }; // Check the tags - BB_ASSERT_EQ(builder.get_tag(pp_one.tag_index), 0U); - BB_ASSERT_EQ(builder.get_tag(pp_two.tag_index), 1U); - BB_ASSERT_EQ(builder.get_tag(pp_three.tag_index), 2U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_one.tag_index), 0U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_two.tag_index), 1U); + BB_ASSERT_EQ(builder.pairing_points_tagging.get_tag(pp_three.tag_index), 2U); // Check that there are different pairing points in the builder - EXPECT_FALSE(builder.has_single_pairing_point_tag()); + EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag()); // Merge the tags pp_one.aggregate(pp_two); // Check that the tags have not been merged - EXPECT_FALSE(builder.has_single_pairing_point_tag()); + EXPECT_FALSE(builder.pairing_points_tagging.has_single_pairing_point_tag()); // Create a ProverInstance, expect failure try { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp b/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp index 1235bd6a06db..21f59b13b53b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp @@ -115,6 +115,8 @@ class KernelIO { } output_hn_accum_hash.set_public(); + // Record that pairing points have been set to public + builder->pairing_points_tagging.set_public_pairing_points(); // Finalize the public inputs to ensure no more public inputs can be added hereafter. builder->finalize_public_inputs(); } @@ -180,6 +182,8 @@ template class DefaultIO { pairing_inputs.set_public(); + // Record that pairing points have been set to public + builder->pairing_points_tagging.set_public_pairing_points(); // Finalize the public inputs to ensure no more public inputs can be added hereafter. builder->finalize_public_inputs(); } @@ -239,11 +243,14 @@ template class GoblinAvmIO { */ void set_public() { + Builder* builder = pairing_inputs.P0.get_context(); + mega_hash.set_public(); pairing_inputs.set_public(); + // Record that pairing points have been set to public + builder->pairing_points_tagging.set_public_pairing_points(); // Finalize the public inputs to ensure no more public inputs can be added hereafter. - Builder* builder = pairing_inputs.P0.get_context(); builder->finalize_public_inputs(); } }; @@ -308,6 +315,8 @@ template class HidingKernelIO { commitment.set_public(); } + // Record that pairing points have been set to public + builder->pairing_points_tagging.set_public_pairing_points(); // Finalize the public inputs to ensure no more public inputs can be added hereafter. builder->finalize_public_inputs(); } @@ -380,6 +389,8 @@ class RollupIO { } ipa_claim.set_public(); + // Record that pairing points have been set to public + builder->pairing_points_tagging.set_public_pairing_points(); // Finalize the public inputs to ensure no more public inputs can be added hereafter. builder->finalize_public_inputs(); } diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index aab3b967caa7..dd26d0994469 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -88,10 +88,13 @@ template class CircuitBuilderBase { */ std::unordered_map _tau; - // Pairing point tag tracking - mutable PairingPointsTagging pairing_points_tagging; - public: + /** + * @brief PairingPoints tagging tool, used to ensure that all pairing points created in this circuit are aggregated + * together. This is not related to circuit logic. + */ + PairingPointsTagging pairing_points_tagging; + /** * @brief Map from witness index to real variable index * @details The "real_variable_index" acts as a map from a "witness index" (e.g. the one stored by a stdlib @@ -240,35 +243,6 @@ template class CircuitBuilderBase { const std::string& err() const; void failure(std::string msg); - - /** - * @brief Create a new unique pairing point tag - */ - uint32_t create_pairing_point_tag() const { return pairing_points_tagging.create_pairing_point_tag(); } - - /** - * @brief Merge two pairing point tags - */ - void merge_pairing_point_tags(uint32_t tag1_index, uint32_t tag2_index) const - { - pairing_points_tagging.merge_pairing_point_tags(tag1_index, tag2_index); - } - - /** - * @brief Check if all pairing point tags belong to a single equivalence class - * @return true if there's only one equivalence class (or no tags at all) - */ - bool has_single_pairing_point_tag() const { return pairing_points_tagging.has_single_pairing_point_tag(); } - - /** - * @brief Return the number of unique pairing point tags - */ - uint32_t num_unique_pairing_points() const { return pairing_points_tagging.num_unique_pairing_points(); } - - /** - * @brief Get the pairing points tagging object - */ - uint32_t get_tag(uint32_t tag_index) const { return pairing_points_tagging.get_tag(tag_index); } }; /** diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp index 60511e037b3c..4b2225065224 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp @@ -5,6 +5,7 @@ // ===================== #pragma once +#include "barretenberg/common/assert.hpp" #include #include #include @@ -13,22 +14,23 @@ namespace bb { /** * @brief Class to manage pairing point tagging - * @details This class tracks pairing points and their tags, providing functionality - * to create new tags, merge tags, and query tag properties. Tags are used to track - * which pairing points can be safely merged together. + * @details This class tracks pairing points and their tags, providing functionality to create new tags, merge tags, and + * query tag properties. Tags are used to ensure that all the pairing points created in a circuit are aggregated + * together and set to public (after aggregation). */ class PairingPointsTagging { private: std::vector pairing_points_tags_; uint32_t next_pairing_point_tag_ = 0; bool has_pairing_points_ = false; + bool has_public_pairing_points_ = false; public: PairingPointsTagging() = default; - PairingPointsTagging(const PairingPointsTagging& other) = default; - PairingPointsTagging(PairingPointsTagging&& other) noexcept = default; - PairingPointsTagging& operator=(const PairingPointsTagging& other) = default; - PairingPointsTagging& operator=(PairingPointsTagging&& other) noexcept = default; + PairingPointsTagging(const PairingPointsTagging& other) = delete; + PairingPointsTagging(PairingPointsTagging&& other) noexcept = delete; + PairingPointsTagging& operator=(const PairingPointsTagging& other) = delete; + PairingPointsTagging& operator=(PairingPointsTagging&& other) noexcept = delete; ~PairingPointsTagging() = default; bool operator==(const PairingPointsTagging& other) const = default; @@ -50,10 +52,14 @@ class PairingPointsTagging { * @brief Merge two pairing point tags * @param tag1 First tag * @param tag2 Second tag - * @details If the tags are different, all instances of tag2 are replaced with tag1 + * @details If the tags are different, all instances of tag2 are replaced with tag1. We also check that the pairing + * points have not been set to public yet. */ void merge_pairing_point_tags(uint32_t tag1_index, uint32_t tag2_index) { + BB_ASSERT(!has_public_pairing_points_, + "Cannot merge pairing point tags after pairing points have been set to public."); + // If different tags, override tag2 with tag1 uint32_t tag1 = pairing_points_tags_[tag1_index]; uint32_t tag2 = pairing_points_tags_[tag2_index]; @@ -103,10 +109,26 @@ class PairingPointsTagging { */ bool has_pairing_points() const { return has_pairing_points_; } + /** + * @brief Check if pairings points have been set to public + * @return true if pairing points have been set to public + */ + bool has_public_pairing_points() const { return has_public_pairing_points_; } + /** * @brief Get the tag for a specific pairing point index */ uint32_t get_tag(uint32_t tag_index) const { return pairing_points_tags_.at(tag_index); } + + /** + * @brief Record that pairing points have been set to public + */ + void set_public_pairing_points() + { + BB_ASSERT(!has_public_pairing_points_, + "Trying to set pairing points to public for a circuit that already has public pairing points."); + has_public_pairing_points_ = true; + } }; } // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp index 94756659b3b4..d6cb9ae40d49 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp @@ -135,13 +135,16 @@ template class ProverInstance_ { vinfo("Constructing ProverInstance"); auto start = std::chrono::steady_clock::now(); - // Check pairing point tagging invariant: either no pairing points were created, + // Check pairing point tagging: either no pairing points were created, // or all pairing points have been aggregated into a single equivalence class - BB_ASSERT(circuit.has_single_pairing_point_tag(), + BB_ASSERT(circuit.pairing_points_tagging.has_single_pairing_point_tag(), "Pairing points must all be aggregated together. Either no pairing points should be created, or " "all created pairing points must be aggregated into a single pairing point. Found ", - circuit.num_unique_pairing_points(), + circuit.pairing_points_tagging.num_unique_pairing_points(), " different pairing points."); + // Check pairing point tagging: check that the pairing points have been set to public + BB_ASSERT(circuit.pairing_points_tagging.has_public_pairing_points(), + "Pairing points must be set to public in the circuit before constructing the ProverInstance."); // Decider proving keys can be constructed multiple times, hence, we check whether the circuit has been // finalized From 4c964eb984dded82030b218ea1d2d176b635b53c Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:32:00 +0000 Subject: [PATCH 08/11] Fixes --- .../stdlib_circuit_builders/circuit_builder_base.hpp | 2 +- .../stdlib_circuit_builders/pairing_points_tagging.hpp | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp index dd26d0994469..6b2110dbeb84 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/circuit_builder_base.hpp @@ -93,7 +93,7 @@ template class CircuitBuilderBase { * @brief PairingPoints tagging tool, used to ensure that all pairing points created in this circuit are aggregated * together. This is not related to circuit logic. */ - PairingPointsTagging pairing_points_tagging; + mutable PairingPointsTagging pairing_points_tagging; /** * @brief Map from witness index to real variable index diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp index 4b2225065224..a0a07e8da3d9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp @@ -27,13 +27,6 @@ class PairingPointsTagging { public: PairingPointsTagging() = default; - PairingPointsTagging(const PairingPointsTagging& other) = delete; - PairingPointsTagging(PairingPointsTagging&& other) noexcept = delete; - PairingPointsTagging& operator=(const PairingPointsTagging& other) = delete; - PairingPointsTagging& operator=(PairingPointsTagging&& other) noexcept = delete; - ~PairingPointsTagging() = default; - - bool operator==(const PairingPointsTagging& other) const = default; /** * @brief Create a new unique pairing point tag From 62075c651f5d4f5ba1898e5960a46924f7771963 Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:35:42 +0000 Subject: [PATCH 09/11] Fixes --- .../stdlib_circuit_builders/pairing_points_tagging.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp index a0a07e8da3d9..a93123adc5d4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/pairing_points_tagging.hpp @@ -27,6 +27,13 @@ class PairingPointsTagging { public: PairingPointsTagging() = default; + PairingPointsTagging(const PairingPointsTagging& other) = default; + PairingPointsTagging(PairingPointsTagging&& other) noexcept = default; + PairingPointsTagging& operator=(const PairingPointsTagging& other) = default; + PairingPointsTagging& operator=(PairingPointsTagging&& other) noexcept = default; + ~PairingPointsTagging() = default; + + bool operator==(const PairingPointsTagging& other) const = default; /** * @brief Create a new unique pairing point tag From ff1f40280602c8afeeffdeeb806622a6d463f35e Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:14:47 +0000 Subject: [PATCH 10/11] Fixes --- .../graph_description_goblin.test.cpp | 6 +++++- .../goblin_recursive_verifier.test.cpp | 10 ++++++++-- .../translator_recursive_verifier.test.cpp | 11 +++++++++-- .../src/barretenberg/ultra_honk/prover_instance.hpp | 3 ++- .../recursion/recursive_verifier.test.cpp | 12 ++++++++---- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_goblin.test.cpp b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_goblin.test.cpp index 2f6230c8c97d..b7813e9d3245 100644 --- a/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_goblin.test.cpp +++ b/barretenberg/cpp/src/barretenberg/boomerang_value_detection/graph_description_goblin.test.cpp @@ -85,7 +85,11 @@ TEST_F(BoomerangGoblinRecursiveVerifierTests, graph_description_basic) GoblinRecursiveVerifier verifier{ &builder, verifier_input }; GoblinRecursiveVerifierOutput output = verifier.verify(proof, recursive_merge_commitments, MergeSettings::APPEND); - output.points_accumulator.set_public(); + + stdlib::recursion::honk::DefaultIO inputs; + inputs.pairing_inputs = output.points_accumulator; + inputs.set_public(); + // Construct and verify a proof for the Goblin Recursive Verifier circuit { auto prover_instance = std::make_shared(builder); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.test.cpp index cafcd12d6e3c..866bc7ba7eaa 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/goblin_verifier/goblin_recursive_verifier.test.cpp @@ -145,7 +145,10 @@ TEST_F(GoblinRecursiveVerifierTests, Basic) GoblinRecursiveVerifier verifier{ &builder, verifier_input }; GoblinRecursiveVerifierOutput output = verifier.verify(proof, recursive_merge_commitments, MergeSettings::APPEND); - output.points_accumulator.set_public(); + + stdlib::recursion::honk::DefaultIO inputs; + inputs.pairing_inputs = output.points_accumulator; + inputs.set_public(); info("Recursive Verifier: num gates = ", builder.num_gates()); @@ -181,7 +184,10 @@ TEST_F(GoblinRecursiveVerifierTests, IndependentVKHash) GoblinRecursiveVerifier verifier{ &builder, verifier_input }; GoblinRecursiveVerifierOutput output = verifier.verify(proof, recursive_merge_commitments, MergeSettings::APPEND); - output.points_accumulator.set_public(); + + stdlib::recursion::honk::DefaultIO inputs; + inputs.pairing_inputs = output.points_accumulator; + inputs.set_public(); info("Recursive Verifier: num gates = ", builder.num_gates()); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.test.cpp index 594aed08f2b2..bef3a1d6e250 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/translator_vm_verifier/translator_recursive_verifier.test.cpp @@ -2,6 +2,7 @@ #include "barretenberg/circuit_checker/translator_circuit_checker.hpp" #include "barretenberg/common/log.hpp" #include "barretenberg/stdlib/honk_verifier/ultra_verification_keys_comparator.hpp" +#include "barretenberg/stdlib/special_public_inputs/special_public_inputs.hpp" #include "barretenberg/translator_vm/translator_verifier.hpp" #include "barretenberg/ultra_honk/ultra_prover.hpp" #include "barretenberg/ultra_honk/ultra_verifier.hpp" @@ -115,7 +116,10 @@ class TranslatorRecursiveTests : public ::testing::Test { RecursiveVerifier verifier{ &outer_circuit, verification_key, transcript }; typename RecursiveVerifier::PairingPoints pairing_points = verifier.verify_proof(proof, evaluation_challenge_x, batching_challenge_v); - pairing_points.set_public(); + + stdlib::recursion::honk::DefaultIO inputs; + inputs.pairing_inputs = pairing_points; + inputs.set_public(); info("Recursive Verifier: num gates = ", outer_circuit.num_gates()); // Check for a failure flag in the recursive verifier circuit @@ -198,7 +202,10 @@ class TranslatorRecursiveTests : public ::testing::Test { transcript->add_to_hash_buffer("batching_challenge_v", stdlib_batching_challenge_v); typename RecursiveVerifier::PairingPoints pairing_points = verifier.verify_proof(inner_proof, stdlib_evaluation_challenge_x, stdlib_batching_challenge_v); - pairing_points.set_public(); + + stdlib::recursion::honk::DefaultIO inputs; + inputs.pairing_inputs = pairing_points; + inputs.set_public(); auto outer_proving_key = std::make_shared(outer_circuit); auto outer_verification_key = diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp index d6cb9ae40d49..b6909ed53f06 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/prover_instance.hpp @@ -143,7 +143,8 @@ template class ProverInstance_ { circuit.pairing_points_tagging.num_unique_pairing_points(), " different pairing points."); // Check pairing point tagging: check that the pairing points have been set to public - BB_ASSERT(circuit.pairing_points_tagging.has_public_pairing_points(), + BB_ASSERT(circuit.pairing_points_tagging.has_public_pairing_points() || + !circuit.pairing_points_tagging.has_pairing_points(), "Pairing points must be set to public in the circuit before constructing the ProverInstance."); // Decider proving keys can be constructed multiple times, hence, we check whether the circuit has been diff --git a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.test.cpp index 82b5835aa65a..0a5076f0c692 100644 --- a/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm2/constraining/recursion/recursive_verifier.test.cpp @@ -127,8 +127,10 @@ TEST_F(AvmRecursiveTests, GoblinRecursion) return result; }(); - verifier_output.points_accumulator.set_public(); - verifier_output.ipa_claim.set_public(); + stdlib::recursion::honk::RollupIO inputs; + inputs.pairing_inputs = verifier_output.points_accumulator; + inputs.ipa_claim = verifier_output.ipa_claim; + inputs.set_public(); outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value(); // Ensure that the pairing check is satisfied on the outputs of the recursive verifier @@ -226,8 +228,10 @@ TEST_F(AvmRecursiveTests, GoblinRecursionWithoutPIValidation) return result; }(); - verifier_output.points_accumulator.set_public(); - verifier_output.ipa_claim.set_public(); + stdlib::recursion::honk::RollupIO inputs; + inputs.pairing_inputs = verifier_output.points_accumulator; + inputs.ipa_claim = verifier_output.ipa_claim; + inputs.set_public(); outer_circuit.ipa_proof = verifier_output.ipa_proof.get_value(); // Ensure that the pairing check is satisfied on the outputs of the recursive verifier From 5c8c3b9aca708a45cbf168425921c57aa7c6087e Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:15:56 +0000 Subject: [PATCH 11/11] Fix --- .../solidity_helpers/circuits/recursive_circuit.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp index a853f89e2999..5360ba5febbb 100644 --- a/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp +++ b/barretenberg/cpp/src/barretenberg/solidity_helpers/circuits/recursive_circuit.hpp @@ -92,7 +92,10 @@ class RecursiveCircuit { StdlibProof stdlib_inner_proof(outer_circuit, inner_proof); VerifierOutput output = verifier.template verify_proof(stdlib_inner_proof); - output.points_accumulator.set_public(); + + stdlib::recursion::honk::DefaultIO public_inputs; + public_inputs.pairing_inputs = output.points_accumulator; + public_inputs.set_public(); return outer_circuit; }