From c5feab70a5154c11d3b3406ba8fc616106d31233 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 8 Mar 2024 13:29:39 +0000 Subject: [PATCH 01/26] Added some tests --- .../commitment_schemes/ipa/ipa.hpp | 1 - .../commitment_schemes/ipa/ipa.test.cpp | 52 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 7637cec8a67..bee64c9a779 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -116,7 +116,6 @@ template class IPA { const Polynomial& polynomial, const std::shared_ptr& transcript) { - ASSERT(opening_pair.challenge != 0 && "The challenge point should not be zero"); auto poly_length = static_cast(polynomial.size()); // Step 1. diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 719be120fda..72f9ec66cf3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -44,6 +44,58 @@ TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) EXPECT_EQ(expected.normalize(), commitment.normalize()); } +TEST_F(IPATest, OpenZeroPolynomial) +{ + using IPA = IPA; + constexpr size_t n = 4; + Polynomial poly(n); + // Commit to a zero polynomial + GroupElement commitment = this->commit(poly); + EXPECT_TRUE(commitment.is_point_at_infinity()); + + auto [x, eval] = this->random_eval(poly); + EXPECT_EQ(eval, Fr::zero()); + const OpeningPair opening_pair = { x, eval }; + const OpeningClaim opening_claim{ opening_pair, commitment }; + + // initialize empty prover transcript + auto prover_transcript = std::make_shared(); + IPA::compute_opening_proof(this->ck(), opening_pair, poly, prover_transcript); + + // initialize verifier transcript from proof data + auto verifier_transcript = std::make_shared(prover_transcript->proof_data); + + auto result = IPA::verify(this->vk(), opening_claim, verifier_transcript); + EXPECT_TRUE(result); + + EXPECT_EQ(prover_transcript->get_manifest(), verifier_transcript->get_manifest()); +} + +TEST_F(IPATest, OpenAtZero) +{ + using IPA = IPA; + // generate a random polynomial, degree needs to be a power of two + size_t n = 128; + auto poly = this->random_polynomial(n); + Fr x = Fr::zero(); + auto eval = poly.evaluate(x); + auto commitment = this->commit(poly); + const OpeningPair opening_pair = { x, eval }; + const OpeningClaim opening_claim{ opening_pair, commitment }; + + // initialize empty prover transcript + auto prover_transcript = std::make_shared(); + IPA::compute_opening_proof(this->ck(), opening_pair, poly, prover_transcript); + + // initialize verifier transcript from proof data + auto verifier_transcript = std::make_shared(prover_transcript->proof_data); + + auto result = IPA::verify(this->vk(), opening_claim, verifier_transcript); + EXPECT_TRUE(result); + + EXPECT_EQ(prover_transcript->get_manifest(), verifier_transcript->get_manifest()); +} + TEST_F(IPATest, Commit) { constexpr size_t n = 128; From 8c7a4bfbfe409b126a9513048077da10fb6e2517 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 8 Mar 2024 14:50:11 +0000 Subject: [PATCH 02/26] add friend --- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp | 5 ++++- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index bee64c9a779..0883d87bc77 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -10,7 +10,6 @@ #include namespace bb { - /** * @brief IPA (inner product argument) commitment scheme class. * @@ -77,6 +76,10 @@ template class IPA { using VK = VerifierCommitmentKey; using Polynomial = bb::Polynomial; +#ifdef IPA_TEST + friend IPATest; +#endif + public: /** * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 72f9ec66cf3..7a48ca06ddc 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -1,4 +1,3 @@ -#include "ipa.hpp" #include "../gemini/gemini.hpp" #include "../shplonk/shplonk.hpp" #include "barretenberg/commitment_schemes/commitment_key.test.hpp" @@ -24,6 +23,9 @@ class IPATest : public CommitmentTest { }; } // namespace +#define IPA_TEST +#include "ipa.hpp" + TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) { constexpr size_t n = 4; From f93c3b6ed5ef946600d6d458b4aa6195a31f9238 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 8 Mar 2024 19:23:26 +0000 Subject: [PATCH 03/26] Zero challenge tests --- .../commitment_schemes/ipa/ipa.hpp | 84 +++++++++++-- .../commitment_schemes/ipa/ipa.test.cpp | 115 +++++++++++++++++- 2 files changed, 183 insertions(+), 16 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 0883d87bc77..c2254eda5fb 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -25,8 +25,9 @@ namespace bb { *The opening and verification procedures expect that there already exists a commitment to \f$f(x)\f$ which is the *scalar product \f$[f(x)]=\langle\vec{f},\vec{G}\rangle\f$, where \f$\vec{f}=(f_0, f_1,..., f_{d-1})\f$​ * - * The opening procedure documentation can be found in the description of \link IPA::compute_opening_proof - compute_opening_proof \endlink. The verification procedure documentation is in \link IPA::verify verify \endlink + * The opening procedure documentation can be found in the description of \link IPA::compute_opening_proof_internal + compute_opening_proof_internal \endlink. The verification procedure documentation is in \link IPA::verify_internal + verify_internal \endlink * * @tparam Curve * @@ -77,13 +78,13 @@ template class IPA { using Polynomial = bb::Polynomial; #ifdef IPA_TEST - friend IPATest; + FRIEND_TEST(IPATest, ChallengesAreZero); #endif - public: /** - * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point + * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point. * + * @tparam Transcript Transcript type. Useful for testing * @param ck The commitment key containing srs and pippenger_runtime_state for computing MSM * @param opening_pair (challenge, evaluation) * @param polynomial The witness polynomial whose opening proof needs to be computed @@ -114,10 +115,11 @@ template class IPA { * *7. Send the final \f$\vec{a}_{0} = (a_0)\f$ to the verifier */ - static void compute_opening_proof(const std::shared_ptr& ck, - const OpeningPair& opening_pair, - const Polynomial& polynomial, - const std::shared_ptr& transcript) + template + static void compute_opening_proof_internal(const std::shared_ptr& ck, + const OpeningPair& opening_pair, + const Polynomial& polynomial, + const std::shared_ptr& transcript) { auto poly_length = static_cast(polynomial.size()); @@ -129,6 +131,10 @@ template class IPA { // Receive challenge for the auxiliary generator const Fr generator_challenge = transcript->template get_challenge("IPA:generator_challenge"); + if (generator_challenge.is_zero()) { + throw_or_abort("The generator challenge can't be zero"); + } + // Step 3. // Compute auxiliary generator U auto aux_generator = Commitment::one() * generator_challenge; @@ -248,7 +254,11 @@ template class IPA { // Step 6.d // Receive the challenge from the verifier - const Fr round_challenge = transcript->get_challenge("IPA:round_challenge_" + index); + const Fr round_challenge = transcript->template get_challenge("IPA:round_challenge_" + index); + + if (round_challenge.is_zero()) { + throw_or_abort("IPA round challenge is zero"); + } const Fr round_challenge_inv = round_challenge.invert(); // Step 6.e @@ -287,6 +297,7 @@ template class IPA { /** * @brief Verify the correctness of a Proof * + * @tparam Transcript Allows to specify a transcript class. Useful for testing * @param vk Verification_key containing srs and pippenger_runtime_state to be used for MSM * @param opening_claim Contains the commitment C and opening pair \f$(\beta, f(\beta))\f$ * @param transcript Transcript with elements from the prover and generated challenges @@ -309,9 +320,10 @@ template class IPA { * * */ - static bool verify(const std::shared_ptr& vk, - const OpeningClaim& opening_claim, - const std::shared_ptr& transcript) + template + static bool verify_internal(const std::shared_ptr& vk, + const OpeningClaim& opening_claim, + const std::shared_ptr& transcript) { // Step 1. // Receive polynomial_degree + 1 = d from the prover @@ -321,6 +333,10 @@ template class IPA { // Step 2. // Receive generator challenge u and compute auxiliary generator const Fr generator_challenge = transcript->template get_challenge("IPA:generator_challenge"); + + if (generator_challenge.is_zero()) { + throw_or_abort("The generator challenge can't be zero"); + } auto aux_generator = Commitment::one() * generator_challenge; auto log_poly_degree = static_cast(numeric::get_msb(poly_length)); @@ -342,6 +358,9 @@ template class IPA { auto element_L = transcript->template receive_from_prover("IPA:L_" + index); auto element_R = transcript->template receive_from_prover("IPA:R_" + index); round_challenges[i] = transcript->template get_challenge("IPA:round_challenge_" + index); + if (round_challenges[i].is_zero()) { + throw_or_abort("Round challenges can't be zero"); + } round_challenges_inv[i] = round_challenges[i].invert(); msm_elements[2 * i] = element_L; @@ -432,6 +451,45 @@ template class IPA { // Check if C_right == C₀ return (C_zero.normalize() == right_hand_side.normalize()); } + + public: + /** + * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point. + * + * @tparam Transcript Transcript type. Useful for testing + * @param ck The commitment key containing srs and pippenger_runtime_state for computing MSM + * @param opening_pair (challenge, evaluation) + * @param polynomial The witness polynomial whose opening proof needs to be computed + * @param transcript Prover transcript + * + * @remark Detailed documentation can be found in \link IPA::compute_opening_proof_internal + * compute_opening_proof_internal \endlink. + */ + static void compute_opening_proof(const std::shared_ptr& ck, + const OpeningPair& opening_pair, + const Polynomial& polynomial, + const std::shared_ptr& transcript) + { + compute_opening_proof_internal(ck, opening_pair, polynomial, transcript); + } + + /** + * @brief Verify the correctness of a Proof + * + * @param vk Verification_key containing srs and pippenger_runtime_state to be used for MSM + * @param opening_claim Contains the commitment C and opening pair \f$(\beta, f(\beta))\f$ + * @param transcript Transcript with elements from the prover and generated challenges + * + * @return true/false depending on if the proof verifies + * + *@remark The verification procedure documentation is in \link IPA::verify_internal verify_internal \endlink + */ + static bool verify(const std::shared_ptr& vk, + const OpeningClaim& opening_claim, + const std::shared_ptr& transcript) + { + return verify_internal(vk, opening_claim, transcript); + } }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 7a48ca06ddc..0cb4f43bc0d 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -8,6 +8,8 @@ #include "barretenberg/polynomials/polynomial_arithmetic.hpp" #include +#include + using namespace bb; namespace { @@ -21,10 +23,72 @@ class IPATest : public CommitmentTest { using VK = VerifierCommitmentKey; using Polynomial = bb::Polynomial; }; + +/** + * @brief Class for sending arbitrary challenges through the transcript + * + */ +class MockTranscript { + public: + std::vector challenges; + std::vector group_elements; + std::vector field_elements; + size_t current_challenge_index = 0; + size_t current_field_index = 0; + size_t current_group_index = 0; + void reset(std::vector challenges_, + std::vector group_elements_ = {}, + std::vector field_elements_ = {}) + { + challenges = std::move(challenges_); + current_challenge_index = 0; + current_field_index = 0; + current_group_index = 0; + group_elements = std::move(group_elements_); + field_elements = std::move(field_elements_); + } + template void send_to_verifier(const std::string&, const T&) {} + template T get_challenge(const std::string&) + { + ASSERT(challenges.size() > current_challenge_index); + T result = challenges[current_challenge_index]; + current_challenge_index++; + return result; + } + template T receive_from_prover(const std::string&) { abort(); } + template <> Curve::ScalarField receive_from_prover(const std::string&) + { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + template <> Curve::BaseField receive_from_prover(const std::string&) + { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + template <> Curve::AffineElement receive_from_prover(const std::string&) + { + ASSERT(group_elements.size() > current_group_index); + return group_elements[current_group_index++]; + } +}; } // namespace #define IPA_TEST #include "ipa.hpp" +// namespace bb { +// class ProxyCaller { +// public: +// template +// static void compute_opening_proof_for_testing(const std::shared_ptr& ck, +// const OpeningPair& opening_pair, +// const IPATest::Polynomial& polynomial, +// const std::shared_ptr& transcript) +// { +// bb::IPA::compute_opening_proof_internal(ck, opening_pair, polynomial, transcript); +// } +// }; +// } // namespace bb TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) { @@ -69,8 +133,6 @@ TEST_F(IPATest, OpenZeroPolynomial) auto result = IPA::verify(this->vk(), opening_claim, verifier_transcript); EXPECT_TRUE(result); - - EXPECT_EQ(prover_transcript->get_manifest(), verifier_transcript->get_manifest()); } TEST_F(IPATest, OpenAtZero) @@ -94,9 +156,56 @@ TEST_F(IPATest, OpenAtZero) auto result = IPA::verify(this->vk(), opening_claim, verifier_transcript); EXPECT_TRUE(result); +} - EXPECT_EQ(prover_transcript->get_manifest(), verifier_transcript->get_manifest()); +namespace bb { +#if !defined(__wasm__) +TEST_F(IPATest, ChallengesAreZero) +{ + using IPA = IPA; + // generate a random polynomial, degree needs to be a power of two + size_t n = 128; + auto poly = this->random_polynomial(n); + auto [x, eval] = this->random_eval(poly); + auto commitment = this->commit(poly); + const OpeningPair opening_pair = { x, eval }; + const OpeningClaim opening_claim{ opening_pair, commitment }; + + // initialize an empty mock transcript + auto transcript = std::make_shared(); + const size_t num_challenges = numeric::get_msb(n) + 1; + std::vector random_vector(num_challenges); + + // Generate a random element vector with challenges + for (size_t i = 0; i < num_challenges; i++) { + random_vector[i] = Fr::random_element(); + } + + // Compute opening proofs several times, where each time a different challenge is equal to zero. Should cause + // exceptions + for (size_t i = 0; i < num_challenges; i++) { + auto new_random_vector = random_vector; + new_random_vector[i] = Fr::zero(); + transcript->reset(new_random_vector); + EXPECT_ANY_THROW(IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript)); + } + // Fill out a vector of affine elements that the verifier receives from the prover with generators (we don't care + // about them right now) + std::vector lrs(num_challenges * 2); + for (size_t i = 0; i < num_challenges * 2; i++) { + lrs[i] = Curve::AffineElement::one(); + } + // Verify proofs several times, where each time a different challenge is equal to zero. Should cause + // exceptions + for (size_t i = 0; i < num_challenges; i++) { + auto new_random_vector = random_vector; + new_random_vector[i] = Fr::zero(); + transcript->reset(new_random_vector, lrs, { uint256_t(n) }); + EXPECT_ANY_THROW(IPA::verify_internal(this->vk(), opening_claim, transcript)); + } } +#endif +} // namespace bb TEST_F(IPATest, Commit) { From 6a239b0fe8f0a79143f4bf22787e9ff9248fc108 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 18:22:38 +0000 Subject: [PATCH 04/26] Stop ASAN shouting --- barretenberg/cpp/src/barretenberg/common/mem.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/common/mem.hpp b/barretenberg/cpp/src/barretenberg/common/mem.hpp index fe4a6351e9e..a26e615de8c 100644 --- a/barretenberg/cpp/src/barretenberg/common/mem.hpp +++ b/barretenberg/cpp/src/barretenberg/common/mem.hpp @@ -31,6 +31,10 @@ inline void* protected_aligned_alloc(size_t alignment, size_t size) { size += (size % alignment); void* t = nullptr; + // pad size to alignment + if (size % alignment != 0) { + size += alignment - (size % alignment); + } // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) t = aligned_alloc(alignment, size); if (t == nullptr) { From 36748e8970cec678238228e6df56a696c1c2cd05 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 18:31:27 +0000 Subject: [PATCH 05/26] added fuzzer --- .../commitment_schemes/ipa/ipa.fuzzer.cpp | 188 ++++++++++++++++++ .../commitment_schemes/ipa/ipa.hpp | 17 +- 2 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp new file mode 100644 index 00000000000..61d6d5c8db0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp @@ -0,0 +1,188 @@ +#define IPA_FUZZ_TEST +#include "ipa.hpp" +#include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/commitment_schemes/verification_key.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/srs/factories/file_crs_factory.hpp" + +namespace bb { + +constexpr size_t COMMITMENT_TEST_NUM_POINTS = 32; +using Curve = curve::Grumpkin; +std::shared_ptr> ck; +std::shared_ptr> vk; +class ProxyCaller { + public: + template + static void compute_opening_proof_internal(const std::shared_ptr>& ck, + const OpeningPair& opening_pair, + const Polynomial& polynomial, + const std::shared_ptr& transcript) + { + IPA::compute_opening_proof_internal(ck, opening_pair, polynomial, transcript); + } + template + static bool verify_internal(const std::shared_ptr>& vk, + const OpeningClaim& opening_claim, + const std::shared_ptr& transcript) + { + return IPA::verify_internal(vk, opening_claim, transcript); + } +}; +} // namespace bb +extern "C" void LLVMFuzzerInitialize(int*, char***) +{ + srs::init_grumpkin_crs_factory("../srs_db/ignition"); + ck = std::make_shared>(COMMITMENT_TEST_NUM_POINTS); + auto crs_factory = std::make_shared>("../srs_db/grumpkin", + COMMITMENT_TEST_NUM_POINTS); + vk = std::make_shared>(COMMITMENT_TEST_NUM_POINTS, crs_factory); +} + +class MockTranscript { + public: + std::vector challenges; + std::vector group_elements; + std::vector field_elements; + size_t current_challenge_index = 0; + size_t current_field_index = 0; + size_t current_group_index = 0; + void reset(std::vector challenges_, + std::vector group_elements_ = {}, + std::vector field_elements_ = {}) + { + challenges = std::move(challenges_); + current_challenge_index = 0; + current_field_index = 0; + current_group_index = 0; + group_elements = std::move(group_elements_); + field_elements = std::move(field_elements_); + } + void reset_for_verifier() + { + current_challenge_index = 0; + current_field_index = 0; + current_challenge_index = 0; + } + template void send_to_verifier(const std::string&, const T& field_element) + { + field_elements.push_back(static_cast(field_element)); + } + template <> void send_to_verifier(const std::string&, const Curve::AffineElement& group_element) + { + group_elements.push_back(group_element); + } + template T get_challenge(const std::string&) + { + ASSERT(challenges.size() > current_challenge_index); + if (current_challenge_index >= challenges.size()) { + abort(); + } + T result = challenges[current_challenge_index]; + current_challenge_index++; + return result; + } + template T receive_from_prover(const std::string&) { abort(); } + template <> Curve::ScalarField receive_from_prover(const std::string&) + { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + template <> Curve::BaseField receive_from_prover(const std::string&) + { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + template <> Curve::AffineElement receive_from_prover(const std::string&) + { + ASSERT(group_elements.size() > current_group_index); + return group_elements[current_group_index++]; + } +}; + +#define IPA_FUZZ_TEST +#include "ipa.hpp" + +/** + * @brief A very primitive fuzzer for the composer + * + * @details Super-slow. Shouldn't be run on its own. First you need to run the circuit builder fuzzer, then minimize the + * corpus and then just use that corpus + * + */ +extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) +{ + using Fr = grumpkin::fr; + using Polynomial = Polynomial; + // We need data + if (size == 0) { + return 0; + } + const auto log_size = static_cast(data[0]); + if (log_size == 0 || log_size > 2) { + return 0; + } + auto offset = data + 1; + const auto num_challenges = log_size + 1; + // How much data do we need? + // Challenges: sizeof(uint256_t) * num_challenges + 1 for montgomery switch + // Polynomial: sizeof(uint256_t) * size + 1 per size/8 + // Eval x: sizeof(uint256_t) + 1 + const size_t polynomial_size = (1 << log_size); + + const size_t expected_size = sizeof(uint256_t) * (num_challenges + polynomial_size + 1) + 3 + + (polynomial_size < 8 ? 1 : polynomial_size / 8); + if (size < expected_size) { + return 0; + } + + auto transcript = std::make_shared(); + + std::vector challenges(num_challenges); + const auto control_byte = offset[0]; + offset++; + for (size_t i = 0; i < num_challenges; i++) { + auto challenge = *(uint256_t*)(offset); + + if ((control_byte >> i) & 1) { + auto field_challenge = Fr(challenge); + + challenge = field_challenge.from_montgomery_form(); + } + if (Fr(challenge).is_zero()) { + return 0; + } + challenges[i] = challenge; + offset += sizeof(uint256_t); + } + transcript->reset(challenges); + std::vector polynomial_coefficients(polynomial_size); + for (size_t i = 0; i < polynomial_size; i++) { + polynomial_coefficients[i] = *(uint256_t*)(offset); + offset += sizeof(uint256_t); + } + Polynomial poly(polynomial_size); + for (size_t i = 0; i < polynomial_size; i++) { + auto b = offset[i / 8]; + + poly[i] = polynomial_coefficients[i]; + if ((b >> (i % 8)) & 1) { + poly[i].self_from_montgomery_form(); + } + } + offset += (polynomial_size < 8 ? 1 : polynomial_size / 8); + auto x = Fr(*(uint256_t*)offset); + offset += sizeof(uint256_t); + if (offset[0] & 1) { + x.self_from_montgomery_form(); + } + auto const opening_pair = OpeningPair{ x, poly.evaluate(x) }; + auto const opening_claim = OpeningClaim{ opening_pair, ck->commit(poly) }; + ProxyCaller::compute_opening_proof_internal(ck, opening_pair, poly, transcript); + transcript->reset_for_verifier(); + + if (!ProxyCaller::verify_internal(vk, opening_claim, transcript)) { + return 1; + } + return 0; +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index c2254eda5fb..c2db3c13ded 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -10,6 +10,8 @@ #include namespace bb { +// clang-format off + /** * @brief IPA (inner product argument) commitment scheme class. * @@ -70,6 +72,7 @@ namespace bb { documentation */ template class IPA { + // clang-fromat on using Fr = typename Curve::ScalarField; using GroupElement = typename Curve::Element; using Commitment = typename Curve::AffineElement; @@ -80,6 +83,10 @@ template class IPA { #ifdef IPA_TEST FRIEND_TEST(IPATest, ChallengesAreZero); #endif +#ifdef IPA_FUZZ_TEST + friend class ProxyCaller; +#endif + // clang-format off /** * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point. @@ -121,6 +128,7 @@ template class IPA { const Polynomial& polynomial, const std::shared_ptr& transcript) { + // clang-format on auto poly_length = static_cast(polynomial.size()); // Step 1. @@ -328,8 +336,9 @@ template class IPA { // Step 1. // Receive polynomial_degree + 1 = d from the prover auto poly_length = static_cast(transcript->template receive_from_prover( - "IPA:poly_degree_plus_1")); // note this is base field because this is a uint32_t, which should map to a - // bb::fr, not a grumpkin::fr, which is a BaseField element for Grumpkin + "IPA:poly_degree_plus_1")); // note this is base field because this is a uint32_t, which should map + // to a bb::fr, not a grumpkin::fr, which is a BaseField element for + // Grumpkin // Step 2. // Receive generator challenge u and compute auxiliary generator const Fr generator_challenge = transcript->template get_challenge("IPA:generator_challenge"); @@ -390,8 +399,8 @@ template class IPA { // Construct vector s std::vector s_vec(poly_length); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/857): This code is not efficient as its O(nlogn). - // This can be optimized to be linear by computing a tree of products. Its very readable, so we're + // TODO(https://github.com/AztecProtocol/barretenberg/issues/857): This code is not efficient as its + // O(nlogn). This can be optimized to be linear by computing a tree of products. Its very readable, so we're // leaving it unoptimized for now. run_loop_in_parallel_if_effective( poly_length, From 5f6a937b040f5d94510f989b4c0c9cad3353951c Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 18:49:25 +0000 Subject: [PATCH 06/26] Move transcript into separate file --- .../commitment_schemes/ipa/ipa.fuzzer.cpp | 62 +----------------- .../commitment_schemes/ipa/ipa.test.cpp | 64 +------------------ .../ipa/mock_transcript.hpp | 63 ++++++++++++++++++ 3 files changed, 65 insertions(+), 124 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp index 61d6d5c8db0..b7697e494b2 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp @@ -1,5 +1,6 @@ #define IPA_FUZZ_TEST #include "ipa.hpp" +#include "./mock_transcript.hpp" #include "barretenberg/commitment_schemes/commitment_key.hpp" #include "barretenberg/commitment_schemes/verification_key.hpp" #include "barretenberg/polynomials/polynomial.hpp" @@ -39,67 +40,6 @@ extern "C" void LLVMFuzzerInitialize(int*, char***) vk = std::make_shared>(COMMITMENT_TEST_NUM_POINTS, crs_factory); } -class MockTranscript { - public: - std::vector challenges; - std::vector group_elements; - std::vector field_elements; - size_t current_challenge_index = 0; - size_t current_field_index = 0; - size_t current_group_index = 0; - void reset(std::vector challenges_, - std::vector group_elements_ = {}, - std::vector field_elements_ = {}) - { - challenges = std::move(challenges_); - current_challenge_index = 0; - current_field_index = 0; - current_group_index = 0; - group_elements = std::move(group_elements_); - field_elements = std::move(field_elements_); - } - void reset_for_verifier() - { - current_challenge_index = 0; - current_field_index = 0; - current_challenge_index = 0; - } - template void send_to_verifier(const std::string&, const T& field_element) - { - field_elements.push_back(static_cast(field_element)); - } - template <> void send_to_verifier(const std::string&, const Curve::AffineElement& group_element) - { - group_elements.push_back(group_element); - } - template T get_challenge(const std::string&) - { - ASSERT(challenges.size() > current_challenge_index); - if (current_challenge_index >= challenges.size()) { - abort(); - } - T result = challenges[current_challenge_index]; - current_challenge_index++; - return result; - } - template T receive_from_prover(const std::string&) { abort(); } - template <> Curve::ScalarField receive_from_prover(const std::string&) - { - ASSERT(field_elements.size() > current_field_index); - return field_elements[current_field_index++]; - } - template <> Curve::BaseField receive_from_prover(const std::string&) - { - ASSERT(field_elements.size() > current_field_index); - return field_elements[current_field_index++]; - } - template <> Curve::AffineElement receive_from_prover(const std::string&) - { - ASSERT(group_elements.size() > current_group_index); - return group_elements[current_group_index++]; - } -}; - #define IPA_FUZZ_TEST #include "ipa.hpp" diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 0cb4f43bc0d..87dfc6fc548 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -1,5 +1,6 @@ #include "../gemini/gemini.hpp" #include "../shplonk/shplonk.hpp" +#include "./mock_transcript.hpp" #include "barretenberg/commitment_schemes/commitment_key.test.hpp" #include "barretenberg/common/mem.hpp" #include "barretenberg/ecc/curves/bn254/fq12.hpp" @@ -7,7 +8,6 @@ #include "barretenberg/polynomials/polynomial.hpp" #include "barretenberg/polynomials/polynomial_arithmetic.hpp" #include - #include using namespace bb; @@ -23,72 +23,10 @@ class IPATest : public CommitmentTest { using VK = VerifierCommitmentKey; using Polynomial = bb::Polynomial; }; - -/** - * @brief Class for sending arbitrary challenges through the transcript - * - */ -class MockTranscript { - public: - std::vector challenges; - std::vector group_elements; - std::vector field_elements; - size_t current_challenge_index = 0; - size_t current_field_index = 0; - size_t current_group_index = 0; - void reset(std::vector challenges_, - std::vector group_elements_ = {}, - std::vector field_elements_ = {}) - { - challenges = std::move(challenges_); - current_challenge_index = 0; - current_field_index = 0; - current_group_index = 0; - group_elements = std::move(group_elements_); - field_elements = std::move(field_elements_); - } - template void send_to_verifier(const std::string&, const T&) {} - template T get_challenge(const std::string&) - { - ASSERT(challenges.size() > current_challenge_index); - T result = challenges[current_challenge_index]; - current_challenge_index++; - return result; - } - template T receive_from_prover(const std::string&) { abort(); } - template <> Curve::ScalarField receive_from_prover(const std::string&) - { - ASSERT(field_elements.size() > current_field_index); - return field_elements[current_field_index++]; - } - template <> Curve::BaseField receive_from_prover(const std::string&) - { - ASSERT(field_elements.size() > current_field_index); - return field_elements[current_field_index++]; - } - template <> Curve::AffineElement receive_from_prover(const std::string&) - { - ASSERT(group_elements.size() > current_group_index); - return group_elements[current_group_index++]; - } -}; } // namespace #define IPA_TEST #include "ipa.hpp" -// namespace bb { -// class ProxyCaller { -// public: -// template -// static void compute_opening_proof_for_testing(const std::shared_ptr& ck, -// const OpeningPair& opening_pair, -// const IPATest::Polynomial& polynomial, -// const std::shared_ptr& transcript) -// { -// bb::IPA::compute_opening_proof_internal(ck, opening_pair, polynomial, transcript); -// } -// }; -// } // namespace bb TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp new file mode 100644 index 00000000000..885ad8934fc --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -0,0 +1,63 @@ +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include +class MockTranscript { + public: + std::vector challenges; + std::vector group_elements; + std::vector field_elements; + size_t current_challenge_index = 0; + size_t current_field_index = 0; + size_t current_group_index = 0; + void reset(std::vector challenges_, + std::vector group_elements_ = {}, + std::vector field_elements_ = {}) + { + challenges = std::move(challenges_); + current_challenge_index = 0; + current_field_index = 0; + current_group_index = 0; + group_elements = std::move(group_elements_); + field_elements = std::move(field_elements_); + } + void reset_for_verifier() + { + current_challenge_index = 0; + current_field_index = 0; + current_challenge_index = 0; + } + template void send_to_verifier(const std::string&, const T& field_element) + { + field_elements.push_back(static_cast(field_element)); + } + template <> void send_to_verifier(const std::string&, const bb::curve::Grumpkin::AffineElement& group_element) + { + group_elements.push_back(group_element); + } + template T get_challenge(const std::string&) + { + ASSERT(challenges.size() > current_challenge_index); + if (current_challenge_index >= challenges.size()) { + abort(); + } + T result = challenges[current_challenge_index]; + current_challenge_index++; + return result; + } + template T receive_from_prover(const std::string&) { abort(); } + template <> bb::curve::Grumpkin::ScalarField receive_from_prover(const std::string&) + { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + template <> bb::curve::Grumpkin::BaseField receive_from_prover(const std::string&) + { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + template <> bb::curve::Grumpkin::AffineElement receive_from_prover(const std::string&) + { + ASSERT(group_elements.size() > current_group_index); + return group_elements[current_group_index++]; + } +}; \ No newline at end of file From cb3aa08f065156856c2e44a5789e857d1ca6cb60 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 20:08:01 +0000 Subject: [PATCH 07/26] Fix GCC --- .../commitment_schemes/ipa/mock_transcript.hpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp index 885ad8934fc..76685cd8daf 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -1,6 +1,9 @@ #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" #include +#pragma GCC diagnostic push +// GCC bug in template specialization +#pragma GCC diagnostic ignored "-Wfatal-errors" class MockTranscript { public: std::vector challenges; @@ -30,6 +33,7 @@ class MockTranscript { { field_elements.push_back(static_cast(field_element)); } + template <> void send_to_verifier(const std::string&, const bb::curve::Grumpkin::AffineElement& group_element) { group_elements.push_back(group_element); @@ -60,4 +64,6 @@ class MockTranscript { ASSERT(group_elements.size() > current_group_index); return group_elements[current_group_index++]; } -}; \ No newline at end of file +}; + +#pragma GCC diagnostic pop \ No newline at end of file From bd4f9899a916c20c31eaac3e9fd8fd2180e4277b Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 20:27:02 +0000 Subject: [PATCH 08/26] mb fix gcc --- .../commitment_schemes/ipa/mock_transcript.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp index 76685cd8daf..b3c1ed627b4 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -1,9 +1,7 @@ #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/numeric/uint256/uint256.hpp" #include -#pragma GCC diagnostic push -// GCC bug in template specialization -#pragma GCC diagnostic ignored "-Wfatal-errors" +namespace bb { class MockTranscript { public: std::vector challenges; @@ -65,5 +63,4 @@ class MockTranscript { return group_elements[current_group_index++]; } }; - -#pragma GCC diagnostic pop \ No newline at end of file +} // namespace bb \ No newline at end of file From 9d90f1cbb10e2e9411c7980791e8b28cbdc3c493 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 20:44:11 +0000 Subject: [PATCH 09/26] comments for mock --- .../ipa/mock_transcript.hpp | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp index b3c1ed627b4..8474ca28bd2 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -2,14 +2,34 @@ #include "barretenberg/numeric/uint256/uint256.hpp" #include namespace bb { +/** + * @brief Mock transcript class used by IPA tests and fuzzer + * + * @details This transcript can send previously determined challenges instead of ones generated by Fiat Shamir. It can + * also store elements received from the prover + * + */ class MockTranscript { public: + // Vector of challenges sent to the verifier std::vector challenges; + // Vector of group elements received from the prover / sent to the verifier std::vector group_elements; + // Vector of field elements received from the prover / sent to the verifier. uint256_t is used to ignore field type std::vector field_elements; + + // Indices of the elements being sampled size_t current_challenge_index = 0; size_t current_field_index = 0; size_t current_group_index = 0; + + /** + * @brief Reset the transcript (requires to submit the challenges) + * + * @param challenges_ Challenges that will be sent to the prover/verifier + * @param group_elements_ Group elements sent to the verifier + * @param field_elements_ Field elements sent to the verifier + */ void reset(std::vector challenges_, std::vector group_elements_ = {}, std::vector field_elements_ = {}) @@ -21,42 +41,74 @@ class MockTranscript { group_elements = std::move(group_elements_); field_elements = std::move(field_elements_); } + /** + * @brief Reset the indices of elements sampled after using the transcript with the prover + * + * @details After the transcipt received elements from the prover, this method allows to reset counters so that the + * verifier can receive those elements + */ void reset_for_verifier() { current_challenge_index = 0; current_field_index = 0; current_challenge_index = 0; } + /** + * @brief Send something that can be converted to uint256_t to the verifier (used for field elements) + * + */ template void send_to_verifier(const std::string&, const T& field_element) { field_elements.push_back(static_cast(field_element)); } + /** + * @brief Send a group element to the verifier + * + */ template <> void send_to_verifier(const std::string&, const bb::curve::Grumpkin::AffineElement& group_element) { group_elements.push_back(group_element); } + + /** + * @brief Get a challenge from the verifier + * + */ template T get_challenge(const std::string&) { - ASSERT(challenges.size() > current_challenge_index); - if (current_challenge_index >= challenges.size()) { - abort(); - } - T result = challenges[current_challenge_index]; + // No heap overreads, please + ASSERT(current_challenge_index >= challenges.size()); + T result = static_cast(challenges[current_challenge_index]); current_challenge_index++; return result; } + /** + * @brief Receive something unknown from prover (fails) + * + */ template T receive_from_prover(const std::string&) { abort(); } + + /** + * @brief Receive a field element from prover + */ template <> bb::curve::Grumpkin::ScalarField receive_from_prover(const std::string&) { ASSERT(field_elements.size() > current_field_index); return field_elements[current_field_index++]; } + /** + * @brief Receive a field element from prover + */ template <> bb::curve::Grumpkin::BaseField receive_from_prover(const std::string&) { ASSERT(field_elements.size() > current_field_index); return field_elements[current_field_index++]; } + /** + * @brief Receive an affine group element from prover + * + */ template <> bb::curve::Grumpkin::AffineElement receive_from_prover(const std::string&) { ASSERT(group_elements.size() > current_group_index); From 296aa07d660ab764a301dc93e5f89d275dbb4c43 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 20:56:17 +0000 Subject: [PATCH 10/26] Fix --- .../src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp index 8474ca28bd2..064212ec5f1 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -78,7 +78,7 @@ class MockTranscript { template T get_challenge(const std::string&) { // No heap overreads, please - ASSERT(current_challenge_index >= challenges.size()); + ASSERT(current_challenge_index < challenges.size()); T result = static_cast(challenges[current_challenge_index]); current_challenge_index++; return result; From 2336bdcea5cb8152cc55e5ddb0e1b102e6ac8f48 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 21:01:44 +0000 Subject: [PATCH 11/26] Comments --- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index c2db3c13ded..afb4bb780bf 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -80,6 +80,7 @@ template class IPA { using VK = VerifierCommitmentKey; using Polynomial = bb::Polynomial; +// These allow access to internal functions so that we can never use a mock transcript unless it's fuzzing or testing of IPA specifically #ifdef IPA_TEST FRIEND_TEST(IPATest, ChallengesAreZero); #endif @@ -465,7 +466,6 @@ template class IPA { /** * @brief Compute an inner product argument proof for opening a single polynomial at a single evaluation point. * - * @tparam Transcript Transcript type. Useful for testing * @param ck The commitment key containing srs and pippenger_runtime_state for computing MSM * @param opening_pair (challenge, evaluation) * @param polynomial The witness polynomial whose opening proof needs to be computed From f9cdaf00d728c6b2ed1e91928241972ee2853325 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 21:28:42 +0000 Subject: [PATCH 12/26] comment fuzzer --- .../commitment_schemes/ipa/ipa.fuzzer.cpp | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp index b7697e494b2..506a632230a 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp @@ -8,10 +8,15 @@ namespace bb { +// We actually only use 4, because fuzzing is very slow constexpr size_t COMMITMENT_TEST_NUM_POINTS = 32; using Curve = curve::Grumpkin; std::shared_ptr> ck; std::shared_ptr> vk; +/** + * @brief Class that allows us to call internal IPA methods, because it's friendly + * + */ class ProxyCaller { public: template @@ -31,6 +36,11 @@ class ProxyCaller { } }; } // namespace bb + +/** + * @brief Initialize SRS, commitment key, verification key + * + */ extern "C" void LLVMFuzzerInitialize(int*, char***) { srs::init_grumpkin_crs_factory("../srs_db/ignition"); @@ -40,15 +50,15 @@ extern "C" void LLVMFuzzerInitialize(int*, char***) vk = std::make_shared>(COMMITMENT_TEST_NUM_POINTS, crs_factory); } +// This define is needed to make ProxyClass a friend of IPA #define IPA_FUZZ_TEST #include "ipa.hpp" /** - * @brief A very primitive fuzzer for the composer - * - * @details Super-slow. Shouldn't be run on its own. First you need to run the circuit builder fuzzer, then minimize the - * corpus and then just use that corpus + * @brief A fuzzer for the IPA primitive * + * @details Parses the given data as a polynomial, a sequence of challenges for the transcript and the evaluation point, + * then opens the polynomial with IPA and verifies that the opening was correct */ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) { @@ -58,50 +68,65 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) if (size == 0) { return 0; } + // Get the logarighmic size of polynomial const auto log_size = static_cast(data[0]); + // More than 4 is so bad if (log_size == 0 || log_size > 2) { return 0; } - auto offset = data + 1; + const auto* offset = data + 1; const auto num_challenges = log_size + 1; // How much data do we need? // Challenges: sizeof(uint256_t) * num_challenges + 1 for montgomery switch // Polynomial: sizeof(uint256_t) * size + 1 per size/8 // Eval x: sizeof(uint256_t) + 1 const size_t polynomial_size = (1 << log_size); - - const size_t expected_size = sizeof(uint256_t) * (num_challenges + polynomial_size + 1) + 3 + - (polynomial_size < 8 ? 1 : polynomial_size / 8); + // Bytes controlling montgomery switching for polynomial coefficients + const size_t polynomial_control_bytes = (polynomial_size < 8 ? 1 : polynomial_size / 8); + const size_t expected_size = + sizeof(uint256_t) * (num_challenges + polynomial_size + 1) + 3 + polynomial_control_bytes; if (size < expected_size) { return 0; } + // Initialize transcript auto transcript = std::make_shared(); std::vector challenges(num_challenges); + // Get the byte, where bits control if we parse challenges in montgomery form or not const auto control_byte = offset[0]; offset++; + // Get challenges one by one for (size_t i = 0; i < num_challenges; i++) { auto challenge = *(uint256_t*)(offset); if ((control_byte >> i) & 1) { + // If control byte says so, parse the value from input as if it's internal state of the field (already + // converted to montgomery). This allows modifying the state directly auto field_challenge = Fr(challenge); challenge = field_challenge.from_montgomery_form(); } + // Challenges can't be zero if (Fr(challenge).is_zero()) { return 0; } challenges[i] = challenge; offset += sizeof(uint256_t); } + + // Put challenges into the transcript transcript->reset(challenges); + + // Parse polynomial std::vector polynomial_coefficients(polynomial_size); for (size_t i = 0; i < polynomial_size; i++) { polynomial_coefficients[i] = *(uint256_t*)(offset); offset += sizeof(uint256_t); } Polynomial poly(polynomial_size); + + // Convert from montgomery if the appropriate bit is set for (size_t i = 0; i < polynomial_size; i++) { auto b = offset[i / 8]; @@ -110,17 +135,22 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) poly[i].self_from_montgomery_form(); } } - offset += (polynomial_size < 8 ? 1 : polynomial_size / 8); + + offset += polynomial_control_bytes; + // Parse the x we are evaluating on auto x = Fr(*(uint256_t*)offset); offset += sizeof(uint256_t); - if (offset[0] & 1) { + if ((offset[0] & 1) != 0) { x.self_from_montgomery_form(); } auto const opening_pair = OpeningPair{ x, poly.evaluate(x) }; auto const opening_claim = OpeningClaim{ opening_pair, ck->commit(poly) }; ProxyCaller::compute_opening_proof_internal(ck, opening_pair, poly, transcript); + + // Reset challenge indices transcript->reset_for_verifier(); + // Should verify if (!ProxyCaller::verify_internal(vk, opening_claim, transcript)) { return 1; } From 30d5e555dae85ccb5d9e124d08715b2efe895903 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Mon, 11 Mar 2024 21:56:08 +0000 Subject: [PATCH 13/26] Created a test. Found a bug --- .../commitment_schemes/ipa/ipa.hpp | 6 ++++ .../commitment_schemes/ipa/ipa.test.cpp | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index afb4bb780bf..b6ddfb2f46d 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -83,6 +83,7 @@ template class IPA { // These allow access to internal functions so that we can never use a mock transcript unless it's fuzzing or testing of IPA specifically #ifdef IPA_TEST FRIEND_TEST(IPATest, ChallengesAreZero); + FRIEND_TEST(IPATest, AIsZeroAfterOneRound); #endif #ifdef IPA_FUZZ_TEST friend class ProxyCaller; @@ -269,6 +270,11 @@ template class IPA { throw_or_abort("IPA round challenge is zero"); } const Fr round_challenge_inv = round_challenge.invert(); + info("Inverse challenge: ", round_challenge_inv, "Modulus: ", Fr::modulus); + for (auto& point : std::span{ G_vec_local.begin() + static_cast(round_size), + G_vec_local.begin() + static_cast(round_size * 2) }) { + info(point, " : ", point.is_point_at_infinity()); + } // Step 6.e // G_vec_new = G_vec_lo + G_vec_hi * round_challenge_inv diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 87dfc6fc548..9b617fc8275 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -142,6 +142,42 @@ TEST_F(IPATest, ChallengesAreZero) EXPECT_ANY_THROW(IPA::verify_internal(this->vk(), opening_claim, transcript)); } } +TEST_F(IPATest, AIsZeroAfterOneRound) +{ + using IPA = IPA; + // generate a random polynomial, degree needs to be a power of two + size_t n = 4; + auto poly = Polynomial(n); + for (size_t i = 0; i < n / 2; i++) { + poly[i] = Fr::random_element(); + poly[i + (n / 2)] = poly[i]; + } + auto [x, eval] = this->random_eval(poly); + auto commitment = this->commit(poly); + const OpeningPair opening_pair = { x, eval }; + const OpeningClaim opening_claim{ opening_pair, commitment }; + + // initialize an empty mock transcript + auto transcript = std::make_shared(); + const size_t num_challenges = numeric::get_msb(n) + 1; + std::vector random_vector(num_challenges); + + // Generate a random element vector with challenges + for (size_t i = 0; i < num_challenges; i++) { + random_vector[i] = Fr::random_element(); + } + // Substitute the first folding challenge with -1 + random_vector[1] = -Fr::one(); + + // Put the challenges in the transcript + transcript->reset(random_vector); + + // Compute opening proof + IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript); + + // Verify + EXPECT_TRUE(IPA::verify_internal(this->vk(), opening_claim, transcript)); +} #endif } // namespace bb From 5bb8af7ddef67aa810e66a63abf7c0bb89c6858f Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Tue, 12 Mar 2024 15:06:31 +0000 Subject: [PATCH 14/26] fix bug --- .../ecc/groups/affine_element.test.cpp | 14 ++++++++++++ .../barretenberg/ecc/groups/element_impl.hpp | 22 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp index 38f0518fb34..1ea7050d021 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp @@ -213,4 +213,18 @@ TEST(AffineElement, InfinityBatchMulByScalarIsInfinity) grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, grumpkin::fr::random_element()); EXPECT_THAT(result, Each(Property(&grumpkin::g1::affine_element::is_point_at_infinity, Eq(true)))); +} + +// A regression test to make sure the -1 case is covered +TEST(AffineElement, BatchByMinusOne) +{ + constexpr size_t num_points = 2; + std::vector affine_points(num_points, grumpkin::g1::affine_element::one()); + + std::vector result = + grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, -grumpkin::fr::one()); + + for (size_t i = 0; i < num_points; i++) { + EXPECT_EQ(affine_points[i], -result[i]); + } } \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp index 78e3b6cccc3..0d1345267b6 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp @@ -901,6 +901,28 @@ std::vector> element::batch_mul_with_endomo /*finite_field_additions_per_iteration=*/7, /*finite_field_multiplications_per_iteration=*/6); }; + + // Since the core algorithm has no edgecase detection, it can't handle -1. (Because constructing -1 is r + -1 as + // skew and r⋅P = Point at infinity, which we can't handle in batch affine add). So we have to handle it separately + if (scalar == -Fr::one()) { + + std::vector results(num_points); + run_loop_in_parallel_if_effective( + num_points, + [&results, &points](size_t start, size_t end) { + for (size_t i = start; i < end; ++i) { + results[i] = -points[i]; + } + }, + /*finite_field_additions_per_iteration=*/0, + /*finite_field_multiplications_per_iteration=*/0, + /*finite_field_inversions_per_iteration=*/0, + /*group_element_additions_per_iteration=*/0, + /*group_element_doublings_per_iteration=*/0, + /*scalar_multiplications_per_iteration=*/0, + /*sequential_copy_ops_per_iteration=*/1); + return results; + } // Compute wnaf for scalar const Fr converted_scalar = scalar.from_montgomery_form(); From b4b447fd46e35a1b76c50cf9517daf306dbcb4b1 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Tue, 12 Mar 2024 15:33:06 +0000 Subject: [PATCH 15/26] appease gcc --- .../ipa/mock_transcript.hpp | 54 +++++++------------ 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp index 064212ec5f1..29d1ce098a8 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -57,18 +57,15 @@ class MockTranscript { * @brief Send something that can be converted to uint256_t to the verifier (used for field elements) * */ - template void send_to_verifier(const std::string&, const T& field_element) + template void send_to_verifier(const std::string&, const T& element) { - field_elements.push_back(static_cast(field_element)); - } + // GCC breaks explicit specialization, so I have to do this + if constexpr (std::is_same_v) { - /** - * @brief Send a group element to the verifier - * - */ - template <> void send_to_verifier(const std::string&, const bb::curve::Grumpkin::AffineElement& group_element) - { - group_elements.push_back(group_element); + group_elements.push_back(element); + } else { + field_elements.push_back(static_cast(element)); + } } /** @@ -84,35 +81,20 @@ class MockTranscript { return result; } /** - * @brief Receive something unknown from prover (fails) - * - */ - template T receive_from_prover(const std::string&) { abort(); } - - /** - * @brief Receive a field element from prover - */ - template <> bb::curve::Grumpkin::ScalarField receive_from_prover(const std::string&) - { - ASSERT(field_elements.size() > current_field_index); - return field_elements[current_field_index++]; - } - /** - * @brief Receive a field element from prover - */ - template <> bb::curve::Grumpkin::BaseField receive_from_prover(const std::string&) - { - ASSERT(field_elements.size() > current_field_index); - return field_elements[current_field_index++]; - } - /** - * @brief Receive an affine group element from prover + * @brief Receive elements from the prover * */ - template <> bb::curve::Grumpkin::AffineElement receive_from_prover(const std::string&) + template T receive_from_prover(const std::string&) { - ASSERT(group_elements.size() > current_group_index); - return group_elements[current_group_index++]; + if constexpr (std::is_same_v || + std::is_same_v) { + ASSERT(field_elements.size() > current_field_index); + return field_elements[current_field_index++]; + } + if constexpr (std::is_same_v) { + ASSERT(group_elements.size() > current_group_index); + return group_elements[current_group_index++]; + } } }; } // namespace bb \ No newline at end of file From d07d9beff32cea359e58d8b7da5c30338dca880a Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Tue, 12 Mar 2024 15:35:58 +0000 Subject: [PATCH 16/26] remove stray info --- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index b6ddfb2f46d..51c980f99de 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -270,11 +270,6 @@ template class IPA { throw_or_abort("IPA round challenge is zero"); } const Fr round_challenge_inv = round_challenge.invert(); - info("Inverse challenge: ", round_challenge_inv, "Modulus: ", Fr::modulus); - for (auto& point : std::span{ G_vec_local.begin() + static_cast(round_size), - G_vec_local.begin() + static_cast(round_size * 2) }) { - info(point, " : ", point.is_point_at_infinity()); - } // Step 6.e // G_vec_new = G_vec_lo + G_vec_hi * round_challenge_inv From d9d2d8c0607164c8099bf162cafd149b885805ac Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Tue, 12 Mar 2024 15:58:13 +0000 Subject: [PATCH 17/26] AAAAAAAAAA --- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 9b617fc8275..105c3576b70 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -175,6 +175,9 @@ TEST_F(IPATest, AIsZeroAfterOneRound) // Compute opening proof IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript); + // Reset indices + transcript->reset_for_verifier(); + // Verify EXPECT_TRUE(IPA::verify_internal(this->vk(), opening_claim, transcript)); } From e76692a200d85fbac24a82f6e0a067697460ddd4 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Thu, 14 Mar 2024 18:01:44 +0000 Subject: [PATCH 18/26] Fix msgpack test for affine element --- .../ecc/groups/affine_element.hpp | 153 +++++++++++++++++- .../ecc/groups/affine_element.test.cpp | 41 +++-- 2 files changed, 180 insertions(+), 14 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp index 003478324e5..6dc1d587e93 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp @@ -160,9 +160,158 @@ template class alignas(64) affine_ } Fq x; Fq y; - // for serialization: update with new fields - MSGPACK_FIELDS(x, y); + + // This function is used to serialize an affine element. It matches the old serialization format by first + // converting the field from Montgomery form, which is a special representation used for efficient + // modular arithmetic. Point at infinity is represented by serializing two moduli sequentially + void msgpack_pack(auto& packer) const + { + std::array bin_data; + if (is_point_at_infinity()) { + // In case of point at infinity, modulus value is repeated twice + bin_data = { htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), htonll(Fq::modulus.data[1]), + htonll(Fq::modulus.data[0]), htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), + htonll(Fq::modulus.data[1]), htonll(Fq::modulus.data[0]) }; + } else { + // The fields are first converted from Montgomery form, similar to how the old format did it. + auto adjusted_x = x.from_montgomery_form(); + auto adjusted_y = y.from_montgomery_form(); + + // The data is then converted to big endian format using htonll, which stands for "host to network long + // long". This is necessary because the data will be written to a raw msgpack buffer, which requires big + // endian format. + bin_data = { htonll(adjusted_x.data[3]), htonll(adjusted_x.data[2]), htonll(adjusted_x.data[1]), + htonll(adjusted_x.data[0]), htonll(adjusted_y.data[3]), htonll(adjusted_y.data[2]), + htonll(adjusted_y.data[1]), htonll(adjusted_y.data[0]) }; + } + + // The packer is then used to write the binary data to the buffer, just like in the old format. + packer.pack_bin(sizeof(bin_data)); + packer.pack_bin_body((const char*)bin_data.data(), sizeof(bin_data)); // NOLINT + } + + // This function is used to deserialize a field. It also matches the old deserialization format by + // reading the binary data as big endian uint64_t's, correcting them to the host endianness, and + // then converting the field back to Montgomery form. Point at infinity is encoded by 2 moduli + void msgpack_unpack(auto o) + { + // 2 sequential values of modulus represent the point at inifnity + uint64_t point_at_infinity_representation[8] = { htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), + htonll(Fq::modulus.data[1]), htonll(Fq::modulus.data[0]), + htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), + htonll(Fq::modulus.data[1]), htonll(Fq::modulus.data[0]) }; + // The binary data is first extracted from the msgpack object. + std::array raw_data = o; + + // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t* + // and then using ntohll ("network to host long long") to correct the endianness to the host's endianness. + uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT + if (!memcmp( + (char*)point_at_infinity_representation, (char*)cast_data, sizeof(point_at_infinity_representation))) { + // Set to point at infinity + this->x = Fq(0); + this->y = Fq(0); + this->self_set_infinity(); + } else { + // Convert binary representation to standard one + uint64_t x_data[4] = { + ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) + }; + + uint64_t y_data[4] = { + ntohll(cast_data[7]), ntohll(cast_data[6]), ntohll(cast_data[5]), ntohll(cast_data[4]) + }; + + // Copy into members + for (size_t i = 0; i < 4; i++) { + this->x.data[i] = x_data[i]; + this->y.data[i] = y_data[i]; + } + // Finally, the field is converted back to Montgomery form, just like in the old format. + this->x.self_to_montgomery_form(); + this->y.self_to_montgomery_form(); + } + } + + void msgpack_schema(auto& packer) const + { + packer.pack_alias("affine(" + Fq::Params::schema_name + "," + Fr::Params::schema_name + ")", "bin32"); + } }; + +/** + * @brief Deserialize affine element (non-msgpack deserialization) + * + * @details Parse two uint256_t but in reverse order of 4 limbs, then check if the values is equal to special value for + * infinity. If not, copy to fields and reduce to reconstruct the element + * + * @remark This API expects well-formed data, because we don't reduce the elements enough times to make sure they + * actually adhere to internal field class expectations. So should not be used for something that might be controlled by + * an attacker + * + */ +template +void read(B& it, affine_element& value) +{ + using serialize::read; + uint256_t x{ 0, 0, 0, 0 }; + uint256_t y{ 0, 0, 0, 0 }; + read(it, x.data[3]); + read(it, x.data[2]); + read(it, x.data[1]); + read(it, x.data[0]); + read(it, y.data[3]); + read(it, y.data[2]); + read(it, y.data[1]); + read(it, y.data[0]); + // Check if values are equal to repeated modulus (then we know that the point at inifinity is encoded) + if (x == Fq_::modulus && y == Fq_::modulus) { + value = { Fq_::zero(), Fq_::zero() }; + value.self_set_infinity(); + } else { + for (size_t i = 0; i < 4; i++) { + value.x.data[i] = x.data[i]; + value.y.data[i] = y.data[i]; + } + value.x.self_to_montgomery_form(); + value.y.self_to_montgomery_form(); + } +} + +/** + * @brief Serialize affine element (non-msgpack serialization) + * + * @details If the point is a point at infinity, put repeated encoding of modulus twice. Otherwise, convert form + * montgomery and write the 4 limbs from x (highest to lowest) and 4 limbs from y (highest to lowest). + * + */ +template +void write(B& buf, affine_element const& value) +{ + using serialize::write; + if (value.is_point_at_infinity()) { + write(buf, Fq_::modulus.data[3]); + write(buf, Fq_::modulus.data[2]); + write(buf, Fq_::modulus.data[1]); + write(buf, Fq_::modulus.data[0]); + write(buf, Fq_::modulus.data[3]); + write(buf, Fq_::modulus.data[2]); + write(buf, Fq_::modulus.data[1]); + write(buf, Fq_::modulus.data[0]); + } else { + const field x = value.x.from_montgomery_form(); + const field y = value.x.from_montgomery_form(); + + write(buf, x.data[3]); + write(buf, x.data[2]); + write(buf, x.data[1]); + write(buf, x.data[0]); + write(buf, y.data[3]); + write(buf, y.data[2]); + write(buf, y.data[1]); + write(buf, y.data[0]); + } +} } // namespace bb::group_elements #include "./affine_element_impl.hpp" diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp index 1ea7050d021..0add7b3686c 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp @@ -98,6 +98,30 @@ template class TestAffineElement : public testing::Test { affine_element R(0, P.y); ASSERT_FALSE(P == R); } + // Regression test to ensure that the point at infinity is not equal to its coordinate-wise reduction, which may lie + // on the curve, depending on the y-coordinate. + static void test_infinity_ordering_regression() + { + affine_element P(0, 1); + affine_element Q(0, 1); + + P.self_set_infinity(); + EXPECT_NE(P < Q, Q < P); + } + + /** + * @brief Check that msgpack encoding is consistent with decoding + * + */ + static void test_msgpack_roundtrip() + { + affine_element point_at_infinity{ 1, 1 }; + point_at_infinity.self_set_infinity(); + auto [actual, expected] = msgpack_roundtrip(affine_element{ 1, 1 }); + EXPECT_EQ(actual, expected); + auto [actual_pif, expected_pif] = msgpack_roundtrip(point_at_infinity); + EXPECT_EQ(actual_pif, expected_pif); + } }; using TestTypes = testing::Types; @@ -128,21 +152,14 @@ TYPED_TEST(TestAffineElement, PointCompressionUnsafe) } } -// Regression test to ensure that the point at infinity is not equal to its coordinate-wise reduction, which may lie -// on the curve, depending on the y-coordinate. -TEST(AffineElement, InfinityOrderingRegression) +TYPED_TEST(TestAffineElement, InfinityOrderingRegression) { - secp256k1::g1::affine_element P(0, 1); - secp256k1::g1::affine_element Q(0, 1); - - P.self_set_infinity(); - EXPECT_NE(P < Q, Q < P); + TestFixture::test_infinity_ordering_regression(); } -TEST(AffineElement, Msgpack) +TYPED_TEST(TestAffineElement, Msgpack) { - auto [actual, expected] = msgpack_roundtrip(secp256k1::g1::affine_element{ 1, 1 }); - EXPECT_EQ(actual, expected); + TestFixture::test_msgpack_roundtrip(); } namespace bb::group_elements { @@ -165,7 +182,7 @@ class TestElementPrivate { } // namespace bb::group_elements // Our endomorphism-specialized multiplication should match our generic multiplication -TEST(AffineElement, MulWithEndomorphismMatchesMulWithoutEndomorphism) +TYPED_TEST(TestAffineElement, MulWithEndomorphismMatchesMulWithoutEndomorphism) { for (int i = 0; i < 100; i++) { auto x1 = bb::group_elements::element(grumpkin::g1::affine_element::random_element()); From a738148d7ad2e75df9173a9145cb04aecf66ae77 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Thu, 14 Mar 2024 18:09:59 +0000 Subject: [PATCH 19/26] made my test typed --- .../ecc/groups/affine_element.test.cpp | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp index 0add7b3686c..f59fb269c06 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp @@ -122,6 +122,23 @@ template class TestAffineElement : public testing::Test { auto [actual_pif, expected_pif] = msgpack_roundtrip(point_at_infinity); EXPECT_EQ(actual_pif, expected_pif); } + + /** + * @brief A regression test to make sure the -1 case is covered + * + */ + static void test_batch_by_minus_one() + { + constexpr size_t num_points = 2; + std::vector affine_points(num_points, affine_element::one()); + + std::vector result = + element::batch_mul_with_endomorphism(affine_points, -affine_element::Fr::one()); + + for (size_t i = 0; i < num_points; i++) { + EXPECT_EQ(affine_points[i], -result[i]); + } + } }; using TestTypes = testing::Types; @@ -232,16 +249,7 @@ TEST(AffineElement, InfinityBatchMulByScalarIsInfinity) EXPECT_THAT(result, Each(Property(&grumpkin::g1::affine_element::is_point_at_infinity, Eq(true)))); } -// A regression test to make sure the -1 case is covered -TEST(AffineElement, BatchByMinusOne) +TYPED_TEST(TestAffineElement, BatchByMinusOne) { - constexpr size_t num_points = 2; - std::vector affine_points(num_points, grumpkin::g1::affine_element::one()); - - std::vector result = - grumpkin::g1::element::batch_mul_with_endomorphism(affine_points, -grumpkin::fr::one()); - - for (size_t i = 0; i < num_points; i++) { - EXPECT_EQ(affine_points[i], -result[i]); - } + TestFixture::test_batch_by_minus_one(); } \ No newline at end of file From 201f083e35a50c02d5df4119fac695a514cdef16 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Thu, 14 Mar 2024 19:35:03 +0000 Subject: [PATCH 20/26] Remove this --- .../ecc/groups/affine_element.hpp | 152 +----------------- .../ecc/groups/affine_element.test.cpp | 4 - 2 files changed, 2 insertions(+), 154 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp index 6dc1d587e93..7cba51e67c0 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp @@ -161,157 +161,9 @@ template class alignas(64) affine_ Fq x; Fq y; - // This function is used to serialize an affine element. It matches the old serialization format by first - // converting the field from Montgomery form, which is a special representation used for efficient - // modular arithmetic. Point at infinity is represented by serializing two moduli sequentially - void msgpack_pack(auto& packer) const - { - std::array bin_data; - if (is_point_at_infinity()) { - // In case of point at infinity, modulus value is repeated twice - bin_data = { htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), htonll(Fq::modulus.data[1]), - htonll(Fq::modulus.data[0]), htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), - htonll(Fq::modulus.data[1]), htonll(Fq::modulus.data[0]) }; - } else { - // The fields are first converted from Montgomery form, similar to how the old format did it. - auto adjusted_x = x.from_montgomery_form(); - auto adjusted_y = y.from_montgomery_form(); - - // The data is then converted to big endian format using htonll, which stands for "host to network long - // long". This is necessary because the data will be written to a raw msgpack buffer, which requires big - // endian format. - bin_data = { htonll(adjusted_x.data[3]), htonll(adjusted_x.data[2]), htonll(adjusted_x.data[1]), - htonll(adjusted_x.data[0]), htonll(adjusted_y.data[3]), htonll(adjusted_y.data[2]), - htonll(adjusted_y.data[1]), htonll(adjusted_y.data[0]) }; - } - - // The packer is then used to write the binary data to the buffer, just like in the old format. - packer.pack_bin(sizeof(bin_data)); - packer.pack_bin_body((const char*)bin_data.data(), sizeof(bin_data)); // NOLINT - } - - // This function is used to deserialize a field. It also matches the old deserialization format by - // reading the binary data as big endian uint64_t's, correcting them to the host endianness, and - // then converting the field back to Montgomery form. Point at infinity is encoded by 2 moduli - void msgpack_unpack(auto o) - { - // 2 sequential values of modulus represent the point at inifnity - uint64_t point_at_infinity_representation[8] = { htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), - htonll(Fq::modulus.data[1]), htonll(Fq::modulus.data[0]), - htonll(Fq::modulus.data[3]), htonll(Fq::modulus.data[2]), - htonll(Fq::modulus.data[1]), htonll(Fq::modulus.data[0]) }; - // The binary data is first extracted from the msgpack object. - std::array raw_data = o; - - // The binary data is then read as big endian uint64_t's. This is done by casting the raw data to uint64_t* - // and then using ntohll ("network to host long long") to correct the endianness to the host's endianness. - uint64_t* cast_data = (uint64_t*)&raw_data[0]; // NOLINT - if (!memcmp( - (char*)point_at_infinity_representation, (char*)cast_data, sizeof(point_at_infinity_representation))) { - // Set to point at infinity - this->x = Fq(0); - this->y = Fq(0); - this->self_set_infinity(); - } else { - // Convert binary representation to standard one - uint64_t x_data[4] = { - ntohll(cast_data[3]), ntohll(cast_data[2]), ntohll(cast_data[1]), ntohll(cast_data[0]) - }; - - uint64_t y_data[4] = { - ntohll(cast_data[7]), ntohll(cast_data[6]), ntohll(cast_data[5]), ntohll(cast_data[4]) - }; - - // Copy into members - for (size_t i = 0; i < 4; i++) { - this->x.data[i] = x_data[i]; - this->y.data[i] = y_data[i]; - } - // Finally, the field is converted back to Montgomery form, just like in the old format. - this->x.self_to_montgomery_form(); - this->y.self_to_montgomery_form(); - } - } - - void msgpack_schema(auto& packer) const - { - packer.pack_alias("affine(" + Fq::Params::schema_name + "," + Fr::Params::schema_name + ")", "bin32"); - } + // for serialization: update with new fields + MSGPACK_FIELDS(x, y); }; - -/** - * @brief Deserialize affine element (non-msgpack deserialization) - * - * @details Parse two uint256_t but in reverse order of 4 limbs, then check if the values is equal to special value for - * infinity. If not, copy to fields and reduce to reconstruct the element - * - * @remark This API expects well-formed data, because we don't reduce the elements enough times to make sure they - * actually adhere to internal field class expectations. So should not be used for something that might be controlled by - * an attacker - * - */ -template -void read(B& it, affine_element& value) -{ - using serialize::read; - uint256_t x{ 0, 0, 0, 0 }; - uint256_t y{ 0, 0, 0, 0 }; - read(it, x.data[3]); - read(it, x.data[2]); - read(it, x.data[1]); - read(it, x.data[0]); - read(it, y.data[3]); - read(it, y.data[2]); - read(it, y.data[1]); - read(it, y.data[0]); - // Check if values are equal to repeated modulus (then we know that the point at inifinity is encoded) - if (x == Fq_::modulus && y == Fq_::modulus) { - value = { Fq_::zero(), Fq_::zero() }; - value.self_set_infinity(); - } else { - for (size_t i = 0; i < 4; i++) { - value.x.data[i] = x.data[i]; - value.y.data[i] = y.data[i]; - } - value.x.self_to_montgomery_form(); - value.y.self_to_montgomery_form(); - } -} - -/** - * @brief Serialize affine element (non-msgpack serialization) - * - * @details If the point is a point at infinity, put repeated encoding of modulus twice. Otherwise, convert form - * montgomery and write the 4 limbs from x (highest to lowest) and 4 limbs from y (highest to lowest). - * - */ -template -void write(B& buf, affine_element const& value) -{ - using serialize::write; - if (value.is_point_at_infinity()) { - write(buf, Fq_::modulus.data[3]); - write(buf, Fq_::modulus.data[2]); - write(buf, Fq_::modulus.data[1]); - write(buf, Fq_::modulus.data[0]); - write(buf, Fq_::modulus.data[3]); - write(buf, Fq_::modulus.data[2]); - write(buf, Fq_::modulus.data[1]); - write(buf, Fq_::modulus.data[0]); - } else { - const field x = value.x.from_montgomery_form(); - const field y = value.x.from_montgomery_form(); - - write(buf, x.data[3]); - write(buf, x.data[2]); - write(buf, x.data[1]); - write(buf, x.data[0]); - write(buf, y.data[3]); - write(buf, y.data[2]); - write(buf, y.data[1]); - write(buf, y.data[0]); - } -} } // namespace bb::group_elements #include "./affine_element_impl.hpp" diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp index f59fb269c06..98089fe91c7 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp @@ -115,12 +115,8 @@ template class TestAffineElement : public testing::Test { */ static void test_msgpack_roundtrip() { - affine_element point_at_infinity{ 1, 1 }; - point_at_infinity.self_set_infinity(); auto [actual, expected] = msgpack_roundtrip(affine_element{ 1, 1 }); EXPECT_EQ(actual, expected); - auto [actual_pif, expected_pif] = msgpack_roundtrip(point_at_infinity); - EXPECT_EQ(actual_pif, expected_pif); } /** From ea28dc2e5d42f39219e7709beb46c41345cf645b Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 15 Mar 2024 14:09:19 +0000 Subject: [PATCH 21/26] issues --- barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp | 1 + .../cpp/src/barretenberg/ecc/groups/affine_element.test.cpp | 2 ++ barretenberg/cpp/src/barretenberg/ecc/groups/element.hpp | 1 + 3 files changed, 4 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp index 7cba51e67c0..e50677834e0 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp @@ -162,6 +162,7 @@ template class alignas(64) affine_ Fq y; // for serialization: update with new fields + // TODO(https://github.com/AztecProtocol/barretenberg/issues/908) point at inifinty isn't handled MSGPACK_FIELDS(x, y); }; } // namespace bb::group_elements diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp index 98089fe91c7..fcbce1f0818 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp @@ -115,6 +115,7 @@ template class TestAffineElement : public testing::Test { */ static void test_msgpack_roundtrip() { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/908) point at inifinty isn't handled auto [actual, expected] = msgpack_roundtrip(affine_element{ 1, 1 }); EXPECT_EQ(actual, expected); } @@ -206,6 +207,7 @@ TYPED_TEST(TestAffineElement, MulWithEndomorphismMatchesMulWithoutEndomorphism) } } +// TODO(https://github.com/AztecProtocol/barretenberg/issues/909): These tests are not typed for no reason // Multiplication of a point at infinity by a scalar should be a point at infinity TEST(AffineElement, InfinityMulByScalarIsInfinity) { diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/element.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/element.hpp index f73f7ab5fe0..cb7946beb0f 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/element.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/element.hpp @@ -128,6 +128,7 @@ template class alignas(32) element { // return { x, y, Fq::one() }; // } // for serialization: update with new fields + // TODO(https://github.com/AztecProtocol/barretenberg/issues/908) point at inifinty isn't handled MSGPACK_FIELDS(x, y, z); static void conditional_negate_affine(const affine_element& in, From c9949bcd60963f9234082055658ffdb6e073e19b Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 15 Mar 2024 14:21:08 +0000 Subject: [PATCH 22/26] more comments --- .../cpp/src/barretenberg/ecc/groups/element_impl.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp index 0d1345267b6..5cc22a57dba 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/element_impl.hpp @@ -902,8 +902,12 @@ std::vector> element::batch_mul_with_endomo /*finite_field_multiplications_per_iteration=*/6); }; - // Since the core algorithm has no edgecase detection, it can't handle -1. (Because constructing -1 is r + -1 as - // skew and r⋅P = Point at infinity, which we can't handle in batch affine add). So we have to handle it separately + // We compute the resulting point through WNAF by evaluating (the (\sum_i (16ⁱ⋅ + // (a_i ∈ {-15,-13,-11,-9,-7,-5,-3,-1,1,3,5,7,9,11,13,15}))) - skew), where skew is 0 or 1. The result of the sum is + // always odd and skew is used to reconstruct an even scalar. This means that to construct scalar p-1, where p is + // the order of the scalar field, we first compute p through the sums and then subtract -1. Howver, since we are + // computing p⋅Point, we get a point at infinity, which is an edgecase, and we don't want to handle edgecases in the + // hot loop since the slow the computation down. So it's better to just handle it here. if (scalar == -Fr::one()) { std::vector results(num_points); From 12813c721b937264a9161e354f0a24a3e481157c Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 15 Mar 2024 14:37:28 +0000 Subject: [PATCH 23/26] fixes --- .../barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp | 2 +- .../barretenberg/commitment_schemes/ipa/ipa.test.cpp | 8 ++++---- .../commitment_schemes/ipa/mock_transcript.hpp | 10 +++++----- .../barretenberg/ecc/groups/affine_element.test.cpp | 10 +++++++--- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp index 506a632230a..9200290b984 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp @@ -148,7 +148,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) ProxyCaller::compute_opening_proof_internal(ck, opening_pair, poly, transcript); // Reset challenge indices - transcript->reset_for_verifier(); + transcript->reset_indices(); // Should verify if (!ProxyCaller::verify_internal(vk, opening_claim, transcript)) { diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 105c3576b70..adb7146c573 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -124,7 +124,7 @@ TEST_F(IPATest, ChallengesAreZero) for (size_t i = 0; i < num_challenges; i++) { auto new_random_vector = random_vector; new_random_vector[i] = Fr::zero(); - transcript->reset(new_random_vector); + transcript->initialize(new_random_vector); EXPECT_ANY_THROW(IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript)); } // Fill out a vector of affine elements that the verifier receives from the prover with generators (we don't care @@ -138,7 +138,7 @@ TEST_F(IPATest, ChallengesAreZero) for (size_t i = 0; i < num_challenges; i++) { auto new_random_vector = random_vector; new_random_vector[i] = Fr::zero(); - transcript->reset(new_random_vector, lrs, { uint256_t(n) }); + transcript->initialize(new_random_vector, lrs, { uint256_t(n) }); EXPECT_ANY_THROW(IPA::verify_internal(this->vk(), opening_claim, transcript)); } } @@ -170,13 +170,13 @@ TEST_F(IPATest, AIsZeroAfterOneRound) random_vector[1] = -Fr::one(); // Put the challenges in the transcript - transcript->reset(random_vector); + transcript->initialize(random_vector); // Compute opening proof IPA::compute_opening_proof_internal(this->ck(), opening_pair, poly, transcript); // Reset indices - transcript->reset_for_verifier(); + transcript->reset_indices(); // Verify EXPECT_TRUE(IPA::verify_internal(this->vk(), opening_claim, transcript)); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp index 29d1ce098a8..91f3b2d03b3 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/mock_transcript.hpp @@ -24,15 +24,15 @@ class MockTranscript { size_t current_group_index = 0; /** - * @brief Reset the transcript (requires to submit the challenges) + * @brief Initialize the transcript (requires to submit the challenges) * * @param challenges_ Challenges that will be sent to the prover/verifier * @param group_elements_ Group elements sent to the verifier * @param field_elements_ Field elements sent to the verifier */ - void reset(std::vector challenges_, - std::vector group_elements_ = {}, - std::vector field_elements_ = {}) + void initialize(std::vector challenges_, + std::vector group_elements_ = {}, + std::vector field_elements_ = {}) { challenges = std::move(challenges_); current_challenge_index = 0; @@ -47,7 +47,7 @@ class MockTranscript { * @details After the transcipt received elements from the prover, this method allows to reset counters so that the * verifier can receive those elements */ - void reset_for_verifier() + void reset_indices() { current_challenge_index = 0; current_field_index = 0; diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp index fcbce1f0818..4c36b16c9d4 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.test.cpp @@ -124,7 +124,7 @@ template class TestAffineElement : public testing::Test { * @brief A regression test to make sure the -1 case is covered * */ - static void test_batch_by_minus_one() + static void test_batch_endomorphism_by_minus_one() { constexpr size_t num_points = 2; std::vector affine_points(num_points, affine_element::one()); @@ -247,7 +247,11 @@ TEST(AffineElement, InfinityBatchMulByScalarIsInfinity) EXPECT_THAT(result, Each(Property(&grumpkin::g1::affine_element::is_point_at_infinity, Eq(true)))); } -TYPED_TEST(TestAffineElement, BatchByMinusOne) +TYPED_TEST(TestAffineElement, BatchEndomoprhismByMinusOne) { - TestFixture::test_batch_by_minus_one(); + if constexpr (TypeParam::USE_ENDOMORPHISM) { + TestFixture::test_batch_endomorphism_by_minus_one(); + } else { + GTEST_SKIP(); + } } \ No newline at end of file From dcf306df5708c560ec84407af045dfd0f3791533 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 15 Mar 2024 14:43:45 +0000 Subject: [PATCH 24/26] maromments --- .../src/barretenberg/commitment_schemes/ipa/ipa.test.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index adb7146c573..87f14d5630c 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -48,6 +48,8 @@ TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) EXPECT_EQ(expected.normalize(), commitment.normalize()); } +// This test checks that we can correctly open a zero polynomial. Since we often have point at infinity troubles, it +// detects those. TEST_F(IPATest, OpenZeroPolynomial) { using IPA = IPA; @@ -73,6 +75,8 @@ TEST_F(IPATest, OpenZeroPolynomial) EXPECT_TRUE(result); } +// This test makes sure that even if the whole vector \vec{b} generated from the x, at which we open the polynomial, is +// zero, IPA behaves TEST_F(IPATest, OpenAtZero) { using IPA = IPA; @@ -98,6 +102,7 @@ TEST_F(IPATest, OpenAtZero) namespace bb { #if !defined(__wasm__) +// This test ensures that IPA throws or aborts when a challenge is zero, since it breaks the logic of the argument TEST_F(IPATest, ChallengesAreZero) { using IPA = IPA; @@ -142,6 +147,8 @@ TEST_F(IPATest, ChallengesAreZero) EXPECT_ANY_THROW(IPA::verify_internal(this->vk(), opening_claim, transcript)); } } + +// This test checks that if the vector \vec{a_new} becomes zero after one round, it doesn't break IPA. TEST_F(IPATest, AIsZeroAfterOneRound) { using IPA = IPA; From 76de62fabbc267881a49c397e27cfcf347cc16fd Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 15 Mar 2024 14:52:14 +0000 Subject: [PATCH 25/26] fix --- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp index 9200290b984..f6d98bbd7fa 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.fuzzer.cpp @@ -116,7 +116,7 @@ extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data, size_t size) } // Put challenges into the transcript - transcript->reset(challenges); + transcript->initialize(challenges); // Parse polynomial std::vector polynomial_coefficients(polynomial_size); From e296885050ae4e4d3279c9c2a1f692ae014fd536 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Fri, 15 Mar 2024 15:10:11 +0000 Subject: [PATCH 26/26] extend docs --- .../cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 51c980f99de..dfe6f6d94f0 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -105,7 +105,7 @@ template class IPA { *as follows: * *1. Send the degree of \f$f(x)\f$ plus one, equal to \f$d\f$ to the verifier - *2. Receive the generator challenge \f$u\f$ from the verifier + *2. Receive the generator challenge \f$u\f$ from the verifier. If it is zero, abort *3. Compute the auxiliary generator \f$U=u\cdot G\f$, where \f$G\f$ is a generator of \f$E(\mathbb{F}_p)\f$​ *4. Set \f$\vec{G}_{k}=\vec{G}\f$, \f$\vec{a}_{k}=\vec{p}\f$ *5. Compute the vector \f$\vec{b}_{k}=(1,\beta,\beta^2,...,\beta^{d-1})\f$ @@ -117,7 +117,7 @@ template class IPA { *\f$R_{i-1}=\langle\vec{a}_{i\_high},\vec{G}_{i\_low}\rangle+\langle\vec{a}_{i\_high},\vec{b}_{i\_low}\rangle\cdot U\f$ * 3. Send \f$L_{i-1}\f$ and \f$R_{i-1}\f$ to the verifier - * 4. Receive round challenge \f$u_{i-1}\f$ from the verifier​ + * 4. Receive round challenge \f$u_{i-1}\f$ from the verifier​, if it is zero, abort * 5. Compute \f$\vec{G}_{i-1}=\vec{G}_{i\_low}+u_{i-1}^{-1}\cdot \vec{G}_{i\_high}\f$ * 6. Compute \f$\vec{a}_{i-1}=\vec{a}_{i\_low}+u_{i-1}\cdot \vec{a}_{i\_high}\f$ * 7. Compute \f$\vec{b}_{i-1}=\vec{b}_{i\_low}+u_{i-1}^{-1}\cdot \vec{b}_{i\_high}\f$​ @@ -317,9 +317,10 @@ template class IPA { * @details The procedure runs as follows: * *1. Receive \f$d\f$ (polynomial degree plus one) from the prover - *2. Receive the generator challenge \f$u\f$ and computes \f$U=u\cdot G\f$ + *2. Receive the generator challenge \f$u\f$, abort if it's zero, otherwise compute \f$U=u\cdot G\f$ *3. Compute \f$C'=C+f(\beta)\cdot U\f$ - *4. Receive \f$L_j, R_j\f$ and compute challenges \f$u_j\f$ for \f$j \in {k-1,..,0}\f$ + *4. Receive \f$L_j, R_j\f$ and compute challenges \f$u_j\f$ for \f$j \in {k-1,..,0}\f$, abort immediately on + receiving a \f$u_j=0\f$ *5. Compute \f$C_0 = C' + \sum_{j=0}^{k-1}(u_j^{-1}L_j + u_jR_j)\f$ *6. Compute \f$b_0=g(\beta)=\prod_{i=0}^{k-1}(1+u_{i}^{-1}x^{2^{i}})\f$ *7. Compute vector \f$\vec{s}=(1,u_{0}^{-1},u_{1}^{-1},u_{0}^{-1}u_{1}^{-1},...,\prod_{i=0}^{k-1}u_{i}^{-1})\f$