From cad38a1728c0a357e69092aa497b132c1d4bdfcb Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Fri, 15 Dec 2023 17:42:20 +0900 Subject: [PATCH 01/11] pref(zk): cache some of properties in `ConstraintSystem` --- tachyon/zk/plonk/constraint_system.h | 87 +++++++++++++++------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/tachyon/zk/plonk/constraint_system.h b/tachyon/zk/plonk/constraint_system.h index 0d5686bd8..bc1cad9e0 100644 --- a/tachyon/zk/plonk/constraint_system.h +++ b/tachyon/zk/plonk/constraint_system.h @@ -427,19 +427,22 @@ class ConstraintSystem { // Compute the degree of the constraint system (the maximum degree of all // constraints). size_t ComputeDegree() const { - // The permutation argument will serve alongside the gates, so must be - // accounted for. - size_t degree = permutation_.RequiredDegree(); + if (!cached_degree_.has_value()) { + // The permutation argument will serve alongside the gates, so must be + // accounted for. + size_t degree = permutation_.RequiredDegree(); - // The lookup argument also serves alongside the gates and must be accounted - // for. - degree = std::max(degree, ComputeLookupRequiredDegree()); + // The lookup argument also serves alongside the gates and must be + // accounted for. + degree = std::max(degree, ComputeLookupRequiredDegree()); - // Account for each gate to ensure our quotient polynomial is the - // correct degree and that our extended domain is the right size. - degree = std::max(degree, ComputeGateRequiredDegree()); + // Account for each gate to ensure our quotient polynomial is the + // correct degree and that our extended domain is the right size. + degree = std::max(degree, ComputeGateRequiredDegree()); - return std::max(degree, minimum_degree_.value_or(1)); + cached_degree_ = std::max(degree, minimum_degree_.value_or(1)); + } + return *cached_degree_; } size_t ComputeExtendedDegree(size_t k) const { @@ -452,35 +455,38 @@ class ConstraintSystem { // Compute the number of blinding factors necessary to perfectly blind // each of the prover's witness polynomials. size_t ComputeBlindingFactors() const { - // All of the prover's advice columns are evaluated at no more than - auto max_num_advice_query_it = std::max_element(num_advice_queries_.begin(), - num_advice_queries_.end()); - size_t factors = max_num_advice_query_it == num_advice_queries_.end() - ? 1 - : *max_num_advice_query_it; - // distinct points during gate checks. - - // - The permutation argument witness polynomials are evaluated at most 3 - // times. - // - Each lookup argument has independent witness polynomials, and they are - // evaluated at most 2 times. - factors = std::max(size_t{3}, factors); - - // Each polynomial is evaluated at most an additional time during - // multiopen (at x₃ to produce q_evals): - ++factors; - - // h(x) is derived by the other evaluations so it does not reveal - // anything; in fact it does not even appear in the proof. - - // h(x₃) is also not revealed; the verifier only learns a single - // evaluation of a polynomial in x₁ which has h(x₃) and another random - // polynomial evaluated at x₃ as coefficients -- this random polynomial - // is "random_poly" in the vanishing argument. - - // Add an additional blinding factor as a slight defense against - // off-by-one errors. - return ++factors; + if (!cached_blinding_factors_.has_value()) { + // All of the prover's advice columns are evaluated at no more than + auto max_num_advice_query_it = std::max_element( + num_advice_queries_.begin(), num_advice_queries_.end()); + size_t factors = max_num_advice_query_it == num_advice_queries_.end() + ? 1 + : *max_num_advice_query_it; + // distinct points during gate checks. + + // - The permutation argument witness polynomials are evaluated at most 3 + // times. + // - Each lookup argument has independent witness polynomials, and they + // are evaluated at most 2 times. + factors = std::max(size_t{3}, factors); + + // Each polynomial is evaluated at most an additional time during + // multiopen (at x₃ to produce q_evals): + ++factors; + + // h(x) is derived by the other evaluations so it does not reveal + // anything; in fact it does not even appear in the proof. + + // h(x₃) is also not revealed; the verifier only learns a single + // evaluation of a polynomial in x₁ which has h(x₃) and another random + // polynomial evaluated at x₃ as coefficients -- this random polynomial + // is "random_poly" in the vanishing argument. + + // Add an additional blinding factor as a slight defense against + // off-by-one errors. + cached_blinding_factors_ = ++factors; + } + return *cached_blinding_factors_; } // Returns the minimum necessary rows that need to exist in order to @@ -578,6 +584,9 @@ class ConstraintSystem { std::vector constants_; std::optional minimum_degree_; + + mutable std::optional cached_degree_; + mutable std::optional cached_blinding_factors_; }; } // namespace tachyon::zk From 414c9b8ab1f66f7636fbbf0f0f0a23011d3195af Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Fri, 15 Dec 2023 22:22:46 +0900 Subject: [PATCH 02/11] feat(math): add `EvaluatePartialLagrangeCoefficients()` --- .../math/polynomials/univariate/BUILD.bazel | 1 + .../univariate/univariate_evaluation_domain.h | 47 ++++++++++++++----- .../univariate_evaluation_domain_unittest.cc | 23 +++++++++ 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/tachyon/math/polynomials/univariate/BUILD.bazel b/tachyon/math/polynomials/univariate/BUILD.bazel index edc0ba03f..123905250 100644 --- a/tachyon/math/polynomials/univariate/BUILD.bazel +++ b/tachyon/math/polynomials/univariate/BUILD.bazel @@ -47,6 +47,7 @@ tachyon_cc_library( ":univariate_polynomial", "//tachyon/base:bits", "//tachyon/base:openmp_util", + "//tachyon/base:range", "//tachyon/math/polynomials:evaluation_domain", ], ) diff --git a/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h b/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h index 05d1d1f8a..8e6880021 100644 --- a/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h +++ b/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h @@ -14,6 +14,7 @@ #include "tachyon/base/bits.h" #include "tachyon/base/logging.h" #include "tachyon/base/openmp_util.h" +#include "tachyon/base/range.h" #include "tachyon/math/polynomials/evaluation_domain.h" #include "tachyon/math/polynomials/univariate/univariate_evaluation_domain_forwards.h" #include "tachyon/math/polynomials/univariate/univariate_evaluations.h" @@ -159,6 +160,19 @@ class UnivariateEvaluationDomain : public EvaluationDomain { // polynomial P over H, where d < m, P(𝜏) can be computed as P(𝜏) = // Σ{i in m} Lᵢ_H(𝜏) * P(gⁱ). constexpr std::vector EvaluateAllLagrangeCoefficients(const F& tau) const { + return EvaluatePartialLagrangeCoefficients( + tau, base::Range::Until(size_)); + } + + // Almost same with above, but it only computes parts of the lagrange + // coefficients defined by |range|. + template + constexpr std::vector EvaluatePartialLagrangeCoefficients( + const F& tau, base::Range range) const { + size_t size = range.GetSize(); + CHECK_LE(size, size_); + if (size == 0) return {}; + // Evaluate all Lagrange polynomials at 𝜏 to get the lagrange // coefficients. // @@ -174,8 +188,8 @@ class UnivariateEvaluationDomain : public EvaluationDomain { // Then i-th lagrange coefficient in this case is then simply 1, // and all other lagrange coefficients are 0. // Thus we find i by brute force. - std::vector u(size_, F::Zero()); - F omega_i = offset_; + std::vector u(size, F::Zero()); + F omega_i = GetElement(range.from); for (F& u_i : u) { if (omega_i == tau) { u_i = F::One(); @@ -197,21 +211,25 @@ class UnivariateEvaluationDomain : public EvaluationDomain { // (See // https://github.com/arkworks-rs/algebra/blob/4152c41769ae0178fc110bfd15cc699673a2ce4b/poly/src/domain/mod.rs#L198) - // v₀⁻¹ = m * hᵐ⁻¹ - F v_0_inv = size_as_field_element_ * offset_pow_size_ * offset_inv_; - // lᵢ = Z_H(𝜏)⁻¹ * v₀⁻¹ = (Z_H(𝜏) * vᵢ)⁻¹ - F l_i = z_h_at_tau.Inverse() * v_0_inv; - F negative_cur_elem = -offset_; + // t = m * hᵐ = v₀⁻¹ * h + F t = size_as_field_element_ * offset_pow_size_; + F omega_i = GetElement(range.from); + // lᵢ = (Z_H(𝜏) * h * gᵢ)⁻¹ * t + // = (Z_H(𝜏) * h * gᵢ * t⁻¹)⁻¹ + // = (Z_H(𝜏) * h * gᵢ * v₀⁻¹ * h⁻¹)⁻¹ + // = (Z_H(𝜏) * gᵢ * v₀)⁻¹ + F l_i = (z_h_at_tau * omega_i).Inverse() * t; + F negative_omega_i = -omega_i; std::vector lagrange_coefficients_inverse = - base::CreateVector(size_, [this, &l_i, &tau, &negative_cur_elem]() { + base::CreateVector(size, [this, &l_i, &tau, &negative_omega_i]() { // 𝜏 - h * gⁱ - F r_i = tau + negative_cur_elem; + F r_i = tau + negative_omega_i; // (Z_H(𝜏) * vᵢ)⁻¹ * (𝜏 - h * gⁱ) F ret = l_i * r_i; // lᵢ₊₁ = g⁻¹ * lᵢ l_i *= group_gen_inv_; // -h * gⁱ⁺¹ - negative_cur_elem *= group_gen_; + negative_omega_i *= group_gen_; return ret; }); @@ -275,8 +293,13 @@ class UnivariateEvaluationDomain : public EvaluationDomain { } // Returns the |i|-th element of the domain. - constexpr F GetElement(size_t i) const { - F result = group_gen_.Pow(i); + constexpr F GetElement(int64_t i) const { + F result; + if (i > 0) { + result = group_gen_.Pow(i); + } else { + result = group_gen_inv_.Pow(-i); + } if (!offset_.IsOne()) { result *= offset_; } diff --git a/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc b/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc index 4345b5cd1..7448cb7b7 100644 --- a/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc +++ b/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc @@ -196,6 +196,17 @@ TYPED_TEST(UnivariateEvaluationDomainTest, NonSystematicLagrangeCoefficients) { std::vector lagrange_coeffs = d.EvaluateAllLagrangeCoefficients(rand_pt); + std::vector sub_lagrange_coeffs = + d.EvaluatePartialLagrangeCoefficients( + rand_pt, base::Range(1, domain_size - 1)); + + if (domain_size > 2) { + sub_lagrange_coeffs.push_back(lagrange_coeffs.back()); + sub_lagrange_coeffs.insert(sub_lagrange_coeffs.begin(), + lagrange_coeffs.front()); + EXPECT_EQ(lagrange_coeffs, sub_lagrange_coeffs); + } + Evals poly_evals = d.FFT(rand_poly); // Do lagrange interpolation, and compare against the actual @@ -227,6 +238,18 @@ TYPED_TEST(UnivariateEvaluationDomainTest, SystematicLagrangeCoefficients) { F x = d.GetElement(i); std::vector lagrange_coeffs = d.EvaluateAllLagrangeCoefficients(x); + + std::vector sub_lagrange_coeffs = + d.EvaluatePartialLagrangeCoefficients( + x, base::Range(1, domain_size - 1)); + + if (domain_size > 2) { + sub_lagrange_coeffs.push_back(lagrange_coeffs.back()); + sub_lagrange_coeffs.insert(sub_lagrange_coeffs.begin(), + lagrange_coeffs.front()); + EXPECT_EQ(lagrange_coeffs, sub_lagrange_coeffs); + } + for (size_t j = 0; j < domain_size; ++j) { // Lagrange coefficient for the evaluation point, // which should be 1 if i == j From 4c7e8a98bbe20a1e1abf809b14583ec09c79f376 Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Fri, 15 Dec 2023 22:23:48 +0900 Subject: [PATCH 03/11] test(math): use same constant with SystematicLagrangeCoefficients test `kNumCoeffs` is not a number for domain dimension! --- .../univariate/univariate_evaluation_domain_unittest.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc b/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc index 7448cb7b7..54d03a0a0 100644 --- a/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc +++ b/tachyon/math/polynomials/univariate/univariate_evaluation_domain_unittest.cc @@ -185,7 +185,7 @@ TYPED_TEST(UnivariateEvaluationDomainTest, NonSystematicLagrangeCoefficients) { using DensePoly = typename UnivariateEvaluationDomainType::DensePoly; using Evals = typename UnivariateEvaluationDomainType::Evals; - for (size_t domain_dim = 1; domain_dim < kNumCoeffs; ++domain_dim) { + for (size_t domain_dim = 1; domain_dim < 5; ++domain_dim) { size_t domain_size = size_t{1} << domain_dim; F rand_pt = F::Random(); DensePoly rand_poly = DensePoly::Random(domain_size - 1); From f1ee26544e8373b3137ff25f34569b134efdf784 Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Fri, 15 Dec 2023 22:37:18 +0900 Subject: [PATCH 04/11] perf(math): optimize `GetElements()` of `UnivariateEvaluationDomain` --- .../univariate/univariate_evaluation_domain.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h b/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h index 8e6880021..f37ff7fde 100644 --- a/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h +++ b/tachyon/math/polynomials/univariate/univariate_evaluation_domain.h @@ -309,11 +309,12 @@ class UnivariateEvaluationDomain : public EvaluationDomain { // Returns all the elements of the domain. constexpr std::vector GetElements() const { if (offset_.IsOne()) { - return base::CreateVector(size_, - [this](size_t i) { return group_gen_.Pow(i); }); + return F::GetSuccessivePowers(size_, group_gen_); } else { - return base::CreateVector( - size_, [this](size_t i) { return group_gen_.Pow(i) * offset_; }); + F value = offset_; + return base::CreateVector(size_, [this, &value]() { + return std::exchange(value, value * group_gen_); + }); } } From 1b24be368ef174aa020da135c58fe094bc183089 Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 13:30:38 +0900 Subject: [PATCH 05/11] perf(math): optimize `Ring::SumOfProducts()` Since we generally don't support APIs for iterator and our parallelization api doens't suppport for iterator, I removed this APIs. Plus, our code doesn't depend on it, either. But if I use it everywhere, unittests are failed because of timeout! --- tachyon/math/base/BUILD.bazel | 2 +- tachyon/math/base/field.h | 18 +++--- tachyon/math/base/field_unittest.cc | 20 +++++- tachyon/math/base/rings.h | 62 +++++++++++++------ .../short_weierstrass/affine_point_impl.h | 2 +- .../short_weierstrass/jacobian_point_impl.h | 4 +- .../short_weierstrass/point_xyzz_impl.h | 6 +- .../short_weierstrass/projective_point_impl.h | 4 +- .../finite_fields/prime_field_unittest.cc | 4 +- .../finite_fields/quadratic_extension_field.h | 4 +- 10 files changed, 82 insertions(+), 44 deletions(-) diff --git a/tachyon/math/base/BUILD.bazel b/tachyon/math/base/BUILD.bazel index 44280176f..90d409c20 100644 --- a/tachyon/math/base/BUILD.bazel +++ b/tachyon/math/base/BUILD.bazel @@ -89,7 +89,7 @@ tachyon_cc_library( hdrs = ["rings.h"], deps = [ ":groups", - "//tachyon/base:template_util", + "//tachyon/base:parallelize", ], ) diff --git a/tachyon/math/base/field.h b/tachyon/math/base/field.h index eb14e58d5..00fb50ed5 100644 --- a/tachyon/math/base/field.h +++ b/tachyon/math/base/field.h @@ -19,21 +19,17 @@ template class Field : public AdditiveGroup, public MultiplicativeGroup { public: // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ - template < - typename InputIterator, - std::enable_if_t>>* = - nullptr> - constexpr static F SumOfProducts(InputIterator a_first, InputIterator a_last, - InputIterator b_first, - InputIterator b_last) { - return Ring::SumOfProducts(std::move(a_first), std::move(a_last), - std::move(b_first), std::move(b_last)); - } - template constexpr static F SumOfProducts(const Container& a, const Container& b) { return Ring::SumOfProducts(a, b); } + + // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ + template + constexpr static F SumOfProductsSerial(const Container& a, + const Container& b) { + return Ring::SumOfProductsSerial(a, b); + } }; } // namespace tachyon::math diff --git a/tachyon/math/base/field_unittest.cc b/tachyon/math/base/field_unittest.cc index 577ff0b8d..937bcdc9c 100644 --- a/tachyon/math/base/field_unittest.cc +++ b/tachyon/math/base/field_unittest.cc @@ -13,12 +13,28 @@ class FieldTest : public testing::Test { } // namespace -TEST(FieldTest, SumOfProducts) { +TEST(FieldTest, SumOfProductsSerial) { std::vector a = {GF7(2), GF7(3), GF7(4)}; std::vector b = {GF7(1), GF7(2), GF7(3)}; - GF7 result = Field::SumOfProducts(a, b); + GF7 result = Field::SumOfProductsSerial(a, b); EXPECT_EQ(result, GF7(6)); } +TEST(FieldTest, SumOfProductsParallel) { +#if defined(TACHYON_HAS_OPENMP) + size_t thread_nums = static_cast(omp_get_max_threads()); +#else + size_t thread_nums = 1; +#endif + + std::vector a = + base::CreateVector(thread_nums * 10, []() { return GF7::Random(); }); + std::vector b = + base::CreateVector(thread_nums * 10, []() { return GF7::Random(); }); + + EXPECT_EQ(Field::SumOfProducts(a, b), + Field::SumOfProductsSerial(a, b)); +} + } // namespace tachyon::math diff --git a/tachyon/math/base/rings.h b/tachyon/math/base/rings.h index f45187754..e7e5972a5 100644 --- a/tachyon/math/base/rings.h +++ b/tachyon/math/base/rings.h @@ -2,8 +2,9 @@ #define TACHYON_MATH_BASE_RINGS_H_ #include +#include -#include "tachyon/base/template_util.h" +#include "tachyon/base/parallelize.h" #include "tachyon/math/base/groups.h" namespace tachyon::math { @@ -25,26 +26,51 @@ class Ring : public AdditiveGroup, public MultiplicativeSemigroup { // This is taken and modified from // https://github.com/arkworks-rs/algebra/blob/5dfeedf560da6937a5de0a2163b7958bd32cd551/ff/src/fields/mod.rs#L298C1-L305 // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ - template < - typename InputIterator, - std::enable_if_t>>* = - nullptr> - constexpr static F SumOfProducts(InputIterator a_first, InputIterator a_last, - InputIterator b_first, - InputIterator b_last) { - F sum = F::Zero(); - while (a_first != a_last) { - sum += (*a_first * *b_first); - ++a_first; - ++b_first; - } - return sum; + // TODO(chokobole): If I call |SumOfProducts()| instead of + // |SumOfProductsSerial| for all call sites, it gets stuck when doing + // unittests. I think we need a some general threshold to check whether it is + // good to doing parallelization. + template + constexpr static F SumOfProducts(const Container& a, const Container& b) { + size_t size = std::size(a); + CHECK_EQ(size, std::size(b)); + CHECK_NE(size, size_t{0}); + std::vector partial_sum_of_products = base::ParallelizeMap( + a, + [&b](absl::Span chunk, size_t chunk_idx, size_t chunk_size) { + F sum = F::Zero(); + size_t i = chunk_idx * chunk_size; + for (size_t j = 0; j < chunk.size(); ++j) { + sum += (chunk[j] * b[i + j]); + } + return sum; + }); + return std::accumulate(partial_sum_of_products.begin(), + partial_sum_of_products.end(), F::Zero(), + [](F& acc, const F& partial_sum_of_product) { + return acc += partial_sum_of_product; + }); } template - constexpr static F SumOfProducts(const Container& a, const Container& b) { - return SumOfProducts(std::begin(a), std::end(a), std::begin(b), - std::end(b)); + constexpr static F SumOfProductsSerial(const Container& a, + const Container& b) { + size_t size = std::size(a); + CHECK_EQ(size, std::size(b)); + CHECK_NE(size, size_t{0}); + return DoSumOfProductsSerial(a, b); + } + + private: + template + constexpr static F DoSumOfProductsSerial(const Container& a, + const Container& b) { + size_t n = std::size(a); + F sum = F::Zero(); + for (size_t i = 0; i < n; ++i) { + sum += (a[i] * b[i]); + } + return sum; } }; diff --git a/tachyon/math/elliptic_curves/short_weierstrass/affine_point_impl.h b/tachyon/math/elliptic_curves/short_weierstrass/affine_point_impl.h index c8cc194b2..8cfc7d474 100644 --- a/tachyon/math/elliptic_curves/short_weierstrass/affine_point_impl.h +++ b/tachyon/math/elliptic_curves/short_weierstrass/affine_point_impl.h @@ -118,7 +118,7 @@ constexpr PointXYZZ CLASS::DoubleXYZZ() const { // Y3 = M * (S - X3) - W * Y1 BaseField lefts[] = {std::move(m), -w}; BaseField rights[] = {s - x, y_}; - BaseField y = BaseField::SumOfProducts(lefts, rights); + BaseField y = BaseField::SumOfProductsSerial(lefts, rights); // ZZ3 = V BaseField zz = std::move(v); diff --git a/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h b/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h index a7db35107..9e98c833c 100644 --- a/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h +++ b/tachyon/math/elliptic_curves/short_weierstrass/jacobian_point_impl.h @@ -86,7 +86,7 @@ constexpr CLASS& CLASS::AddInPlace(const JacobianPoint& other) { y_.DoubleInPlace(); BaseField lefts[] = {std::move(r), y_}; BaseField rights[] = {std::move(v), std::move(j)}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // Z3 = ((Z1 + Z2)² - Z1Z1 - Z2Z2) * H // This is equal to Z3 = 2 * Z1 * Z2 * H, and computing it this way is @@ -159,7 +159,7 @@ constexpr CLASS& CLASS::AddInPlace(const AffinePoint& other) { // Y3 = r * (V - X3) + 2 * Y1 * J BaseField lefts[] = {std::move(r), y_.Double()}; BaseField rights[] = {v - x_, std::move(j)}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // Z3 = 2 * Z1 * H; // Can alternatively be computed as (Z1 + H)² - Z1Z1 - HH, but the latter is diff --git a/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_impl.h b/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_impl.h index 03986bd02..b730c1478 100644 --- a/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_impl.h +++ b/tachyon/math/elliptic_curves/short_weierstrass/point_xyzz_impl.h @@ -71,7 +71,7 @@ constexpr CLASS& CLASS::AddInPlace(const PointXYZZ& other) { // Y3 = R * (Q - X3) - S1 * PPP BaseField lefts[] = {std::move(r), -s1}; BaseField rights[] = {q - x_, ppp}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // ZZ3 = ZZ1 * ZZ2 * PP zz_ *= other.zz_; @@ -136,7 +136,7 @@ constexpr CLASS& CLASS::AddInPlace(const AffinePoint& other) { // Y3 = R * (Q - X3) - Y1 * PPP BaseField lefts[] = {std::move(r), -y_}; BaseField rights[] = {q - x_, ppp}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // ZZ3 = ZZ1 * PP zz_ *= pp; @@ -186,7 +186,7 @@ constexpr CLASS& CLASS::DoubleInPlace() { // Y3 = M * (S - X3) - W * Y1 BaseField lefts[] = {std::move(m), -w}; BaseField rights[] = {s - x_, y_}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // ZZ3 = V * ZZ1 zz_ *= v; diff --git a/tachyon/math/elliptic_curves/short_weierstrass/projective_point_impl.h b/tachyon/math/elliptic_curves/short_weierstrass/projective_point_impl.h index 10fbb2594..d68b4f98c 100644 --- a/tachyon/math/elliptic_curves/short_weierstrass/projective_point_impl.h +++ b/tachyon/math/elliptic_curves/short_weierstrass/projective_point_impl.h @@ -76,7 +76,7 @@ constexpr CLASS& CLASS::AddInPlace(const ProjectivePoint& other) { // Y3 = u * (R - A) - vvv * Y1Z2 BaseField lefts[] = {std::move(u), -vvv}; BaseField rights[] = {r - a, std::move(y1z2)}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // Z3 = vvv * Z1Z2 z_ = std::move(vvv); @@ -139,7 +139,7 @@ constexpr CLASS& CLASS::AddInPlace(const AffinePoint& other) { // Y3 = u * (R - A) - vvv * Y1 BaseField lefts[] = {std::move(u), -vvv}; BaseField rights[] = {r - a, std::move(y_)}; - y_ = BaseField::SumOfProducts(lefts, rights); + y_ = BaseField::SumOfProductsSerial(lefts, rights); // Z3 = vvv * Z1 z_ *= vvv; diff --git a/tachyon/math/finite_fields/prime_field_unittest.cc b/tachyon/math/finite_fields/prime_field_unittest.cc index 4a8a23c51..fe82d2127 100644 --- a/tachyon/math/finite_fields/prime_field_unittest.cc +++ b/tachyon/math/finite_fields/prime_field_unittest.cc @@ -150,10 +150,10 @@ TEST_F(PrimeFieldTest, MultiplicativeGroupOperators) { EXPECT_EQ(f.Pow(5), GF7(5)); } -TEST_F(PrimeFieldTest, SumOfProducts) { +TEST_F(PrimeFieldTest, SumOfProductsSerial) { const GF7 a[] = {GF7(3), GF7(2)}; const GF7 b[] = {GF7(2), GF7(5)}; - EXPECT_EQ(GF7::SumOfProducts(a, b), GF7(2)); + EXPECT_EQ(GF7::SumOfProductsSerial(a, b), GF7(2)); } TEST_F(PrimeFieldTest, Random) { diff --git a/tachyon/math/finite_fields/quadratic_extension_field.h b/tachyon/math/finite_fields/quadratic_extension_field.h index f6bff80ff..2e2661e7b 100644 --- a/tachyon/math/finite_fields/quadratic_extension_field.h +++ b/tachyon/math/finite_fields/quadratic_extension_field.h @@ -157,13 +157,13 @@ class QuadraticExtensionField { BaseField lefts[] = {c0_, Config::MulByNonResidue(c1_)}; BaseField rights[] = {other.c0_, other.c1_}; - c0 = BaseField::SumOfProducts(lefts, rights); + c0 = BaseField::SumOfProductsSerial(lefts, rights); } BaseField c1; { BaseField lefts[] = {c0_, c1_}; BaseField rights[] = {other.c1_, other.c0_}; - c1 = BaseField::SumOfProducts(lefts, rights); + c1 = BaseField::SumOfProductsSerial(lefts, rights); } c0_ = std::move(c0); c1_ = std::move(c1); From 3ad95f8fefb603eb2bd3f5c5887a31b183d5aaca Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 14:21:21 +0900 Subject: [PATCH 06/11] feat(math): enable `SumOfProducts` to accept different types of container --- tachyon/math/base/field.h | 10 +++++----- tachyon/math/base/rings.h | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tachyon/math/base/field.h b/tachyon/math/base/field.h index 00fb50ed5..12d933995 100644 --- a/tachyon/math/base/field.h +++ b/tachyon/math/base/field.h @@ -19,15 +19,15 @@ template class Field : public AdditiveGroup, public MultiplicativeGroup { public: // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ - template - constexpr static F SumOfProducts(const Container& a, const Container& b) { + template + constexpr static F SumOfProducts(const ContainerA& a, const ContainerB& b) { return Ring::SumOfProducts(a, b); } // Sum of products: a₁ * b₁ + a₂ * b₂ + ... + aₙ * bₙ - template - constexpr static F SumOfProductsSerial(const Container& a, - const Container& b) { + template + constexpr static F SumOfProductsSerial(const ContainerA& a, + const ContainerB& b) { return Ring::SumOfProductsSerial(a, b); } }; diff --git a/tachyon/math/base/rings.h b/tachyon/math/base/rings.h index e7e5972a5..c750ac014 100644 --- a/tachyon/math/base/rings.h +++ b/tachyon/math/base/rings.h @@ -30,8 +30,8 @@ class Ring : public AdditiveGroup, public MultiplicativeSemigroup { // |SumOfProductsSerial| for all call sites, it gets stuck when doing // unittests. I think we need a some general threshold to check whether it is // good to doing parallelization. - template - constexpr static F SumOfProducts(const Container& a, const Container& b) { + template + constexpr static F SumOfProducts(const ContainerA& a, const ContainerB& b) { size_t size = std::size(a); CHECK_EQ(size, std::size(b)); CHECK_NE(size, size_t{0}); @@ -52,9 +52,9 @@ class Ring : public AdditiveGroup, public MultiplicativeSemigroup { }); } - template - constexpr static F SumOfProductsSerial(const Container& a, - const Container& b) { + template + constexpr static F SumOfProductsSerial(const ContainerA& a, + const ContainerB& b) { size_t size = std::size(a); CHECK_EQ(size, std::size(b)); CHECK_NE(size, size_t{0}); @@ -62,9 +62,9 @@ class Ring : public AdditiveGroup, public MultiplicativeSemigroup { } private: - template - constexpr static F DoSumOfProductsSerial(const Container& a, - const Container& b) { + template + constexpr static F DoSumOfProductsSerial(const ContainerA& a, + const ContainerB& b) { size_t n = std::size(a); F sum = F::Zero(); for (size_t i = 0; i < n; ++i) { From 1616daff3476f269127d4719cea363240a3b5fbf Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 16:33:48 +0900 Subject: [PATCH 07/11] feat(zk): implement `halo2::Proof` Note that this doesn't include pairing part yet. --- tachyon/zk/plonk/halo2/BUILD.bazel | 6 +++++ tachyon/zk/plonk/halo2/proof.h | 42 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tachyon/zk/plonk/halo2/proof.h diff --git a/tachyon/zk/plonk/halo2/BUILD.bazel b/tachyon/zk/plonk/halo2/BUILD.bazel index 7556fd3c5..17e2d0aa1 100644 --- a/tachyon/zk/plonk/halo2/BUILD.bazel +++ b/tachyon/zk/plonk/halo2/BUILD.bazel @@ -78,6 +78,12 @@ tachyon_cc_library( ], ) +tachyon_cc_library( + name = "proof", + hdrs = ["proof.h"], + deps = ["//tachyon/zk/lookup:lookup_pair"], +) + tachyon_cc_library( name = "proof_serializer", hdrs = ["proof_serializer.h"], diff --git a/tachyon/zk/plonk/halo2/proof.h b/tachyon/zk/plonk/halo2/proof.h new file mode 100644 index 000000000..aa78519d9 --- /dev/null +++ b/tachyon/zk/plonk/halo2/proof.h @@ -0,0 +1,42 @@ +#ifndef TACHYON_ZK_PLONK_HALO2_PROOF_H_ +#define TACHYON_ZK_PLONK_HALO2_PROOF_H_ + +#include +#include + +#include "tachyon/zk/lookup/lookup_pair.h" + +namespace tachyon::zk::halo2 { + +template +struct Proof { + std::vector> advices_commitments_vec; + std::vector challenges; + F theta; + std::vector>> lookup_permuted_commitments_vec; + F beta; + F gamma; + std::vector> permutation_product_commitments_vec; + std::vector> lookup_product_commitments_vec; + C vanishing_random_poly_commitment; + F y; + std::vector vanishing_h_poly_commitments; + F x; + std::vector> instance_evals_vec; + std::vector> advice_evals_vec; + std::vector fixed_evals; + F vanishing_eval; + std::vector common_permutation_evals; + std::vector> permutation_product_evals_vec; + std::vector> permutation_product_next_evals_vec; + std::vector>> permutation_product_last_evals_vec; + std::vector> lookup_product_evals_vec; + std::vector> lookup_product_next_evals_vec; + std::vector> lookup_permuted_input_evals_vec; + std::vector> lookup_permuted_input_inv_evals_vec; + std::vector> lookup_permuted_table_evals_vec; +}; + +} // namespace tachyon::zk::halo2 + +#endif // TACHYON_ZK_PLONK_HALO2_PROOF_H_ From a5b413b24cc30e50ad6b2c3732176e88d0e2b127 Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 16:37:21 +0900 Subject: [PATCH 08/11] feat(zk): implement `halo2::ProofReader` Note that this doesn't include pairing part yet. See [verify_proof()](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/plonk/verifier.rs#L76-L240). --- tachyon/zk/plonk/halo2/BUILD.bazel | 11 ++ tachyon/zk/plonk/halo2/proof_reader.h | 268 ++++++++++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 tachyon/zk/plonk/halo2/proof_reader.h diff --git a/tachyon/zk/plonk/halo2/BUILD.bazel b/tachyon/zk/plonk/halo2/BUILD.bazel index 17e2d0aa1..a35030511 100644 --- a/tachyon/zk/plonk/halo2/BUILD.bazel +++ b/tachyon/zk/plonk/halo2/BUILD.bazel @@ -84,6 +84,17 @@ tachyon_cc_library( deps = ["//tachyon/zk/lookup:lookup_pair"], ) +tachyon_cc_library( + name = "proof_reader", + hdrs = ["proof_reader.h"], + deps = [ + ":proof", + "//tachyon/base:logging", + "//tachyon/crypto/transcripts:transcript", + "//tachyon/zk/plonk/keys:verifying_key", + ], +) + tachyon_cc_library( name = "proof_serializer", hdrs = ["proof_serializer.h"], diff --git a/tachyon/zk/plonk/halo2/proof_reader.h b/tachyon/zk/plonk/halo2/proof_reader.h new file mode 100644 index 000000000..897b65c7d --- /dev/null +++ b/tachyon/zk/plonk/halo2/proof_reader.h @@ -0,0 +1,268 @@ +#ifndef TACHYON_ZK_PLONK_HALO2_PROOF_READER_H_ +#define TACHYON_ZK_PLONK_HALO2_PROOF_READER_H_ + +#include +#include + +#include "tachyon/base/logging.h" +#include "tachyon/crypto/transcripts/transcript.h" +#include "tachyon/zk/plonk/halo2/proof.h" +#include "tachyon/zk/plonk/keys/verifying_key.h" + +namespace tachyon::zk::halo2 { + +enum class ProofCursor { + kAdviceCommitmentsVecAndChallenges, + kTheta, + kLookupPermutedCommitments, + kBetaAndGamma, + kPermutationProductCommitments, + kLookupProductCommitments, + kVanishingRandomPolyCommitment, + kY, + kVanishingHPolyCommitments, + kX, + kInstanceEvals, + kAdviceEvals, + kFixedEvals, + kVanishingEval, + kCommonPermutationEvals, + kPermutationEvals, + kLookupEvalsVec, + kDone, +}; + +template +class ProofReader { + public: + using F = typename PCSTy::Field; + using C = typename PCSTy::Commitment; + + ProofReader(const VerifyingKey& verifying_key, + crypto::TranscriptReader* transcript, size_t num_circuits) + : verifying_key_(verifying_key), + transcript_(transcript), + num_circuits_(num_circuits) {} + + const Proof& proof() const { return proof_; } + Proof& proof() { return proof_; } + + void ReadAdviceCommitmentsVecAndChallenges() { + CHECK_EQ(cursor_, ProofCursor::kAdviceCommitmentsVecAndChallenges); + const ConstraintSystem& constraint_system = + verifying_key_.constraint_system(); + proof_.advices_commitments_vec.resize(num_circuits_); + for (size_t i = 0; i < num_circuits_; ++i) { + proof_.advices_commitments_vec[i].reserve( + constraint_system.num_advice_columns()); + } + proof_.challenges.reserve(constraint_system.challenge_phases().size()); + for (Phase current_phase : constraint_system.GetPhases()) { + for (size_t i = 0; i < num_circuits_; ++i) { + for (Phase phase : constraint_system.advice_column_phases()) { + if (current_phase == phase) { + proof_.advices_commitments_vec[i].push_back(Read()); + } + } + } + for (Phase phase : constraint_system.challenge_phases()) { + if (current_phase == phase) { + proof_.challenges.push_back(transcript_->SqueezeChallenge()); + } + } + } + cursor_ = ProofCursor::kTheta; + } + + void ReadTheta() { + CHECK_EQ(cursor_, ProofCursor::kTheta); + proof_.theta = transcript_->SqueezeChallenge(); + cursor_ = ProofCursor::kLookupPermutedCommitments; + } + + void ReadLookupPermutedCommitments() { + CHECK_EQ(cursor_, ProofCursor::kLookupPermutedCommitments); + size_t num_lookups = verifying_key_.constraint_system().lookups().size(); + proof_.lookup_permuted_commitments_vec = + base::CreateVector(num_circuits_, [this, num_lookups]() { + return base::CreateVector(num_lookups, [this]() { + C input = Read(); + C table = Read(); + return LookupPair(std::move(input), std::move(table)); + }); + }); + cursor_ = ProofCursor::kBetaAndGamma; + } + + void ReadBetaAndGamma() { + CHECK_EQ(cursor_, ProofCursor::kBetaAndGamma); + proof_.beta = transcript_->SqueezeChallenge(); + proof_.gamma = transcript_->SqueezeChallenge(); + cursor_ = ProofCursor::kPermutationProductCommitments; + } + + void ReadPermutationProductCommitments() { + CHECK_EQ(cursor_, ProofCursor::kPermutationProductCommitments); + const ConstraintSystem& constraint_system = + verifying_key_.constraint_system(); + size_t chunk_len = constraint_system.ComputeDegree() - 2; + size_t num_products = + (constraint_system.permutation().columns().size() + chunk_len - 1) / + chunk_len; + proof_.permutation_product_commitments_vec = base::CreateVector( + num_circuits_, + [this, num_products]() { return ReadMany(num_products); }); + cursor_ = ProofCursor::kLookupProductCommitments; + } + + void ReadLookupProductCommitments() { + CHECK_EQ(cursor_, ProofCursor::kLookupProductCommitments); + size_t num_lookups = verifying_key_.constraint_system().lookups().size(); + proof_.lookup_product_commitments_vec = base::CreateVector( + num_circuits_, + [this, num_lookups]() { return ReadMany(num_lookups); }); + cursor_ = ProofCursor::kVanishingRandomPolyCommitment; + } + + void ReadVanishingRandomPolyCommitment() { + CHECK_EQ(cursor_, ProofCursor::kVanishingRandomPolyCommitment); + proof_.vanishing_random_poly_commitment = Read(); + cursor_ = ProofCursor::kY; + } + + void ReadY() { + CHECK_EQ(cursor_, ProofCursor::kY); + proof_.y = transcript_->SqueezeChallenge(); + cursor_ = ProofCursor::kVanishingHPolyCommitments; + } + + void ReadVanishingHPolyCommitments() { + CHECK_EQ(cursor_, ProofCursor::kVanishingHPolyCommitments); + size_t quotient_poly_degree = + verifying_key_.constraint_system().ComputeDegree() - 1; + proof_.vanishing_h_poly_commitments = ReadMany(quotient_poly_degree); + cursor_ = ProofCursor::kX; + } + + void ReadX() { + CHECK_EQ(cursor_, ProofCursor::kX); + proof_.x = transcript_->SqueezeChallenge(); + cursor_ = ProofCursor::kInstanceEvals; + } + + void ReadInstanceEvalsIfQueryInstance() { + CHECK_EQ(cursor_, ProofCursor::kInstanceEvals); + size_t num_instance_queries = + verifying_key_.constraint_system().instance_queries().size(); + return base::CreateVector(num_circuits_, [this, num_instance_queries]() { + return ReadMany(num_instance_queries); + }); + cursor_ = ProofCursor::kAdviceEvals; + } + + void ReadInstanceEvalsIfNoQueryInstance() { + CHECK_EQ(cursor_, ProofCursor::kInstanceEvals); + cursor_ = ProofCursor::kAdviceEvals; + } + + void ReadAdviceEvals() { + CHECK_EQ(cursor_, ProofCursor::kAdviceEvals); + size_t num_advice_queries = + verifying_key_.constraint_system().advice_queries().size(); + proof_.advice_evals_vec = + base::CreateVector(num_circuits_, [this, num_advice_queries]() { + return ReadMany(num_advice_queries); + }); + cursor_ = ProofCursor::kFixedEvals; + } + + void ReadFixedEvals() { + CHECK_EQ(cursor_, ProofCursor::kFixedEvals); + size_t num_fixed_queries = + verifying_key_.constraint_system().fixed_queries().size(); + proof_.fixed_evals = ReadMany(num_fixed_queries); + cursor_ = ProofCursor::kVanishingEval; + } + + void ReadVanishingEval() { + CHECK_EQ(cursor_, ProofCursor::kVanishingEval); + proof_.vanishing_eval = Read(); + cursor_ = ProofCursor::kCommonPermutationEvals; + } + + void ReadCommonPermutationEvals() { + CHECK_EQ(cursor_, ProofCursor::kCommonPermutationEvals); + proof_.common_permutation_evals = ReadMany( + verifying_key_.permutation_verifying_key().commitments().size()); + cursor_ = ProofCursor::kPermutationEvals; + } + + void ReadPermutationEvals() { + CHECK_EQ(cursor_, ProofCursor::kPermutationEvals); + proof_.permutation_product_evals_vec.resize(num_circuits_); + proof_.permutation_product_next_evals_vec.resize(num_circuits_); + proof_.permutation_product_last_evals_vec.resize(num_circuits_); + for (size_t i = 0; i < num_circuits_; ++i) { + size_t size = proof_.permutation_product_commitments_vec[i].size(); + proof_.permutation_product_evals_vec[i].reserve(size); + proof_.permutation_product_next_evals_vec[i].reserve(size); + proof_.permutation_product_last_evals_vec[i].reserve(size); + for (size_t j = 0; j < size; ++j) { + proof_.permutation_product_evals_vec[i].push_back(Read()); + proof_.permutation_product_next_evals_vec[i].push_back(Read()); + proof_.permutation_product_last_evals_vec[i].push_back( + (j != size - 1) ? std::optional(Read()) : std::optional()); + } + } + cursor_ = ProofCursor::kLookupEvalsVec; + } + + void ReadLookupEvals() { + CHECK_EQ(cursor_, ProofCursor::kLookupEvalsVec); + proof_.lookup_product_evals_vec.resize(num_circuits_); + proof_.lookup_product_next_evals_vec.resize(num_circuits_); + proof_.lookup_permuted_input_evals_vec.resize(num_circuits_); + proof_.lookup_permuted_input_inv_evals_vec.resize(num_circuits_); + proof_.lookup_permuted_table_evals_vec.resize(num_circuits_); + for (size_t i = 0; i < num_circuits_; ++i) { + size_t size = proof_.lookup_product_commitments_vec[i].size(); + proof_.lookup_product_evals_vec[i].reserve(size); + proof_.lookup_product_next_evals_vec[i].reserve(size); + proof_.lookup_permuted_input_evals_vec[i].reserve(size); + proof_.lookup_permuted_input_inv_evals_vec[i].reserve(size); + proof_.lookup_permuted_table_evals_vec[i].reserve(size); + for (size_t j = 0; j < size; ++j) { + proof_.lookup_product_evals_vec[i].push_back(Read()); + proof_.lookup_product_next_evals_vec[i].push_back(Read()); + proof_.lookup_permuted_input_evals_vec[i].push_back(Read()); + proof_.lookup_permuted_input_inv_evals_vec[i].push_back(Read()); + proof_.lookup_permuted_table_evals_vec[i].push_back(Read()); + } + } + // TODO(chokobole): Implement reading data for the last pairing step. + } + + private: + template + T Read() { + T value; + CHECK(transcript_->ReadFromProof(&value)); + return value; + } + + template + std::vector ReadMany(size_t n) { + return base::CreateVector(n, [this]() { return Read(); }); + } + + const VerifyingKey& verifying_key_; + // not owned + crypto::TranscriptReader* const transcript_ = nullptr; + size_t num_circuits_ = 0; + Proof proof_; + ProofCursor cursor_ = ProofCursor::kAdviceCommitmentsVecAndChallenges; +}; + +} // namespace tachyon::zk::halo2 + +#endif // TACHYON_ZK_PLONK_HALO2_PROOF_READER_H_ From 6fccb2e18207afc970d805a61c2c77f3de2cf826 Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 16:39:02 +0900 Subject: [PATCH 09/11] feat(zk): implement `halo2::Verifier` What `halo2::Verifier` does is some validation done from halo2 `verify_proof` function and read proof using `halo2::ProofReader`. See [verify_proof()](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/plonk/verifier.rs#L41-L70). --- tachyon/zk/base/entities/entity.h | 6 - tachyon/zk/plonk/halo2/BUILD.bazel | 14 + tachyon/zk/plonk/halo2/prover.h | 10 + tachyon/zk/plonk/halo2/prover_test.h | 6 + tachyon/zk/plonk/halo2/verifier.h | 253 ++++++++++++++++++ .../vanishing/vanishing_argument_unittest.cc | 15 +- 6 files changed, 287 insertions(+), 17 deletions(-) create mode 100644 tachyon/zk/plonk/halo2/verifier.h diff --git a/tachyon/zk/base/entities/entity.h b/tachyon/zk/base/entities/entity.h index 0fbd135dc..70901b43a 100644 --- a/tachyon/zk/base/entities/entity.h +++ b/tachyon/zk/base/entities/entity.h @@ -49,12 +49,6 @@ class Entity { } crypto::Transcript* transcript() { return transcript_.get(); } - PCSTy&& TakePCS() { return std::move(pcs_); } - std::unique_ptr TakeDomain() { return std::move(domain_); } - std::unique_ptr TakeExtendedDomain() { - return std::move(extended_domain_); - } - protected: PCSTy pcs_; std::unique_ptr domain_; diff --git a/tachyon/zk/plonk/halo2/BUILD.bazel b/tachyon/zk/plonk/halo2/BUILD.bazel index a35030511..73b508a5a 100644 --- a/tachyon/zk/plonk/halo2/BUILD.bazel +++ b/tachyon/zk/plonk/halo2/BUILD.bazel @@ -110,6 +110,7 @@ tachyon_cc_library( hdrs = ["prover.h"], deps = [ ":random_field_generator", + ":verifier", "//tachyon/zk/base/entities:prover_base", ], ) @@ -153,6 +154,19 @@ tachyon_cc_library( ], ) +tachyon_cc_library( + name = "verifier", + hdrs = ["verifier.h"], + deps = [ + ":proof_reader", + "//tachyon/base/containers:container_util", + "//tachyon/zk/base/entities:verifier_base", + "//tachyon/zk/plonk/keys:verifying_key", + "@com_google_absl//absl/functional:bind_front", + "@com_google_googletest//:gtest_prod", + ], +) + tachyon_cc_unittest( name = "halo2_unittests", srcs = [ diff --git a/tachyon/zk/plonk/halo2/prover.h b/tachyon/zk/plonk/halo2/prover.h index d24242fd1..222a2fedf 100644 --- a/tachyon/zk/plonk/halo2/prover.h +++ b/tachyon/zk/plonk/halo2/prover.h @@ -12,6 +12,7 @@ #include "tachyon/zk/base/entities/prover_base.h" #include "tachyon/zk/plonk/halo2/random_field_generator.h" +#include "tachyon/zk/plonk/halo2/verifier.h" namespace tachyon::zk::halo2 { @@ -52,6 +53,15 @@ class Prover : public ProverBase { crypto::XORShiftRNG* rng() { return rng_.get(); } RandomFieldGenerator* generator() { return generator_.get(); } + std::unique_ptr> ToVerifier( + std::unique_ptr> reader) { + std::unique_ptr> ret = std::make_unique>( + std::move(this->pcs_), std::move(reader)); + ret->set_domain(std::move(this->domain_)); + ret->set_extended_domain(std::move(this->extended_domain_)); + return ret; + } + private: Prover(PCSTy&& pcs, std::unique_ptr> writer, diff --git a/tachyon/zk/plonk/halo2/prover_test.h b/tachyon/zk/plonk/halo2/prover_test.h index e16af7dce..210f819ad 100644 --- a/tachyon/zk/plonk/halo2/prover_test.h +++ b/tachyon/zk/plonk/halo2/prover_test.h @@ -56,6 +56,12 @@ class ProverTest : public testing::Test { } protected: + std::unique_ptr> CreateVerifier(base::Buffer read_buf) { + std::unique_ptr> reader = + std::make_unique>(std::move(read_buf)); + return prover_->ToVerifier(std::move(reader)); + } + std::unique_ptr> prover_; }; diff --git a/tachyon/zk/plonk/halo2/verifier.h b/tachyon/zk/plonk/halo2/verifier.h new file mode 100644 index 000000000..1edd22659 --- /dev/null +++ b/tachyon/zk/plonk/halo2/verifier.h @@ -0,0 +1,253 @@ +// Copyright 2020-2022 The Electric Coin Company +// Copyright 2022 The Halo2 developers +// Use of this source code is governed by a MIT/Apache-2.0 style license that +// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2 +// file. + +#ifndef TACHYON_ZK_PLONK_HALO2_VERIFIER_H_ +#define TACHYON_ZK_PLONK_HALO2_VERIFIER_H_ + +#include +#include +#include + +#include "absl/functional/bind_front.h" +#include "gtest/gtest_prod.h" + +#include "tachyon/base/containers/container_util.h" +#include "tachyon/zk/base/entities/verifier_base.h" +#include "tachyon/zk/plonk/halo2/proof_reader.h" +#include "tachyon/zk/plonk/keys/verifying_key.h" + +namespace tachyon::zk::halo2 { + +template +class Verifier : public VerifierBase { + public: + using F = typename PCSTy::Field; + using Commitment = typename PCSTy::Commitment; + using Evals = typename PCSTy::Evals; + using Poly = typename PCSTy::Poly; + using Coefficients = typename Poly::Coefficients; + + using VerifierBase::VerifierBase; + + [[nodiscard]] bool VerifyProof( + const VerifyingKey& vkey, + const std::vector>& instance_columns_vec) { + return VerifyProofForTesting(vkey, instance_columns_vec, nullptr); + } + + private: + FRIEND_TEST(SimpleCircuitTest, Verify); + FRIEND_TEST(SimpleLookupCircuitTest, Verify); + + bool VerifyProofForTesting( + const VerifyingKey& vkey, + const std::vector>& instance_columns_vec, + Proof* proof_out) { + if (!ValidateInstanceColumnsVec(vkey, instance_columns_vec)) return false; + + std::vector> instance_commitments_vec; + if constexpr (PCSTy::kQueryInstance) { + instance_commitments_vec = CommitColumnsVec(vkey, instance_columns_vec); + } else { + instance_commitments_vec.resize(instance_columns_vec.size()); + } + + crypto::TranscriptReader* transcript = this->GetReader(); + CHECK(transcript->WriteToTranscript(vkey.transcript_repr())); + + if constexpr (PCSTy::kQueryInstance) { + WriteCommitmentsVecToTranscript(transcript, instance_commitments_vec); + } else { + WriteColumnsVecToTranscript(transcript, instance_columns_vec); + } + + ProofReader proof_reader(vkey, transcript, + instance_commitments_vec.size()); + Proof& proof = proof_reader.proof(); + proof_reader.ReadAdviceCommitmentsVecAndChallenges(); + proof_reader.ReadTheta(); + proof_reader.ReadLookupPermutedCommitments(); + proof_reader.ReadBetaAndGamma(); + proof_reader.ReadPermutationProductCommitments(); + proof_reader.ReadLookupProductCommitments(); + proof_reader.ReadVanishingRandomPolyCommitment(); + proof_reader.ReadY(); + proof_reader.ReadVanishingHPolyCommitments(); + proof_reader.ReadX(); + if constexpr (PCSTy::kQueryInstance) { + proof_reader.ReadInstanceEvalsIfQueryInstance(); + } else { + proof_reader.ReadInstanceEvalsIfNoQueryInstance(); + proof.instance_evals_vec = + ComputeInstanceEvalsVec(vkey, instance_columns_vec, proof.x); + } + proof_reader.ReadAdviceEvals(); + proof_reader.ReadFixedEvals(); + proof_reader.ReadVanishingEval(); + proof_reader.ReadCommonPermutationEvals(); + proof_reader.ReadPermutationEvals(); + proof_reader.ReadLookupEvals(); + + if (proof_out) { + *proof_out = proof; + } + return true; + } + + bool ValidateInstanceColumnsVec( + const VerifyingKey& vkey, + const std::vector>& instance_columns_vec) { + size_t num_instance_columns = + vkey.constraint_system().num_instance_columns(); + auto check_num_instance_columns = + [num_instance_columns](const std::vector& instance_columns) { + if (instance_columns.size() != num_instance_columns) { + LOG(ERROR) << "The size of instance columns doesn't match with " + "constraint system"; + return false; + } + return true; + }; + + size_t max_rows = this->pcs_.N() - + (vkey.constraint_system().ComputeBlindingFactors() + 1); + auto check_rows = [max_rows](const Evals& instance_columns) { + if (instance_columns.NumElements() > max_rows) { + LOG(ERROR) << "Too many number of elements in instance column"; + return false; + } + return true; + }; + + return std::all_of(instance_columns_vec.begin(), instance_columns_vec.end(), + [&check_num_instance_columns, &check_rows]( + const std::vector& instance_columns) { + if (!check_num_instance_columns(instance_columns)) + return false; + return std::all_of(instance_columns.begin(), + instance_columns.end(), check_rows); + }); + } + + std::vector CommitColumns(const std::vector& columns) { + return base::Map(columns, [this](const Evals& column) { + std::vector expanded_evals = column.evaluations(); + expanded_evals.resize(this->pcs_.N()); + Commitment c; + CHECK(this->pcs_.CommitLagrange(Evals(std::move(expanded_evals), &c))); + return c; + }); + } + + std::vector> CommitColumnsVec( + const std::vector>& columns_vec) { + return base::Map(columns_vec, absl::bind_front(&CommitColumns, this)); + } + + static void WriteCommitmentsVecToTranscript( + crypto::TranscriptReader* transcript, + const std::vector>& commitments_vec) { + for (const std::vector& commitments : commitments_vec) { + for (const Commitment& commitment : commitments) { + CHECK(transcript->WriteToTranscript(commitment)); + } + } + } + + static void WriteColumnsVecToTranscript( + crypto::TranscriptReader* transcript, + const std::vector>& columns_vec) { + for (const std::vector& columns : columns_vec) { + for (const Evals& column : columns) { + for (const F& value : column.evaluations()) { + CHECK(transcript->WriteToTranscript(value)); + } + } + } + } + + static size_t ComputeMaxRow(const std::vector& columns) { + if (columns.empty()) return 0; + std::vector rows = base::Map( + columns, [](const Evals& evals) { return evals.NumElements(); }); + return *std::max_element(rows.begin(), rows.end()); + } + + static F ComputeInstanceEval(const std::vector& instance_columns, + const InstanceQueryData& instance_query, + const std::vector& partial_lagrange_coeffs, + size_t max_rotation) { + const std::vector& instances = + instance_columns[instance_query.column().index()].evaluations(); + size_t offset = max_rotation - instance_query.rotation().value(); + absl::Span sub_partial_lagrange_coeffs = + absl::MakeConstSpan(partial_lagrange_coeffs); + sub_partial_lagrange_coeffs = + sub_partial_lagrange_coeffs.subspan(offset, instances.size()); + return F::SumOfProductsSerial(instances, sub_partial_lagrange_coeffs); + } + + static std::vector ComputeInstanceEvals( + const std::vector& instance_columns, + const std::vector& instance_queries, + const std::vector& partial_lagrange_coeffs, size_t max_rotation) { + return base::Map(instance_queries, + [&instance_columns, &partial_lagrange_coeffs, + max_rotation](const InstanceQueryData& instance_query) { + return ComputeInstanceEval( + instance_columns, instance_query, + partial_lagrange_coeffs, max_rotation); + }); + } + + std::vector> ComputeInstanceEvalsVec( + const VerifyingKey& vkey, + const std::vector>& instance_columns_vec, const F& x) { + struct RotationRange { + int32_t min = 0; + int32_t max = 0; + }; + + const std::vector& instance_queries = + vkey.constraint_system().instance_queries(); + RotationRange range = std::accumulate( + instance_queries.begin(), instance_queries.end(), RotationRange(), + [](RotationRange& range, const InstanceQueryData& instance) { + int32_t rotation_value = instance.rotation().value(); + if (rotation_value < range.min) { + range.min = rotation_value; + } else if (rotation_value > range.max) { + range.max = rotation_value; + } + return range; + }); + + std::vector max_instances_rows = + base::Map(instance_columns_vec, &ComputeMaxRow); + auto max_instances_row_it = + std::max_element(max_instances_rows.begin(), max_instances_rows.end()); + size_t max_instances_row = max_instances_row_it != max_instances_rows.end() + ? *max_instances_row_it + : 0; + std::vector partial_lagrange_coeffs = + this->domain_->EvaluatePartialLagrangeCoefficients( + x, base::Range(-range.max, + static_cast(max_instances_row) + + std::abs(range.min))); + + return base::Map(instance_columns_vec, + [&instance_queries, &partial_lagrange_coeffs, + &range](const std::vector& instance_columns) { + return ComputeInstanceEvals( + instance_columns, instance_queries, + partial_lagrange_coeffs, range.max); + }); + } +}; + +} // namespace tachyon::zk::halo2 + +#endif // TACHYON_ZK_PLONK_HALO2_VERIFIER_H_ diff --git a/tachyon/zk/plonk/vanishing/vanishing_argument_unittest.cc b/tachyon/zk/plonk/vanishing/vanishing_argument_unittest.cc index 6427a5d56..f400ccef8 100644 --- a/tachyon/zk/plonk/vanishing/vanishing_argument_unittest.cc +++ b/tachyon/zk/plonk/vanishing/vanishing_argument_unittest.cc @@ -43,17 +43,10 @@ TEST_F(VanishingArgumentTest, VanishingArgument) { std::vector> h_x = OpenVanishingArgument(std::move(evaluated), x); - base::Buffer read_buf(prover_->GetWriter()->buffer().buffer(), - prover_->GetWriter()->buffer().buffer_len()); - std::unique_ptr> reader = - absl::WrapUnique( - new halo2::Blake2bReader(std::move(read_buf))); - - std::unique_ptr> verifier = - std::make_unique>( - VerifierBase(prover_->TakePCS(), std::move(reader))); - verifier->set_domain(prover_->TakeDomain()); - verifier->set_extended_domain(prover_->TakeExtendedDomain()); + base::Uint8VectorBuffer& write_buf = prover_->GetWriter()->buffer(); + write_buf.set_buffer_offset(0); + std::unique_ptr> verifier = + CreateVerifier(std::move(write_buf)); VanishingCommitted committed_v; ASSERT_TRUE(ReadCommitmentsBeforeY(verifier->GetReader(), &committed_v)); From ca02479191ce09ced2f95de79d26b7466545dd9f Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 16:35:44 +0900 Subject: [PATCH 10/11] feat(zk): add equality operator to `LookupPair` --- tachyon/zk/lookup/lookup_pair.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tachyon/zk/lookup/lookup_pair.h b/tachyon/zk/lookup/lookup_pair.h index e58ce32b3..3a722cf3b 100644 --- a/tachyon/zk/lookup/lookup_pair.h +++ b/tachyon/zk/lookup/lookup_pair.h @@ -25,6 +25,11 @@ class LookupPair { T&& TakeInput() && { return std::move(input_); } U&& TakeTable() && { return std::move(table_); } + bool operator==(const LookupPair& other) const { + return input_ == other.input_ && table_ == other.table_; + } + bool operator!=(const LookupPair& other) const { return !operator==(other); } + private: T input_; U table_; From 295e50c8218409dc018a198cece2054befd21a3b Mon Sep 17 00:00:00 2001 From: Ryan Kim Date: Mon, 18 Dec 2023 16:41:35 +0900 Subject: [PATCH 11/11] feat(zk): implement proof verification initially Note that this doesn't include pairing part yet. --- .../zk/plonk/circuit/examples/circuit_test.h | 39 ++- .../examples/simple_circuit_unittest.cc | 277 +++++++++++++++++- .../simple_lookup_circuit_unittest.cc | 245 +++++++++++++++- 3 files changed, 554 insertions(+), 7 deletions(-) diff --git a/tachyon/zk/plonk/circuit/examples/circuit_test.h b/tachyon/zk/plonk/circuit/examples/circuit_test.h index bfdff7b4a..93c1c5905 100644 --- a/tachyon/zk/plonk/circuit/examples/circuit_test.h +++ b/tachyon/zk/plonk/circuit/examples/circuit_test.h @@ -1,14 +1,18 @@ #ifndef TACHYON_ZK_PLONK_CIRCUIT_EXAMPLES_CIRCUIT_TEST_H_ #define TACHYON_ZK_PLONK_CIRCUIT_EXAMPLES_CIRCUIT_TEST_H_ +#include #include #include +#include "absl/types/span.h" + +#include "tachyon/zk/lookup/lookup_pair.h" #include "tachyon/zk/plonk/halo2/prover_test.h" -namespace tachyon::zk { +namespace tachyon::zk::halo2 { -class CircuitTest : public halo2::ProverTest { +class CircuitTest : public ProverTest { protected: struct Point { std::string_view x; @@ -25,6 +29,17 @@ class CircuitTest : public halo2::ProverTest { return base::Map(points, &CreateCommitment); } + static std::vector> CreateLookupPermutedCommitments( + const std::vector& input_points, + const std::vector& table_points) { + std::vector> lookup_pairs; + return base::Map( + input_points, [&table_points](size_t i, const Point& input_point) { + return LookupPair(CreateCommitment(input_point), + CreateCommitment(table_points[i])); + }); + } + static Evals CreateColumn(const std::vector& column) { std::vector evaluations = base::Map( column, [](std::string_view coeff) { return F::FromHexString(coeff); }); @@ -61,8 +76,26 @@ class CircuitTest : public halo2::ProverTest { const std::vector>& polys) { return base::Map(polys, &CreatePoly); } + + static std::vector CreateEvals( + const std::vector& evals) { + return base::Map( + evals, [](std::string_view eval) { return F::FromHexString(eval); }); + } + + static std::vector> CreateOptionalEvals( + const std::vector& evals) { + return base::Map(evals, [](std::string_view eval) { + if (eval.empty()) return std::optional(); + return std::optional(F::FromHexString(eval)); + }); + } + + static base::Buffer CreateBufferWithProof(absl::Span proof) { + return {proof.data(), proof.size()}; + } }; -} // namespace tachyon::zk +} // namespace tachyon::zk::halo2 #endif // TACHYON_ZK_PLONK_CIRCUIT_EXAMPLES_CIRCUIT_TEST_H_ diff --git a/tachyon/zk/plonk/circuit/examples/simple_circuit_unittest.cc b/tachyon/zk/plonk/circuit/examples/simple_circuit_unittest.cc index 916cf4ba7..9cf566bee 100644 --- a/tachyon/zk/plonk/circuit/examples/simple_circuit_unittest.cc +++ b/tachyon/zk/plonk/circuit/examples/simple_circuit_unittest.cc @@ -6,10 +6,81 @@ #include "tachyon/zk/plonk/halo2/pinned_verifying_key.h" #include "tachyon/zk/plonk/keys/proving_key.h" -namespace tachyon::zk { +namespace tachyon::zk::halo2 { namespace { +constexpr uint8_t kExpectedProof[] = { + 206, 109, 139, 136, 181, 35, 204, 231, 212, 93, 105, 116, 154, 77, 204, + 23, 71, 148, 11, 151, 126, 145, 6, 150, 171, 185, 254, 230, 41, 136, + 76, 141, 132, 227, 154, 206, 134, 35, 253, 67, 8, 186, 228, 143, 116, + 139, 145, 119, 85, 253, 127, 208, 95, 153, 195, 112, 209, 116, 172, 45, + 15, 175, 128, 142, 204, 179, 55, 39, 34, 51, 72, 104, 203, 18, 200, + 167, 238, 128, 150, 95, 51, 70, 102, 245, 31, 126, 175, 75, 128, 131, + 210, 183, 26, 150, 167, 148, 4, 122, 209, 122, 247, 23, 28, 107, 152, + 91, 100, 24, 117, 196, 95, 56, 57, 63, 0, 13, 164, 147, 133, 185, + 227, 117, 218, 126, 171, 80, 126, 29, 105, 149, 45, 14, 144, 234, 250, + 146, 228, 251, 88, 94, 78, 192, 70, 209, 240, 185, 175, 207, 176, 237, + 223, 162, 182, 167, 55, 27, 174, 75, 86, 146, 242, 122, 52, 24, 231, + 152, 166, 17, 135, 33, 51, 216, 81, 14, 114, 175, 246, 221, 85, 47, + 12, 246, 175, 152, 25, 30, 71, 14, 217, 13, 253, 129, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, + 124, 38, 97, 203, 45, 175, 171, 18, 114, 71, 19, 96, 21, 168, 135, + 110, 37, 229, 152, 175, 52, 149, 169, 112, 55, 141, 136, 233, 197, 87, + 33, 12, 248, 45, 245, 255, 25, 21, 60, 244, 81, 90, 111, 175, 128, + 152, 67, 107, 251, 200, 109, 221, 63, 39, 110, 145, 162, 222, 19, 222, + 231, 116, 159, 18, 176, 226, 254, 178, 169, 131, 246, 249, 216, 204, 245, + 126, 14, 4, 60, 243, 190, 16, 143, 233, 121, 48, 35, 89, 249, 30, + 177, 21, 176, 212, 15, 104, 131, 160, 212, 128, 253, 250, 162, 45, 82, + 102, 102, 122, 91, 166, 246, 215, 246, 183, 243, 105, 62, 217, 187, 0, + 204, 214, 174, 4, 59, 14, 38, 128, 53, 79, 244, 40, 240, 226, 63, + 154, 124, 81, 250, 85, 206, 241, 220, 188, 207, 0, 8, 38, 48, 13, + 222, 204, 177, 10, 132, 98, 177, 91, 38, 92, 92, 44, 89, 83, 82, + 255, 169, 79, 241, 50, 24, 75, 236, 80, 251, 125, 22, 180, 97, 200, + 127, 13, 206, 115, 245, 155, 160, 230, 136, 154, 30, 172, 120, 173, 39, + 72, 19, 73, 8, 116, 13, 98, 133, 200, 250, 234, 55, 132, 198, 99, + 242, 169, 149, 17, 203, 131, 253, 90, 251, 56, 205, 125, 9, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 219, 98, 123, 150, 137, 140, 239, 244, 207, 227, 226, 239, 50, 245, + 206, 4, 135, 3, 203, 197, 107, 95, 238, 229, 70, 60, 3, 84, 16, + 193, 21, 186, 141, 39, 27, 61, 37, 131, 39, 58, 247, 149, 128, 72, + 154, 7, 98, 194, 213, 81, 39, 101, 190, 142, 121, 16, 198, 34, 24, + 115, 198, 18, 26, 144, 125, 33, 75, 113, 228, 149, 7, 80, 26, 242, + 236, 2, 250, 37, 139, 215, 91, 203, 46, 222, 13, 30, 16, 43, 95, + 137, 62, 35, 128, 253, 0, 252, 149, 54, 172, 100, 182, 3, 142, 64, + 61, 20, 112, 162, 3, 168, 57, 60, 208, 203, 114, 237, 54, 238, 243, + 178, 83, 100, 252, 146, 17, 48, 19, 248, 244, 246, 126, 158, 218, 45, + 239, 52, 234, 165, 93, 254, 40, 74, 97, 79, 71, 103, 94, 68, 40, + 254, 199, 137, 112, 217, 4, 174, 90, 36, 10, 160, 10, 23, 50, 127, + 108, 35, 107, 233, 162, 124, 252, 90, 154, 69, 130, 138, 146, 152, 92, + 204, 222, 218, 175, 136, 170, 222, 10, 121, 198, 196, 0, 118, 232, 193, + 37, 167, 252, 236, 132, 180, 32, 133, 173, 84, 210, 183, 226, 17, 152, + 254, 134, 173, 241, 28, 87, 229, 48, 185, 197, 202, 126, 116, 13, 158, + 17, 242, 206, 155, 216, 163, 131, 207, 97, 146, 105, 198, 21, 29, 235, + 163, 253, 248, 54, 166, 140, 84, 109, 77, 88, 15, 133, 239, 177, 66, + 35, 141, 193, 65, 27, 252, 207, 0, 238, 201, 89, 146, 26, 31, 49, + 36, 248, 237, 114, 159, 174, 81, 102, 131, 204, 164, 209, 155, 208, 54, + 242, 11, 26, 18, 81, 255, 169, 79, 127, 254, 205, 201, 84, 81, 26, + 100, 113, 181, 175, 12, 64, 206, 141, 30, 234, 221, 69, 88, 123, 58, + 46, 251, 89, 163, 12, 0, 129, 169, 250, 211, 36, 4, 21, 235, 7, + 141, 16, 88, 60, 29, 62, 39, 190, 103, 126, 80, 118, 119, 139, 28, + 111, 2, 47, 160, 211, 93, 37, 106, 199, 117, 42, 209, 3, 67, 40, + 228, 53, 162, 184, 195, 168, 100, 242, 224, 72, 249, 239, 186, 235, 197, + 16, 140, 187, 204, 147, 92, 10, 4, 3, 195, 25, 12, 118, 126, 116, + 71, 45, 243, 105, 220, 34, 185, 219, 254, 31, 237, 109, 158, 176, 236, + 151, 52, 245, 254, 108, 56, 157, 136, 166, 107, 43, 100, 140, 44, 126, + 190, 248, 75, 134, 46, 182, 90, 241, 5, 37, 202, 235, 23, 237, 68, + 131, 22, 85, 163, 159, 155, 92, 163, 70, 168, 174, 112, 0, 188, 235, + 127, 169, 179, 70, 81, 52, 243, 101, 53, 74, 114, 117, 219, 241, 37, + 65, 109, 119, 112, 207, 45, 130, 99, 31, 149, 218, 238, 224, 161, 30, + 37, 157, 153, 114, 39, 20, 158, 233, 47, 4, 162, 237, 192, 218, 12, + 61, 155, 149, 69, 29, 211, 217, 53, 3, 249, 233, 19, 147, 126, 73, + 150, 24, 76, 186, 238, 245, 143, 157, 17, 150, 41, 114, 91, 230, 104, + 213, 121, 49, 182, 91, 207, 101, 207, 228, 155, 97, 73, 192, 230, 142, + 99, 202, 97, 38}; + class SimpleCircuitTest : public CircuitTest {}; } // namespace @@ -556,4 +627,206 @@ TEST_F(SimpleCircuitTest, LoadProvingKey) { } } -} // namespace tachyon::zk +TEST_F(SimpleCircuitTest, Verify) { + size_t n = 16; + CHECK(prover_->pcs().UnsafeSetup(n, F(2))); + prover_->set_domain(Domain::Create(n)); + + F constant(7); + F a(2); + F b(3); + SimpleCircuit circuit(constant, a, b); + + VerifyingKey vkey; + ASSERT_TRUE(vkey.Load(prover_.get(), circuit)); + + std::vector owned_proof(std::begin(kExpectedProof), + std::end(kExpectedProof)); + std::unique_ptr> verifier = + CreateVerifier(CreateBufferWithProof(absl::MakeSpan(owned_proof))); + F c = constant * a.Square() * b.Square(); + std::vector instance_column = {std::move(c)}; + std::vector instance_columns = {Evals(std::move(instance_column))}; + std::vector> instance_columns_vec = { + std::move(instance_columns)}; + + Proof proof; + ASSERT_TRUE( + verifier->VerifyProofForTesting(vkey, instance_columns_vec, &proof)); + + std::vector> expected_advice_commitments_vec; + { + std::vector points = { + {"0x0d4c8829e6feb9ab9606917e970b944717cc4d9a74695dd4e7cc23b5888b6dce", + "0x03a99ef4660a95515763e072043119fcbf6d3f3b709af6bf05b5c8b4d815a775"}, + {"0x0e80af0f2dac74d170c3995fd07ffd5577918b748fe4ba0843fd2386ce9ae384", + "0x058b31b773e7a0e22f1ef9d6bbcc154b3dfaec09ff6c78084c1ae5c150f6624d"}, + }; + expected_advice_commitments_vec.push_back(CreateCommitments(points)); + } + EXPECT_EQ(proof.advices_commitments_vec, expected_advice_commitments_vec); + + EXPECT_TRUE(proof.challenges.empty()); + + F expected_theta = F::FromHexString( + "0x12a46ce074901bb2ec3136e73969ba388a925ace4891d853aa071cabaf4589ce"); + EXPECT_EQ(proof.theta, expected_theta); + + ASSERT_EQ(proof.lookup_permuted_commitments_vec.size(), 1); + EXPECT_TRUE(proof.lookup_permuted_commitments_vec[0].empty()); + + F expected_beta = F::FromHexString( + "0x1e2502cf4ba7d2e862c9432f546db6549f0073ff75bcce16ec6ba78c12a1d682"); + EXPECT_EQ(proof.beta, expected_beta); + + F expected_gamma = F::FromHexString( + "0x13ca5867ed47dd5ee525ced9e7c6c82907ee4b622d638bc5ec5c484e850d561b"); + EXPECT_EQ(proof.gamma, expected_gamma); + + std::vector> + expected_permutation_product_commitments_vec; + { + std::vector points = { + {"0x14a7961ab7d283804baf7e1ff56646335f9680eea7c812cb684833222737b3cc", + "0x1775df6698bfa7af48a83408e92cf0a132f20438bf9636100567960c6f26bb59"}, + {"0x1d7e50ab7eda75e3b98593a40d003f39385fc47518645b986b1c17f77ad17a04", + "0x00ef3b17b03c469c18380ab7acc3c5d5befd60a46a1659631d50fd480416cab6"}, + {"0x12564bae1b37a7b6a2dfedb0cfafb9f0d146c04e5e58fbe492faea900e2d9569", + "0x0cf1580112a8d2918afd4ebe3b021a2e3844712efb346c49029a077a67e06df1"}, + {"0x01fd0dd90e471e1998aff60c2f55ddf6af720e51d833218711a698e718347af2", + "0x2cc11fb912c0f0e8fcd677d966361edf4c005813981ed9c20fd10184835556f7"}, + }; + expected_permutation_product_commitments_vec.push_back( + CreateCommitments(points)); + } + EXPECT_EQ(proof.permutation_product_commitments_vec, + expected_permutation_product_commitments_vec); + + ASSERT_EQ(proof.lookup_product_commitments_vec.size(), 1); + EXPECT_TRUE(proof.lookup_product_commitments_vec[0].empty()); + + Commitment expected_vanishing_random_poly_commitment; + { + expected_vanishing_random_poly_commitment = CreateCommitment( + {"0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002"}); + } + EXPECT_EQ(proof.vanishing_random_poly_commitment, + expected_vanishing_random_poly_commitment); + + F expected_y = F::FromHexString( + "0x1af9ee1bca5f25fb7746430586cc6d4d4cc9152d5e3251e9c77fe38f6c9178b1"); + EXPECT_EQ(proof.y, expected_y); + + std::vector expected_vanishing_h_poly_commitments; + { + std::vector points = { + {"0x2157c5e9888d3770a99534af98e5256e87a8156013477212abaf2dcb61267cc1", + "0x2b04d3cec50250e35b5919da1cfe8142db0a636c0f29c6427fe713f52c1fff60"}, + {"0x1f74e7de13dea2916e273fdd6dc8fb6b439880af6f5a51f43c1519fff52df80c", + "0x1fd85823b8d30be9fdde823da1f8c4c876bb874a236050c3d04032999ac56eb7"}, + }; + expected_vanishing_h_poly_commitments = CreateCommitments(points); + } + EXPECT_EQ(proof.vanishing_h_poly_commitments, + expected_vanishing_h_poly_commitments); + + F expected_x = F::FromHexString( + "0x16b464904a5cd90ce14436a70fcf094ec288e227a6fe7ece36e9630b5a18d6cc"); + EXPECT_EQ(proof.x, expected_x); + + std::vector> expected_advice_evals_vec; + { + std::vector evals = { + "0x0fd4b015b11ef959233079e98f10bef33c040e7ef5ccd8f9f683a9b2fee2b012", + "0x260e3b04aed6cc00bbd93e69f3b7f6d7f6a65b7a6666522da2fafd80d4a08368", + "0x265bb162840ab1ccde0d30260800cfbcdcf1ce55fa517c9a3fe2f028f44f3580", + }; + expected_advice_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.advice_evals_vec, expected_advice_evals_vec); + + std::vector expected_fixed_evals; + { + std::vector evals = { + "0x1e9a88e6a09bf573ce0d7fc861b4167dfb50ec4b1832f14fa9ff5253592c5c5c", + "0x097dcd38fb5afd83cb1195a9f263c68437eafac885620d740849134827ad78ac", + }; + expected_fixed_evals = CreateEvals(evals); + } + EXPECT_EQ(proof.fixed_evals, expected_fixed_evals); + + F expected_vanishing_eval = F::FromHexString( + "0x0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(proof.vanishing_eval, expected_vanishing_eval); + + std::vector expected_common_permutation_evals; + { + std::vector evals = { + "0x15c11054033c46e5ee5f6bc5cb038704cef532efe2e3cff4ef8c89967b62db65", + "0x1a12c6731822c610798ebe652751d5c262079a488095f73a2783253d1b278dba", + "0x00fd80233e895f2b101e0dde2ecb5bd78b25fa02ecf21a500795e4714b217d90", + "0x13301192fc6453b2f3ee36ed72cbd03c39a803a270143d408e03b664ac3695fc", + }; + expected_common_permutation_evals = CreateEvals(evals); + } + EXPECT_EQ(proof.common_permutation_evals, expected_common_permutation_evals); + + std::vector> expected_permutation_product_evals_vec; + { + std::vector evals = { + "0x0a245aae04d97089c7fe28445e67474f614a28fe5da5ea34ef2dda9e7ef6f4f8", + "0x2342b1ef850f584d6d548ca636f8fda3eb1d15c6699261cf83a3d89bcef2119e", + "0x255dd3a02f026f1c8b7776507e67be273e1d3c58108d07eb150424d3faa98100", + "0x0070aea846a35c9b9fa355168344ed17ebca2505f15ab62e864bf8be7e2c8c64", + }; + expected_permutation_product_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.permutation_product_evals_vec, + expected_permutation_product_evals_vec); + + std::vector> expected_permutation_product_next_evals_vec; + { + std::vector evals = { + "0x00c4c6790adeaa88afdadecc5c98928a82459a5afc7ca2e96b236c7f32170aa0", + "0x1a0bf236d09bd1a4cc836651ae9f72edf824311f1a9259c9ee00cffc1b41c18d", + "0x03040a5c93ccbb8c10c5ebbaeff948e0f264a8c3b8a235e4284303d12a75c76a", + "0x1ea1e0eeda951f63822dcf70776d4125f1db75724a3565f3345146b3a97febbc", + }; + expected_permutation_product_next_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.permutation_product_next_evals_vec, + expected_permutation_product_next_evals_vec); + + std::vector>> + expected_permutation_product_last_evals_vec; + { + std::vector evals = { + "0x0d747ecac5b930e5571cf1ad86fe9811e2b7d254ad8520b484ecfca725c1e876", + "0x0ca359fb2e3a7b5845ddea1e8dce400cafb571641a5154c9cdfe7f4fa9ff5112", + "0x2b6ba6889d386cfef53497ecb09e6ded1ffedbb922dc69f32d47747e760c19c3", + "", + }; + expected_permutation_product_last_evals_vec.push_back( + CreateOptionalEvals(evals)); + } + EXPECT_EQ(proof.permutation_product_last_evals_vec, + expected_permutation_product_last_evals_vec); + + ASSERT_EQ(proof.lookup_product_evals_vec.size(), 1); + EXPECT_TRUE(proof.lookup_product_evals_vec[0].empty()); + + ASSERT_EQ(proof.lookup_product_next_evals_vec.size(), 1); + EXPECT_TRUE(proof.lookup_product_next_evals_vec[0].empty()); + + ASSERT_EQ(proof.lookup_permuted_input_evals_vec.size(), 1); + EXPECT_TRUE(proof.lookup_permuted_input_evals_vec[0].empty()); + + ASSERT_EQ(proof.lookup_permuted_input_inv_evals_vec.size(), 1); + EXPECT_TRUE(proof.lookup_permuted_input_inv_evals_vec[0].empty()); + + ASSERT_EQ(proof.lookup_permuted_table_evals_vec.size(), 1); + EXPECT_TRUE(proof.lookup_permuted_table_evals_vec[0].empty()); +} + +} // namespace tachyon::zk::halo2 diff --git a/tachyon/zk/plonk/circuit/examples/simple_lookup_circuit_unittest.cc b/tachyon/zk/plonk/circuit/examples/simple_lookup_circuit_unittest.cc index 2968dd745..ed7b090ae 100644 --- a/tachyon/zk/plonk/circuit/examples/simple_lookup_circuit_unittest.cc +++ b/tachyon/zk/plonk/circuit/examples/simple_lookup_circuit_unittest.cc @@ -8,12 +8,57 @@ #include "tachyon/zk/plonk/halo2/pinned_verifying_key.h" #include "tachyon/zk/plonk/keys/proving_key.h" -namespace tachyon::zk { +namespace tachyon::zk::halo2 { namespace { constexpr size_t kBits = 3; +constexpr uint8_t kExpectedProof[] = { + 47, 182, 87, 188, 243, 30, 63, 165, 203, 25, 53, 75, 159, 66, 202, + 142, 241, 131, 243, 14, 210, 25, 158, 80, 248, 54, 227, 231, 128, 14, + 188, 33, 135, 89, 21, 76, 201, 3, 235, 80, 0, 226, 252, 125, 123, + 57, 32, 116, 77, 132, 74, 28, 129, 87, 84, 88, 98, 197, 114, 110, + 128, 182, 153, 19, 181, 198, 105, 109, 162, 166, 183, 84, 67, 244, 177, + 201, 49, 196, 14, 0, 219, 248, 92, 5, 235, 57, 222, 34, 192, 113, + 234, 166, 141, 6, 20, 44, 163, 197, 58, 201, 108, 119, 98, 206, 182, + 224, 163, 217, 204, 19, 116, 126, 186, 254, 5, 145, 243, 143, 77, 148, + 12, 156, 51, 163, 253, 50, 169, 174, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 201, 198, 216, 151, + 252, 234, 109, 125, 227, 44, 215, 90, 19, 227, 211, 16, 132, 218, 47, + 20, 14, 103, 227, 190, 48, 56, 18, 141, 245, 147, 155, 111, 69, 183, + 230, 171, 201, 152, 96, 96, 50, 231, 189, 239, 146, 228, 77, 195, 44, + 115, 52, 100, 50, 26, 190, 251, 125, 237, 62, 51, 129, 206, 41, 213, + 73, 24, 105, 24, 52, 90, 88, 74, 250, 189, 72, 232, 182, 82, 66, + 236, 149, 14, 82, 230, 247, 77, 156, 203, 208, 59, 229, 232, 91, 174, + 16, 185, 190, 49, 196, 79, 222, 232, 164, 156, 122, 177, 104, 52, 223, + 34, 13, 85, 36, 146, 51, 96, 212, 188, 159, 26, 113, 139, 225, 166, + 134, 23, 32, 124, 225, 54, 77, 154, 74, 58, 114, 7, 152, 224, 110, + 164, 15, 197, 176, 210, 102, 181, 255, 137, 174, 192, 151, 54, 27, 201, + 157, 161, 51, 146, 0, 206, 113, 69, 204, 199, 117, 182, 68, 68, 29, + 252, 62, 114, 155, 151, 16, 34, 113, 23, 153, 119, 62, 100, 62, 166, + 56, 197, 14, 18, 113, 57, 38, 12, 174, 198, 230, 163, 123, 21, 249, + 53, 24, 174, 72, 9, 196, 231, 43, 117, 53, 121, 116, 113, 120, 244, + 212, 71, 250, 245, 200, 183, 177, 200, 46, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 233, 48, 131, + 22, 89, 250, 105, 203, 216, 104, 232, 153, 60, 228, 53, 103, 220, 115, + 5, 204, 55, 219, 211, 190, 108, 68, 54, 163, 239, 67, 27, 110, 95, + 81, 83, 107, 43, 251, 249, 233, 48, 243, 174, 235, 248, 127, 71, 163, + 105, 128, 254, 55, 87, 239, 219, 221, 85, 82, 48, 34, 172, 26, 19, + 44, 27, 229, 55, 53, 174, 87, 94, 5, 41, 104, 180, 183, 3, 108, + 207, 242, 98, 41, 2, 174, 175, 254, 144, 50, 246, 136, 187, 62, 224, + 212, 25, 16, 255, 92, 32, 104, 219, 15, 248, 16, 49, 250, 136, 75, + 83, 112, 23, 116, 159, 191, 103, 244, 34, 61, 35, 192, 60, 93, 212, + 133, 187, 243, 28, 111, 98, 50, 77, 146, 192, 142, 77, 44, 183, 174, + 206, 135, 161, 1, 134, 241, 9, 121, 230, 187, 75, 196, 164, 56, 0, + 174, 101, 190, 81, 190, 12, 74, 166, 34, 101, 37, 83, 187, 10, 167, + 168, 129, 126, 12, 132, 0, 130, 183, 115, 192, 14, 219, 226, 9, 51, + 208, 151, 250, 198, 0, 173, 91, 30, 189, 194, 22, 141, 13, 114, 113, + 162, 210, 171, 230, 111, 50, 248, 118, 206, 52, 121, 43, 116, 34, 115, + 77, 174, 137, 208, 21, 143, 232, 236, 188, 160}; + class SimpleLookupCircuitTest : public CircuitTest {}; } // namespace @@ -471,4 +516,200 @@ TEST_F(SimpleLookupCircuitTest, LoadProvingKey) { } } -} // namespace tachyon::zk +TEST_F(SimpleLookupCircuitTest, Verify) { + size_t n = 32; + CHECK(prover_->pcs().UnsafeSetup(n, F(2))); + prover_->set_domain(Domain::Create(n)); + + SimpleLookupCircuit circuit(4); + + VerifyingKey vkey; + ASSERT_TRUE(vkey.Load(prover_.get(), circuit)); + + std::vector owned_proof(std::begin(kExpectedProof), + std::end(kExpectedProof)); + std::unique_ptr> verifier = + CreateVerifier(CreateBufferWithProof(absl::MakeSpan(owned_proof))); + std::vector instance_columns; + std::vector> instance_columns_vec = { + std::move(instance_columns)}; + + Proof proof; + ASSERT_TRUE( + verifier->VerifyProofForTesting(vkey, instance_columns_vec, &proof)); + + std::vector> expected_advice_commitments_vec; + { + std::vector points = { + {"0x21bc0e80e7e336f8509e19d20ef383f18eca429f4b3519cba53f1ef3bc57b62f", + "0x20f77de309a3f81a09883e451c96e1b091f7fc7799e27905444c4759806fc6d2"}, + }; + expected_advice_commitments_vec.push_back(CreateCommitments(points)); + } + EXPECT_EQ(proof.advices_commitments_vec, expected_advice_commitments_vec); + + EXPECT_TRUE(proof.challenges.empty()); + + F expected_theta = F::FromHexString( + "0x290f20708b8b7e7985088e9221743049fba8e682c6d7488e5213494da7649505"); + EXPECT_EQ(proof.theta, expected_theta); + + std::vector>> + expected_lookup_permuted_commitments_vec; + { + std::vector input_points = { + {"0x1399b6806e72c562585457811c4a844d7420397b7dfce20050eb03c94c155987", + "0x0e2f049e4b617f6d07f7cfbd90d8cead3b4368a413f2c71cc4e73f1612c412f6"}, + }; + std::vector table_points = { + {"0x2c14068da6ea71c022de39eb055cf8db000ec431c9b1f44354b7a6a26d69c6b5", + "0x1bb517ae1ce20941980e210163591fc2b6c48892105d68b9ad8f1d48dd9aa59c"}, + }; + expected_lookup_permuted_commitments_vec.push_back( + CreateLookupPermutedCommitments(input_points, table_points)); + } + EXPECT_EQ(proof.lookup_permuted_commitments_vec, + expected_lookup_permuted_commitments_vec); + + F expected_beta = F::FromHexString( + "0x12183c0dc15c5e6071d6beecad709fa4fb11c14ad6089f1b39027775560e190c"); + EXPECT_EQ(proof.beta, expected_beta); + + F expected_gamma = F::FromHexString( + "0x1083f5b6c86390dfb35fbaeca4bf3fa53a05277508abc025bf7b05c4305b29a0"); + EXPECT_EQ(proof.gamma, expected_gamma); + + ASSERT_EQ(proof.permutation_product_commitments_vec.size(), 1); + EXPECT_TRUE(proof.permutation_product_commitments_vec[0].empty()); + + std::vector> expected_lookup_product_commitments_vec; + { + std::vector points = { + {"0x2ea932fda3339c0c944d8ff39105feba7e7413ccd9a3e0b6ce62776cc93ac5a3", + "0x1b0446e5fd9752be88503dba0cfde03e808ce6114ed5d2766996b0a3e5717671"}, + }; + expected_lookup_product_commitments_vec.push_back( + CreateCommitments(points)); + } + EXPECT_EQ(proof.lookup_product_commitments_vec, + expected_lookup_product_commitments_vec); + + Commitment expected_vanishing_random_poly_commitment; + { + expected_vanishing_random_poly_commitment = CreateCommitment( + {"0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002"}); + } + EXPECT_EQ(proof.vanishing_random_poly_commitment, + expected_vanishing_random_poly_commitment); + + F expected_y = F::FromHexString( + "0x0ed52b381813f71a5b8c931fd9696534b96d49f2d21fa71a73c0ed19c5843953"); + EXPECT_EQ(proof.y, expected_y); + + std::vector expected_vanishing_h_poly_commitments; + { + std::vector points = { + {"0x1b93f58d123830bee3670e142fda8410d3e3135ad72ce37d6deafc97d8c6c9fc", + "0x2f5101e174e83a1e04f79bf48381b7369c256ae64bc5fefd0d499c6b81711abf"}, + {"0x29ce81333eed7dfbbe1a326434732cc34de492efbde732606098c9abe6b7456f", + "0x2ad970331aee6de0a9464e6d4e2f928284c076c53b75f68280b9fe0c71590f9a"}, + {"0x10ae5be8e53bd0cb9c4df7e6520e95ec4252b6e848bdfa4a585a3418691849d5", + "0x227b2836dd30acb1eb885302cd2ccb7092bccfe4fffe155eaa64da910044c8d6"}, + {"0x201786a6e18b711a9fbcd460339224550d22df3468b17a9ca4e8de4fc431beb9", + "0x15aa249466f9008be03bbdaf7b79dc2dfcc3ba4aa6b7d3e6f7d8d172e4880e24"}, + }; + expected_vanishing_h_poly_commitments = CreateCommitments(points); + } + EXPECT_EQ(proof.vanishing_h_poly_commitments, + expected_vanishing_h_poly_commitments); + + F expected_x = F::FromHexString( + "0x053a454eca02741827b727febbe275db5436ebd46105f169c4c82fdc6386f6c0"); + EXPECT_EQ(proof.x, expected_x); + + std::vector> expected_advice_evals_vec; + { + std::vector evals = { + "0x009233a19dc91b3697c0ae89ffb566d2b0c50fa46ee09807723a4a9a4d36e17c", + }; + expected_advice_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.advice_evals_vec, expected_advice_evals_vec); + + std::vector expected_fixed_evals; + { + std::vector evals = { + "0x263971120ec538a63e643e779917712210979b723efc1d4444b675c7cc4571ce", + "0x2ec8b1b7c8f5fa47d4f47871747935752be7c40948ae1835f9157ba3e6c6ae0c", + }; + expected_fixed_evals = CreateEvals(evals); + } + EXPECT_EQ(proof.fixed_evals, expected_fixed_evals); + + F expected_vanishing_eval = F::FromHexString( + "0x0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(proof.vanishing_eval, expected_vanishing_eval); + + EXPECT_TRUE(proof.common_permutation_evals.empty()); + + ASSERT_EQ(proof.permutation_product_evals_vec.size(), 1); + EXPECT_TRUE(proof.permutation_product_evals_vec[0].empty()); + + ASSERT_EQ(proof.permutation_product_next_evals_vec.size(), 1); + EXPECT_TRUE(proof.permutation_product_next_evals_vec[0].empty()); + + ASSERT_EQ(proof.permutation_product_last_evals_vec.size(), 1); + EXPECT_TRUE(proof.permutation_product_last_evals_vec[0].empty()); + + std::vector> expected_lookup_product_evals_vec; + { + std::vector evals = { + "0x1b43efa336446cbed3db37cc0573dc6735e43c99e868d8cb69fa59168330e94a", + }; + expected_lookup_product_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.lookup_product_evals_vec, expected_lookup_product_evals_vec); + + std::vector> expected_lookup_product_next_evals_vec; + { + std::vector evals = { + "0x131aac22305255dddbef5737fe8069a3477ff8ebaef330e9f9fb2b6b53515f6e", + }; + expected_lookup_product_next_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.lookup_product_next_evals_vec, + expected_lookup_product_next_evals_vec); + + std::vector> expected_lookup_permuted_input_evals_vec; + { + std::vector evals = { + "0x19d4e03ebb88f63290feafae022962f2cf6c03b7b46829055e57ae3537e51b2c", + }; + expected_lookup_permuted_input_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.lookup_permuted_input_evals_vec, + expected_lookup_permuted_input_evals_vec); + + std::vector> expected_lookup_permuted_input_inv_evals_vec; + { + std::vector evals = { + "0x1cf3bb85d45d3cc0233d22f467bf9f741770534b88fa3110f80fdb68205cff10", + }; + expected_lookup_permuted_input_inv_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.lookup_permuted_input_inv_evals_vec, + expected_lookup_permuted_input_inv_evals_vec); + + std::vector> expected_lookup_permuted_table_evals_vec; + { + std::vector evals = { + "0x0cbe51be65ae0038a4c44bbbe67909f18601a187ceaeb72c4d8ec0924d32626f", + }; + expected_lookup_permuted_table_evals_vec.push_back(CreateEvals(evals)); + } + EXPECT_EQ(proof.lookup_permuted_table_evals_vec, + expected_lookup_permuted_table_evals_vec); +} + +} // namespace tachyon::zk::halo2