diff --git a/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.hpp b/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.hpp index 438f0cf3a..1b1698860 100644 --- a/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.hpp +++ b/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.hpp @@ -72,7 +72,8 @@ class variable_or_identity : public gadget> pb_variable is_identity_var; }; -/// Selector gadget for variable_or_identity +/// Selector gadget for variable_or_identity. Outputs one of two +/// variable_or_identity objects, depending on a scalar parameter. template< typename ppT, typename groupT, @@ -401,6 +402,51 @@ class point_mul_by_scalar_gadget : public gadget protoboard &pb, const std::string &annotation_prefix); }; +/// Generic gadget to perform scalar multiplication of variable_or_identity +/// group points by scalar variables. Used by the individual group element +/// implementations. +template< + typename ppT, + typename groupT, + typename groupVarT, + typename selectorGadgetT, + typename addGadgetT, + typename dblGadgetT> +class point_variable_or_identity_mul_by_scalar_gadget + : public gadget +{ +public: + using Field = libff::Fr; + using nFr = libff::Fr>; + + using varMulByScalar = point_mul_by_scalar_gadget< + ppT, + groupT, + groupVarT, + selectorGadgetT, + addGadgetT, + dblGadgetT>; + + using groupVarOrIdentity = variable_or_identity; + using selectVarIdentityGadget = + variable_or_identity_selector; + + varMulByScalar scalar_mul; + groupVarOrIdentity selected_result; + selectVarIdentityGadget select_result; + + point_variable_or_identity_mul_by_scalar_gadget( + protoboard &pb, + const pb_linear_combination &scalar, + const groupVarOrIdentity &P, + const groupVarOrIdentity &result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + const groupVarOrIdentity &result() const; +}; + } // namespace libsnark #include "libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.tcc" diff --git a/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.tcc b/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.tcc index 0e34c1308..97d6b848e 100644 --- a/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.tcc +++ b/libsnark/gadgetlib1/gadgets/curves/scalar_multiplication.tcc @@ -821,6 +821,103 @@ pb_variable_array> point_mul_by_scalar_gadget< return bits; } +template< + typename ppT, + typename groupT, + typename groupVarT, + typename selectorGadgetT, + typename addGadgetT, + typename dblGadgetT> +point_variable_or_identity_mul_by_scalar_gadget< + ppT, + groupT, + groupVarT, + selectorGadgetT, + addGadgetT, + dblGadgetT>:: + point_variable_or_identity_mul_by_scalar_gadget( + protoboard &pb, + const pb_linear_combination &scalar, + const groupVarOrIdentity &P, + const groupVarOrIdentity &result, + const std::string &annotation_prefix) + : gadget>(pb, annotation_prefix) + , scalar_mul( + pb, + scalar, + P.value, + groupVarOrIdentity(pb, FMT(annotation_prefix, " scalar_mul_result")), + FMT(annotation_prefix, " scalar_mul")) + // result = P.is_identity ? P : scalar_mul_result + // = select(P.is_identity, scalar_mul_result, P) + , selected_result(result) + , select_result( + pb, + P.is_identity, + scalar_mul.result(), + P, + selected_result, + FMT(annotation_prefix, " select_result")) +{ +} + +template< + typename ppT, + typename groupT, + typename groupVarT, + typename selectorGadgetT, + typename addGadgetT, + typename dblGadgetT> +void point_variable_or_identity_mul_by_scalar_gadget< + ppT, + groupT, + groupVarT, + selectorGadgetT, + addGadgetT, + dblGadgetT>::generate_r1cs_constraints() +{ + scalar_mul.generate_r1cs_constraints(); + select_result.generate_r1cs_constraints(); +} + +template< + typename ppT, + typename groupT, + typename groupVarT, + typename selectorGadgetT, + typename addGadgetT, + typename dblGadgetT> +void point_variable_or_identity_mul_by_scalar_gadget< + ppT, + groupT, + groupVarT, + selectorGadgetT, + addGadgetT, + dblGadgetT>::generate_r1cs_witness() +{ + scalar_mul.generate_r1cs_witness(); + select_result.generate_r1cs_witness(); +} + +template< + typename ppT, + typename groupT, + typename groupVarT, + typename selectorGadgetT, + typename addGadgetT, + typename dblGadgetT> +const variable_or_identity + &point_variable_or_identity_mul_by_scalar_gadget< + ppT, + groupT, + groupVarT, + selectorGadgetT, + addGadgetT, + dblGadgetT>::result() const +{ + return selected_result; +} + } // namespace libsnark #endif // LIBSNARK_GADGETLIB1_GADGETS_CURVE_SCALAR_MULTIPLICATION_TCC_ diff --git a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g1_gadget.hpp b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g1_gadget.hpp index 87f0b6f69..d70dcc402 100644 --- a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g1_gadget.hpp +++ b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g1_gadget.hpp @@ -254,6 +254,16 @@ using G1_mul_by_scalar_gadget = point_mul_by_scalar_gadget< G1_add_gadget, G1_dbl_gadget>; +template +using G1_variable_or_identity_mul_by_scalar_gadget = + point_variable_or_identity_mul_by_scalar_gadget< + wppT, + libff::G1>, + G1_variable, + G1_variable_selector_gadget, + G1_add_gadget, + G1_dbl_gadget>; + } // namespace libsnark #include diff --git a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp index 47112130a..9fb51698b 100644 --- a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp +++ b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp @@ -292,6 +292,16 @@ using G2_mul_by_scalar_gadget = point_mul_by_scalar_gadget< G2_add_gadget, G2_dbl_gadget>; +template +using G2_variable_or_identity_mul_by_scalar_gadget = + point_variable_or_identity_mul_by_scalar_gadget< + wppT, + libff::G2>, + G2_variable, + G2_variable_selector_gadget, + G2_add_gadget, + G2_dbl_gadget>; + } // namespace libsnark #include diff --git a/libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.hpp b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.hpp new file mode 100644 index 000000000..e4ce96812 --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.hpp @@ -0,0 +1,244 @@ +/** @file + ***************************************************************************** + * @author This file is part of libff, developed by Clearmatics Ltd + * (originally developed by SCIPR Lab) and contributors + * (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef LIBSNARK_GADGETLIB1_GADGETS_VERIFIERS_KZG10_BATCHED_VERIFIER_GADGET_HPP_ +#define LIBSNARK_GADGETLIB1_GADGETS_VERIFIERS_KZG10_BATCHED_VERIFIER_GADGET_HPP_ + +#include "libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp" +#include "libsnark/polynomial_commitments/kzg10_batched.hpp" + +namespace libsnark +{ + +// TODO: create an "evaluations variable object" + +template class kzg10_batched_witness_variable +{ +public: + kzg10_witness_variable W_1; + kzg10_witness_variable W_2; + + kzg10_batched_witness_variable( + protoboard> &pb, const std::string &annotation_prefix); + + void generate_r1cs_witness( + const typename kzg10_batched_2_point< + other_curve>::evaluation_witness &eval_witness); +}; + +/// Given a value `gamma` and a vector `points` of `n` group variables, compute: +/// +/// result = \sum_{i=0}^{n-1} gamma^i * points[n] +/// +/// by computing: +/// +/// intermediate[0] = gamma * points[n] + points[n-1] +/// intermediate[i>0] = gamma * intermediate[i-1] + points[n-1-i] +/// result = gamma * intermediate[n-2] + points[0] +template +class kzg10_batched_compute_gamma_powers_times_points : gadget> +{ +public: + // Full calculation is as follows: + // + // intermediate_mul[0] = gamma * points[n-1] + // intermediate_sum[0] = intermediate_mul[0] + points[n-2] + // intermediate_mul[i=1..n-2] = gamma * intermediate_sum[i-1] + // intermediate_sum[i=1..n-3] = intermediate_mul[i] + points[n-2-i] + // intermediate_sum[n-2] = result = intermediate_mul[n-2] + points[0] + // + // so intermediate_mul.size() = intermediate_sum.size() = n-1 + std::vector> intermediate_mul; + std::vector> + intermediate_sum; + + kzg10_batched_compute_gamma_powers_times_points( + protoboard> &pb, + const pb_linear_combination> &gamma, + const std::vector> &points, + const G1_variable &result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + + const G1_variable &result() const; +}; + +/// Given an array of commitments, and array of evaluations, and some field +/// element gamma, compute terms of the form: +/// +/// \sum_{i=1}^{t_1} \gamma^{i-1} (cm_i - [s_i]_1) +/// +/// The `num_entries` parameter here is intended to reflect the fact that this +/// must be statically defined, although all internal structures are currently +/// dynamic. This also allows specialization for the case of 2 entries. +template +class kzg10_batched_compute_gamma_powers_commit_minus_eval_sum + : gadget> +{ + static_assert(num_entries > 2, "num_entries must be greater that 2"); + +public: + using Field = libff::Fr; + + // These are the negative encoded evaluations: + // + // encoded_evals[i] = G1_mul(evals[i], -G1::one()); + std::vector> encoded_evals; + std::vector> compute_encoded_evals; + + // Negative evals are added to commits to compute cm_i - [s_i]_1: + // + // commit_minus_encoded_eval[i] = cm_i - [s_i]_1 + std::vector> commit_minus_encoded_eval; + std::vector> + compute_commit_minus_encoded_eval; + + // result = sum_{i=0}^{n-1} gamma^i commit_minus_encoded_eval[i] + kzg10_batched_compute_gamma_powers_times_points + compute_gamma_power_times_commit_minus_encoded_eval; + + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum( + protoboard> &pb, + const pb_linear_combination> &gamma, + const std::vector> &commits, + const pb_linear_combination_array> &evals, + const G1_variable &result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + + const G1_variable &result() const; +}; + +// Specialization for num_entries == 2 (in which we do not need to compute +// further powers of gamma). This simplifies the generic (num_entries >= 3) +// version, since it does not need to account for special cases. +template +class kzg10_batched_compute_gamma_powers_commit_minus_eval_sum + : gadget> +{ +public: + // encoded_evals[i] = evals[i] * -G1::one() + std::vector> compute_encoded_evals; + + // cm_minus_encoded_eval[i] = commits[i] - encoded_evals[i] + std::vector> + compute_cm_minus_eval; + + // gamma_term = gamma * commit_minus_encoded_eval[1] + // return = gamma_term + (commit_minus_encoded_eval[0] + std::shared_ptr> compute_gamma_term; + std::shared_ptr> + compute_result; + + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum( + protoboard> &pb, + const pb_linear_combination> &gamma, + const std::vector> &commitments, + const pb_linear_combination_array> &evals, + const G1_variable &result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + + const G1_variable &result() const; +}; + +/// Gadget version of the native KZG10 batched verifier in +/// libsnark/polynomial_commitments/kzg10_batched.hpp. +/// +/// Each polynomial in a batch can be evaluated at 1 of 2 points. +/// `polyomials_1` determines the number of polynomials evaluated at the first +/// point `z_1`, and `polynomials_2` determines the number to be evaluated at +/// the second point `z_2`. Hence these also determine the number of +/// commitments and evaluation points. +/// +/// The number of polynomials in each group is intentionally a template +/// parameter, reflecting the fact that it must be statically defined for a +/// given circuit. +template +class kzg10_batched_verifier_gadget : public gadget> +{ +public: + using Field = libff::Fr; + + // Matching the native calculations in kzg10_batched.tcc, compute: + // + // F = \sum_{i=1}^{t1} \gamma_1^{i-1} (cm_1[i] - [s_1[i]]_1) + + // r \sum_{i=1}^{t2} \gamma_2^{i-1} (cm_2[i] - [s_2[i]]_1) + // = G + r * H + // + // where: + // + // G = \sum_{i=1}^{t1} \gamma_1^{i-1} (cm_1[i] - [s_1[i]]_1) + // H = \sum_{i=1}^{t2} \gamma_2^{i-1} (cm_2[i] - [s_2[i]]_1) + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum< + ppT, + num_polyomials_1> + compute_G; + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum< + ppT, + num_polyomials_2> + compute_H; + G1_mul_by_scalar_gadget compute_rH; + G1_add_variable_and_variable_or_identity_gadget compute_F; + + // Expression to check is: + // e(W_1 + r * W_2, srs.alpha_g2) * + // e(F + z_1 * W_1 + r * z_2 * W_2, -[1]_2) + // = 1 + // = e(A, srs.alpha_g2) * e(B, -[1]_2) + // + // where + // A = W_1 + r * W_2 + // B = F + z_1 * W_1 + r * z_2 * W_2 + + G1_mul_by_scalar_gadget compute_r_times_W_2; + G1_add_variable_and_variable_or_identity_gadget compute_A; + G1_variable_or_identity_mul_by_scalar_gadget + compute_r_times_z_2_times_W_2; + G1_mul_by_scalar_gadget compute_z_1_times_W_1; + G1_add_variable_and_variable_or_identity_gadget + compute_F_plus_z_1_times_W_1; + G1_add_variable_and_variable_or_identity_gadget compute_B; + + kzg10_pairing_check_gadget pairing_check; + + // TODO: Since polyomials_1 and polyomials_2 are statically defined, we + // could use statically sized containers here. For now, the interfaces and + // initialization make this a bit inconvenient (requiring default + // constructors for the contained types). + kzg10_batched_verifier_gadget( + protoboard> &pb, + pb_linear_combination> z_1, + pb_linear_combination> z_2, + const pb_linear_combination_array> &poly_evals_1, + const pb_linear_combination_array> &poly_evals_2, + const kzg10_srs_variable &srs, + pb_linear_combination> gamma_1, + pb_linear_combination> gamma_2, + const kzg10_batched_witness_variable &eval_witness, + const std::vector> &commitments_1, + const std::vector> &commitments_2, + pb_linear_combination> r, + pb_variable> result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +} // namespace libsnark + +#include "libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.tcc" + +#endif // LIBSNARK_GADGETLIB1_GADGETS_VERIFIERS_KZG10_BATCHED_VERIFIER_GADGET_HPP_ diff --git a/libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.tcc b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.tcc new file mode 100644 index 000000000..0a45b5515 --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.tcc @@ -0,0 +1,491 @@ +/** @file + ***************************************************************************** + * @author This file is part of libff, developed by Clearmatics Ltd + * (originally developed by SCIPR Lab) and contributors + * (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef LIBSNARK_GADGETLIB1_GADGETS_VERIFIERS_KZG10_BATCHED_VERIFIER_GADGET_TCC_ +#define LIBSNARK_GADGETLIB1_GADGETS_VERIFIERS_KZG10_BATCHED_VERIFIER_GADGET_TCC_ + +#include "libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp" + +namespace libsnark +{ + +namespace internal +{ + +// TODO: It may make sense to expose this more widely if we start to create +// other gadgets which allocate and process arrays of curve points, etc. We may +// also make this more generic and add constructor args etc. + +// Convenience function to allocate vectors of complex variables. +template +std::vector allocate_variable_array( + protoboard> &pb, + const size_t length, + const std::string &annotation_prefix) +{ + std::vector variables; + variables.reserve(length); + for (size_t i = 0; i < length; ++i) { + variables.emplace_back(pb, FMT(annotation_prefix, "[%zu]", i)); + } + + return variables; +} + +} // namespace internal + +// +// kzg10_batched_witness_variable +// + +template +kzg10_batched_witness_variable::kzg10_batched_witness_variable( + protoboard> &pb, const std::string &annotation_prefix) + : W_1(pb, FMT(annotation_prefix, " W_1")) + , W_2(pb, FMT(annotation_prefix, " W_2")) +{ +} + +template +void kzg10_batched_witness_variable::generate_r1cs_witness( + const typename kzg10_batched_2_point>::evaluation_witness + &eval_witness) +{ + W_1.generate_r1cs_witness(eval_witness.W_1); + W_2.generate_r1cs_witness(eval_witness.W_2); +} + +// +// kzg10_batched_compute_gamma_powers_times_points +// + +template +kzg10_batched_compute_gamma_powers_times_points:: + kzg10_batched_compute_gamma_powers_times_points( + protoboard> &pb, + const pb_linear_combination> &gamma, + const std::vector> &points, + const G1_variable &result, + const std::string &annotation_prefix) + : gadget>(pb, annotation_prefix) +{ + intermediate_mul.reserve(n - 1); + intermediate_sum.reserve(n - 1); + + // intermediate_mul_result[0] = gamma * points[n-1] + intermediate_mul.emplace_back( + pb, + gamma, + points[n - 1], + G1_variable_or_identity( + pb, FMT(annotation_prefix, " intermediate_mul_result[0]")), + FMT(annotation_prefix, " intermediate_mul[0]")); + + // intermediate_sum[i=0..n-3] = intermediate_mul[i] + points[n-2-i] + // intermediate_mul[i=1..n-2] = gamma * intermediate_sum[i-1] + for (size_t i = 1; i < n - 1; ++i) { + intermediate_sum.emplace_back( + pb, + intermediate_mul.back().result(), + points[n - 1 - i], + G1_variable( + pb, + FMT(annotation_prefix, " intermediate_sum_result[%zu]", i - 1)), + FMT(annotation_prefix, " intermediate_sum[%zu]", i - 1)); + + intermediate_mul.emplace_back( + pb, + gamma, + intermediate_sum.back().result, + G1_variable_or_identity( + pb, FMT(annotation_prefix, " intermediate_mul_result[%zu]", i)), + FMT(annotation_prefix, " intermediate_mul[%zu]", i)); + } + + // intermediate_sum[n-2] = result = intermediate_mul[n-2] + points[0] + intermediate_sum.emplace_back( + pb, + intermediate_mul[n - 2].result(), + points[0], + result, + FMT(annotation_prefix, " intermediate_sum[%zu]", n - 2)); + + assert(intermediate_mul.size() == n - 1); + assert(intermediate_sum.size() == n - 1); +} + +template +void kzg10_batched_compute_gamma_powers_times_points:: + generate_r1cs_constraints() +{ + assert(intermediate_mul.size() == n - 1); + assert(intermediate_sum.size() == n - 1); + + for (size_t i = 0; i < n - 1; ++i) { + intermediate_mul[i].generate_r1cs_constraints(); + intermediate_sum[i].generate_r1cs_constraints(); + } +} + +template +void kzg10_batched_compute_gamma_powers_times_points:: + generate_r1cs_witness() +{ + assert(intermediate_mul.size() == n - 1); + assert(intermediate_sum.size() == n - 1); + + for (size_t i = 0; i < n - 1; ++i) { + intermediate_mul[i].generate_r1cs_witness(); + intermediate_sum[i].generate_r1cs_witness(); + } +} + +template +const G1_variable + &kzg10_batched_compute_gamma_powers_times_points::result() const +{ + return intermediate_sum.back().result; +} + +// +// kzg10_batched_compute_gamma_powers_commit_minus_eval_sum +// + +// specialization for >2 entries +template +kzg10_batched_compute_gamma_powers_commit_minus_eval_sum:: + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum( + protoboard> &pb, + const pb_linear_combination> &gamma, + const std::vector> &commits, + const pb_linear_combination_array> &evals, + const G1_variable &result, + const std::string &annotation_prefix) + : gadget>(pb, annotation_prefix) + , encoded_evals( + internal::allocate_variable_array>( + pb, num_entries, FMT(annotation_prefix, " encoded_evals"))) + , compute_encoded_evals() + , commit_minus_encoded_eval( + internal::allocate_variable_array>( + pb, + num_entries, + FMT(annotation_prefix, " commit_minus_encoded_eval"))) + , compute_commit_minus_encoded_eval() + , compute_gamma_power_times_commit_minus_encoded_eval( + pb, + gamma, + commit_minus_encoded_eval, + result, + FMT(annotation_prefix, + " compute_gamma_power_times_commit_minus_encoded_eval")) +{ + // encoded_eval[i] = G1_mul_by_scalar(evals[i], -G1::one()) + // len(encoded_eval) = num_entries + G1_variable g1_minus_one( + pb, + -libff::G1>::one(), + FMT(annotation_prefix, " g1_one")); + compute_encoded_evals.reserve(num_entries); + for (size_t i = 0; i < num_entries; ++i) { + compute_encoded_evals.emplace_back( + pb, + evals[i], + g1_minus_one, + encoded_evals[i], + FMT(annotation_prefix, " compute_encoded_evals[%zu]", i)); + } + + // commit_minus_encoded_eval[i] = cm_i - [s_i]_1 + compute_commit_minus_encoded_eval.reserve(num_entries); + for (size_t i = 0; i < num_entries; ++i) { + compute_commit_minus_encoded_eval.emplace_back( + pb, + encoded_evals[i], + commits[i], + commit_minus_encoded_eval[i], + FMT(annotation_prefix, " compute_commit_minus_encoded_eval")); + } +} + +template +void kzg10_batched_compute_gamma_powers_commit_minus_eval_sum< + ppT, + num_entries>::generate_r1cs_constraints() +{ + for (auto &gadget : compute_encoded_evals) { + gadget.generate_r1cs_constraints(); + } + + for (auto &gadget : compute_commit_minus_encoded_eval) { + gadget.generate_r1cs_constraints(); + } + + compute_gamma_power_times_commit_minus_encoded_eval + .generate_r1cs_constraints(); +} + +template +void kzg10_batched_compute_gamma_powers_commit_minus_eval_sum< + ppT, + num_entries>::generate_r1cs_witness() +{ + for (auto &gadget : compute_encoded_evals) { + gadget.generate_r1cs_witness(); + } + + for (auto &gadget : compute_commit_minus_encoded_eval) { + gadget.generate_r1cs_witness(); + } + + compute_gamma_power_times_commit_minus_encoded_eval.generate_r1cs_witness(); +} + +template +const G1_variable + &kzg10_batched_compute_gamma_powers_commit_minus_eval_sum< + ppT, + num_entries>::result() const +{ + return compute_gamma_power_times_commit_minus_encoded_eval.result(); +} + +// specialization for 2 entries +template +kzg10_batched_compute_gamma_powers_commit_minus_eval_sum:: + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum( + protoboard> &pb, + const pb_linear_combination> &gamma, + const std::vector> &commits, + const pb_linear_combination_array> &evals, + const G1_variable &result, + const std::string &annotation_prefix) + : gadget>(pb, annotation_prefix) +{ + G1_variable g1_minus_1( + pb, + -libff::G1>::one(), + FMT(annotation_prefix, " g1_minus_1")); + + // encoded_evals[i] = evals[i] * -G1::one() + compute_encoded_evals.emplace_back( + pb, + evals[0], + g1_minus_1, + G1_variable_or_identity( + pb, FMT(annotation_prefix, " encoded_evals[0]")), + FMT(annotation_prefix, " compute_encoded_evals[0]")); + compute_encoded_evals.emplace_back( + pb, + evals[1], + g1_minus_1, + G1_variable_or_identity( + pb, FMT(annotation_prefix, " encoded_evals[1]")), + FMT(annotation_prefix, " compute_encoded_evals[1]")); + + // cm_minus_encoded_eval[i] = commits[i] - encoded_evals[i] + compute_cm_minus_eval.emplace_back( + pb, + compute_encoded_evals[0].result(), + commits[0], + G1_variable(pb, FMT(annotation_prefix, " cm_minus_eval[0]")), + FMT(annotation_prefix, " compute_cm_minus_eval[0]")); + compute_cm_minus_eval.emplace_back( + pb, + compute_encoded_evals[1].result(), + commits[1], + G1_variable(pb, FMT(annotation_prefix, " cm_minus_eval[1]")), + FMT(annotation_prefix, " compute_cm_minus_eval[1]")); + + // gamma_term = gamma * commit_minus_encoded_eval[1] + // return = gamma_term + (commit_minus_encoded_eval[0] + compute_gamma_term = std::make_shared>( + pb, + gamma, + compute_cm_minus_eval[1].result, + G1_variable_or_identity(pb, FMT(annotation_prefix, " gamma_term")), + FMT(annotation_prefix, " compute_gamma_term")); + + compute_result = + std::make_shared>( + pb, + compute_gamma_term->result(), + compute_cm_minus_eval[0].result, + result, + FMT(annotation_prefix, " compute_result")); +} + +template +void kzg10_batched_compute_gamma_powers_commit_minus_eval_sum:: + generate_r1cs_constraints() +{ + compute_encoded_evals[0].generate_r1cs_constraints(); + compute_encoded_evals[1].generate_r1cs_constraints(); + compute_cm_minus_eval[0].generate_r1cs_constraints(); + compute_cm_minus_eval[1].generate_r1cs_constraints(); + compute_gamma_term->generate_r1cs_constraints(); + compute_result->generate_r1cs_constraints(); +} + +template +void kzg10_batched_compute_gamma_powers_commit_minus_eval_sum:: + generate_r1cs_witness() +{ + compute_encoded_evals[0].generate_r1cs_witness(); + compute_encoded_evals[1].generate_r1cs_witness(); + compute_cm_minus_eval[0].generate_r1cs_witness(); + compute_cm_minus_eval[1].generate_r1cs_witness(); + compute_gamma_term->generate_r1cs_witness(); + compute_result->generate_r1cs_witness(); +} + +template +const G1_variable + &kzg10_batched_compute_gamma_powers_commit_minus_eval_sum::result() + const +{ + return compute_result->result; +} + +// +// kzg10_batched_verifier_gadget +// + +template +kzg10_batched_verifier_gadget:: + kzg10_batched_verifier_gadget( + protoboard> &pb, + pb_linear_combination> z_1, + pb_linear_combination> z_2, + const pb_linear_combination_array> &poly_evals_1, + const pb_linear_combination_array> &poly_evals_2, + const kzg10_srs_variable &srs, + pb_linear_combination> gamma_1, + pb_linear_combination> gamma_2, + const kzg10_batched_witness_variable &eval_witness, + const std::vector> &commitments_1, + const std::vector> &commitments_2, + pb_linear_combination> r, + pb_variable> result, + const std::string &annotation_prefix) + : gadget>(pb, annotation_prefix) + , compute_G( + pb, + gamma_1, + commitments_1, + poly_evals_1, + G1_variable(pb, FMT(annotation_prefix, " G")), + FMT(annotation_prefix, " compute_G")) + , compute_H( + pb, + gamma_2, + commitments_2, + poly_evals_2, + G1_variable(pb, FMT(annotation_prefix, " H")), + FMT(annotation_prefix, " compute_H")) + , compute_rH( + pb, + r, + compute_H.result(), + G1_variable_or_identity(pb, FMT(annotation_prefix, " rH")), + FMT(annotation_prefix, " compute_rH")) + , compute_F( + pb, + compute_rH.result(), + compute_G.result(), + G1_variable(pb, FMT(annotation_prefix, " F")), + FMT(annotation_prefix, " compute_F")) + // A = W_1 + r * W_2 + , compute_r_times_W_2( + pb, + r, + eval_witness.W_2, + G1_variable_or_identity( + pb, FMT(annotation_prefix, " r_times_W_2")), + FMT(annotation_prefix, " compute_r_times_W_2")) + , compute_A( + pb, + compute_r_times_W_2.result(), + eval_witness.W_1, + G1_variable(pb, FMT(annotation_prefix, " A")), + FMT(annotation_prefix, " compute_A")) + // B = F + z_1 * W_1 + r * z_2 * W_2 + , compute_r_times_z_2_times_W_2( + pb, + z_2, + compute_r_times_W_2.result(), + G1_variable_or_identity( + pb, FMT(annotation_prefix, " r_times_z_2_times_W_2")), + FMT(annotation_prefix, " compute_r_times_z_2_times_W_2")) + , compute_z_1_times_W_1( + pb, + z_1, + eval_witness.W_1, + G1_variable_or_identity( + pb, FMT(annotation_prefix, " z_1_times_W_1")), + FMT(annotation_prefix, " compute_z_1_times_W_1")) + , compute_F_plus_z_1_times_W_1( + pb, + compute_z_1_times_W_1.result(), + compute_F.result, + G1_variable(pb, FMT(annotation_prefix, " F_plus_z_1_times_W_1")), + FMT(annotation_prefix, " compute_F_plus_z_1_times_W_1")) + , compute_B( + pb, + compute_r_times_z_2_times_W_2.result(), + compute_F_plus_z_1_times_W_1.result, + G1_variable(pb, FMT(annotation_prefix, " B")), + FMT(annotation_prefix, " compute_B")) + , pairing_check( + pb, + compute_A.result, + srs.alpha_g2, + compute_B.result, + libff::G2>::one(), + result, + FMT(annotation_prefix, " pairing_check")) +{ +} + +template +void kzg10_batched_verifier_gadget:: + generate_r1cs_constraints() +{ + compute_G.generate_r1cs_constraints(); + compute_H.generate_r1cs_constraints(); + compute_rH.generate_r1cs_constraints(); + compute_F.generate_r1cs_constraints(); + compute_r_times_W_2.generate_r1cs_constraints(); + compute_A.generate_r1cs_constraints(); + compute_r_times_z_2_times_W_2.generate_r1cs_constraints(); + compute_z_1_times_W_1.generate_r1cs_constraints(); + compute_F_plus_z_1_times_W_1.generate_r1cs_constraints(); + compute_B.generate_r1cs_constraints(); + pairing_check.generate_r1cs_constraints(); +} + +template +void kzg10_batched_verifier_gadget:: + generate_r1cs_witness() +{ + compute_G.generate_r1cs_witness(); + compute_H.generate_r1cs_witness(); + compute_rH.generate_r1cs_witness(); + compute_F.generate_r1cs_witness(); + compute_r_times_W_2.generate_r1cs_witness(); + compute_A.generate_r1cs_witness(); + compute_r_times_z_2_times_W_2.generate_r1cs_witness(); + compute_z_1_times_W_1.generate_r1cs_witness(); + compute_F_plus_z_1_times_W_1.generate_r1cs_witness(); + compute_B.generate_r1cs_witness(); + pairing_check.generate_r1cs_witness(); +} + +} // namespace libsnark + +#endif // LIBSNARK_GADGETLIB1_GADGETS_VERIFIERS_KZG10_BATCHED_VERIFIER_GADGET_TCC_ diff --git a/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp index 3b1200364..9be1f0007 100644 --- a/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp +++ b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp @@ -52,6 +52,41 @@ template using kzg10_commitment_variable = G1_variable; /// This is also a single G1_variable (see the native implementation). template using kzg10_witness_variable = G1_variable; +/// A pairing check specific to the KZG10 scheme. +/// Check: +/// +/// e(A, B) = e(C, D) +/// +/// where D is a fixed constant (and thereby some the precompute step for D is +/// baked into the circuit). +template class kzg10_pairing_check_gadget : gadget> +{ +public: + G1_precomputation A_precomp; + precompute_G1_gadget compute_A_precomp; + G2_precomputation B_precomp; + precompute_G2_gadget compute_B_precomp; + G1_precomputation C_precomp; + precompute_G1_gadget compute_C_precomp; + // D_precomp is statically computed from a constant, so does not need a + // precompute gadget. + G2_precomputation D_precomp; + + check_e_equals_e_gadget check_pairing_equality; + + kzg10_pairing_check_gadget( + protoboard> &pb, + const G1_variable &A, + const G2_variable &B, + const G1_variable &C, + const libff::G2> &D, + pb_variable> &result, + const std::string annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + /// Uses a nested pairing (via a pairing selector) to implement the /// verification step of [KZG10]. See the native implementation for details. /// @@ -94,19 +129,9 @@ class kzg10_verifier_gadget : public gadget> G1_variable C; G1_add_gadget compute_C; - // Pairing computation - G1_precomputation A_precomp; - precompute_G1_gadget compute_A_precomp; - G2_precomputation B_precomp; - precompute_G2_gadget compute_B_precomp; - G1_precomputation C_precomp; - precompute_G1_gadget compute_C_precomp; - // D_precomp is computed from (constant) G2::one(), and baked into the - // circuit, saving a few constraints. - G2_precomputation D_precomp; - + // Pairing check pb_variable> check_result; - check_e_equals_e_gadget check_pairing_equality; + kzg10_pairing_check_gadget pairing_check; // group_elements_non_zero = // (1 - i_in_G2.is_zero) * (1 - poly_eval_in_G1.is_zero) @@ -118,7 +143,7 @@ class kzg10_verifier_gadget : public gadget> kzg10_verifier_gadget( protoboard> &pb, const kzg10_srs_variable &srs, - const kzg10_commitment_variable &commitmennt, + const kzg10_commitment_variable &commitment, pb_linear_combination> i, pb_linear_combination> poly_eval, const kzg10_witness_variable &witness, diff --git a/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.tcc b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.tcc index 61bed590e..686b0c318 100644 --- a/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.tcc +++ b/libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.tcc @@ -14,6 +14,8 @@ namespace libsnark { +// kzg10_srs_variable + template kzg10_srs_variable::kzg10_srs_variable( protoboard> &pb, @@ -41,6 +43,60 @@ void kzg10_srs_variable::generate_r1cs_witness( alpha_g2.generate_r1cs_witness(srs.alpha_g2); } +// kzg10_pairing_check_gadget + +template +kzg10_pairing_check_gadget::kzg10_pairing_check_gadget( + protoboard> &pb, + const G1_variable &A, + const G2_variable &B, + const G1_variable &C, + const libff::G2> &D, + pb_variable> &result, + const std::string annotation_prefix) + : gadget>(pb, annotation_prefix) + , A_precomp() + , compute_A_precomp( + pb, A, A_precomp, FMT(annotation_prefix, " compute_A_precomp")) + , B_precomp() + , compute_B_precomp( + pb, B, B_precomp, FMT(annotation_prefix, " compute_B_precomp")) + , C_precomp() + , compute_C_precomp( + pb, C, C_precomp, FMT(annotation_prefix, " compute_C_precomp")) + , D_precomp(pb, D, FMT(annotation_prefix, " D_precomp")) + , check_pairing_equality( + pb, + A_precomp, + B_precomp, + C_precomp, + D_precomp, + result, + FMT(annotation_prefix, " check_pairing_equality")) + +{ +} + +template +void kzg10_pairing_check_gadget::generate_r1cs_constraints() +{ + compute_A_precomp.generate_r1cs_constraints(); + compute_B_precomp.generate_r1cs_constraints(); + compute_C_precomp.generate_r1cs_constraints(); + check_pairing_equality.generate_r1cs_constraints(); +} + +template +void kzg10_pairing_check_gadget::generate_r1cs_witness() +{ + compute_A_precomp.generate_r1cs_witness(); + compute_B_precomp.generate_r1cs_witness(); + compute_C_precomp.generate_r1cs_witness(); + check_pairing_equality.generate_r1cs_witness(); +} + +// kzg10_verifier_gadget + template kzg10_verifier_gadget::kzg10_verifier_gadget( protoboard> &pb, @@ -90,31 +146,16 @@ kzg10_verifier_gadget::kzg10_verifier_gadget( -poly_eval_in_G1.value, C, FMT(annotation_prefix, " compute_C")) - - , A_precomp() - , compute_A_precomp( - pb, witness, A_precomp, FMT(annotation_prefix, " compute_A_precomp")) - , B_precomp() - , compute_B_precomp( - pb, B, B_precomp, FMT(annotation_prefix, " compute_B_precomp")) - , C_precomp() - , compute_C_precomp( - pb, C, C_precomp, FMT(annotation_prefix, " compute_C_precomp")) - , D_precomp( - pb, - libff::G2>::one(), - FMT(annotation_prefix, " D_precomp")) - , check_result(pb_variable_allocate( pb, FMT(annotation_prefix, " check_result"))) - , check_pairing_equality( + , pairing_check( pb, - A_precomp, - B_precomp, - C_precomp, - D_precomp, + witness, + B, + C, + libff::G2>::one(), check_result, - FMT(annotation_prefix, " check_pairing_equality")) + FMT(annotation_prefix, "pairing_check")) , group_elements_non_zero(pb_variable_allocate( pb, FMT(annotation_prefix, " group_elements_non_zero"))) @@ -129,10 +170,7 @@ void kzg10_verifier_gadget::generate_r1cs_constraints() compute_B.generate_r1cs_constraints(); compute_poly_eval_in_G1.generate_r1cs_constraints(); compute_C.generate_r1cs_constraints(); - compute_A_precomp.generate_r1cs_constraints(); - compute_B_precomp.generate_r1cs_constraints(); - compute_C_precomp.generate_r1cs_constraints(); - check_pairing_equality.generate_r1cs_constraints(); + pairing_check.generate_r1cs_constraints(); // group_elements_non_zero = // (1 - i_in_G2.is_identity) * (1 - poly_eval_in_G1.is_identity) @@ -159,10 +197,7 @@ template void kzg10_verifier_gadget::generate_r1cs_witness() // compute_C.B = -poly_eval_in_G1.value. Evaluate the result of negation. compute_C.B.Y.evaluate(this->pb); compute_C.generate_r1cs_witness(); - compute_A_precomp.generate_r1cs_witness(); - compute_B_precomp.generate_r1cs_witness(); - compute_C_precomp.generate_r1cs_witness(); - check_pairing_equality.generate_r1cs_witness(); + pairing_check.generate_r1cs_witness(); const FieldT group_elements_non_zero_val = (FieldT::one() - this->pb.lc_val(i_in_G2.is_identity)) * diff --git a/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp b/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp index 2042089be..f77f0c627 100644 --- a/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp +++ b/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp @@ -469,10 +469,10 @@ TEST(TestCurveGadgets, G1MulByConstScalar) // Circuit protoboard> pb; G1_variable P(pb, "P"); - G1_variable result_a(pb, "result"); + G1_variable result_a(pb, "result_a"); G1_mul_by_const_scalar_gadget::num_limbs> mul_gadget_a( pb, scalar_val_a.as_bigint(), P, result_a, "mul_gadget_a"); - G1_variable result_b(pb, "result"); + G1_variable result_b(pb, "result_b"); G1_mul_by_const_scalar_gadget::num_limbs> mul_gadget_b( pb, scalar_val_b.as_bigint(), P, result_b, "mul_gadget_b"); @@ -700,7 +700,7 @@ void test_mul_by_scalar_gadget( generate_and_check_proof(pb); } -TEST(TestCurveGadgets, G1MulScalarVar) +TEST(TestCurveGadgets, MulScalarVar) { auto test_g1_mul_by_scalar_gadget = test_mul_by_scalar_gadget< wpp, @@ -725,6 +725,43 @@ TEST(TestCurveGadgets, G1MulScalarVar) test_g2_mul_by_scalar_gadget(libff::Fr(13), -libff::Fr::one()); } +TEST(TestCurveGadgets, VarOrIdentityMulScalarVar) +{ + auto test_g1_var_or_identity_mul_by_scalar_gadget = + test_mul_by_scalar_gadget< + wpp, + libff::G1, + G1_variable_or_identity, + G1_variable_or_identity, + G1_variable_or_identity_mul_by_scalar_gadget>; + + auto test_g2_var_or_identity_mul_by_scalar_gadget = + test_mul_by_scalar_gadget< + wpp, + libff::G2, + G2_variable_or_identity, + G2_variable_or_identity, + G2_variable_or_identity_mul_by_scalar_gadget>; + + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), libff::Fr::zero()); + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr::zero(), libff::Fr(13)); + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), libff::Fr(127)); + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), -libff::Fr::one()); + + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), libff::Fr::zero()); + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr::zero(), libff::Fr(13)); + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), libff::Fr(127)); + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), -libff::Fr::one()); +} + } // namespace int main(int argc, char **argv) diff --git a/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp b/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp index 85c0e66e6..44ffccfa6 100644 --- a/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp +++ b/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp @@ -7,8 +7,9 @@ *****************************************************************************/ #include "libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bw6_761_pairing_params.hpp" +#include "libsnark/gadgetlib1/gadgets/verifiers/kzg10_batched_verifier_gadget.hpp" #include "libsnark/gadgetlib1/gadgets/verifiers/kzg10_verifier_gadget.hpp" -#include "libsnark/polynomial_commitments/kzg10.hpp" +#include "libsnark/polynomial_commitments/kzg10_batched.hpp" #include "libsnark/polynomial_commitments/tests/polynomial_commitment_test_utils.hpp" #include "libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp" @@ -155,11 +156,492 @@ template void test_kzg10_verifier_gadget() } } +template +void do_test_kzg10_batched_gamma_powers_commit_minus_eval_sum( + const libff::Fr> &gamma, + const std::vector>> &evals, + const std::vector>::commitment> &cms, + const libff::G1> &r, + const bool expected_result) +{ + using Field = libff::Fr; + + ASSERT_EQ(num_entries, evals.size()); + ASSERT_EQ(num_entries, cms.size()); + + // Protoboard and constraints + + protoboard pb; + + pb_variable gamma_var = pb_variable_allocate(pb, "gamma_var"); + std::vector> cms_var = + internal::allocate_variable_array>( + pb, num_entries, "cms_var"); + pb_variable_array evals_var; + evals_var.allocate(pb, num_entries, "evals_var"); + G1_variable result_var(pb, "result_var"); + + kzg10_batched_compute_gamma_powers_commit_minus_eval_sum + compute_sum( + pb, gamma_var, cms_var, evals_var, result_var, "compute_sum"); + + compute_sum.generate_r1cs_constraints(); + + // Witness + + Field wrapping_gamma; + fp_from_fp(wrapping_gamma, gamma); + pb.val(gamma_var) = wrapping_gamma; + + for (size_t i = 0; i < num_entries; ++i) { + cms_var[i].generate_r1cs_witness(cms[i]); + + Field wrapping_eval; + fp_from_fp(wrapping_eval, evals[i]); + pb.val(evals_var[i]) = wrapping_eval; + } + compute_sum.generate_r1cs_witness(); + + // Check result value + + if (expected_result) { + ASSERT_TRUE(pb.is_satisfied()); + ASSERT_EQ(r, result_var.get_element()); + } else { + ASSERT_NE(r, result_var.get_element()); + } + + // Test in proof + + const r1cs_gg_ppzksnark_keypair keypair = + r1cs_gg_ppzksnark_generator(pb.get_constraint_system(), true); + const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover( + keypair.pk, pb.primary_input(), pb.auxiliary_input(), true); + ASSERT_TRUE(r1cs_gg_ppzksnark_verifier_strong_IC( + keypair.vk, pb.primary_input(), proof)); +} + +template +void test_kzg10_batched_gamma_powers_commit_minus_eval_sum_gadget() +{ + using nField = libff::Fr>; + using nG1 = libff::G1>; + + const nField gamma = -nField("51"); + const std::vector evals{{"3"}, {"5"}, {"7"}, {"11"}}; + const std::vector cms{{ + nField("13") * nG1::one(), + nField("17") * nG1::one(), + nField("19") * nG1::one(), + nField("23") * nG1::one(), + }}; + + // // 2-entry case + // const nG1 r_2 = nField((13 - 3) - 51 * (17 - 5)) * nG1::one(); + // do_test_kzg10_batched_commit_minus_eval_sum( + // gamma, {evals[0], evals[1]}, {cms[0], cms[1]}, r_2, true); + + // 3-entry case + const nG1 r_3 = + nField((13 - 3) - 51 * (17 - 5) + (51 * 51) * (19 - 7)) * nG1::one(); + do_test_kzg10_batched_gamma_powers_commit_minus_eval_sum( + gamma, + {evals[0], evals[1], evals[2]}, + {cms[0], cms[1], cms[2]}, + r_3, + true); + + // 4-entry case + const nG1 r_4 = nField( + (13 - 3) - 51 * (17 - 5) + (51 * 51) * (19 - 7) - + (51 * 51 * 51) * (23 - 11)) * + nG1::one(); + do_test_kzg10_batched_gamma_powers_commit_minus_eval_sum( + gamma, evals, cms, r_4, true); +} + +template +void do_test_kzg10_batched_verifier_gadget( + const libff::Fr> &z_1, + const libff::Fr> &z_2, + const typename kzg10_batched_2_point>::evaluations + &evaluations, + const typename kzg10>::srs &srs, + const libff::Fr> &gamma_1, + const libff::Fr> &gamma_2, + const typename kzg10_batched_2_point>::evaluation_witness + &eval_witness, + const std::vector>::commitment> &cm_1s, + const std::vector>::commitment> &cm_2s, + const libff::Fr> &r, + const bool expected_result) +{ + using Field = libff::Fr; + using npp = other_curve; + using nG1 = libff::G1; + + protoboard pb; + + // z_1 and z_2 + pb_variable z_1_var; + z_1_var.allocate(pb, "z_1_var"); + pb_variable z_2_var; + z_2_var.allocate(pb, "z_2_var"); + + // Evaluations + pb_variable_array poly_1_evals_var; + poly_1_evals_var.allocate(pb, evaluations.s_1s.size(), "poly_1_evals_var"); + + pb_variable_array poly_2_evals_var; + poly_2_evals_var.allocate(pb, evaluations.s_2s.size(), "poly_2_evals_var"); + + // srs + kzg10_srs_variable srs_var(pb, POLYNOMIAL_MAX_DEGREE, "srs_var"); + + // Gammas + pb_variable gamma_1_var; + gamma_1_var.allocate(pb, "gamma_1_var"); + pb_variable gamma_2_var; + gamma_2_var.allocate(pb, "gamma_2_var"); + + // Witness + kzg10_batched_witness_variable eval_witness_var( + pb, "eval_witness_var"); + + // Commitments + std::vector> cm_1s_var; + std::vector> cm_2s_var; + + for (size_t i = 0; i < cm_1s.size(); ++i) { + cm_1s_var.push_back( + kzg10_commitment_variable(pb, FMT("", "cm_1s_var[%zu]", i))); + } + + for (size_t i = 0; i < cm_2s.size(); ++i) { + cm_2s_var.push_back( + kzg10_commitment_variable(pb, FMT("", "cm_2s_var[%zu]", i))); + } + + // r + pb_variable r_var; + r_var.allocate(pb, "r_var"); + + // result + pb_variable result_var; + result_var.allocate(pb, "result_var"); + + // Verifier gadget + kzg10_batched_verifier_gadget + verifier_gadget( + pb, + z_1_var, + z_2_var, + poly_1_evals_var, + poly_2_evals_var, + srs_var, + gamma_1_var, + gamma_2_var, + eval_witness_var, + cm_1s_var, + cm_2s_var, + r_var, + result_var, + "verifier_gadget"); + + verifier_gadget.generate_r1cs_constraints(); + + // Field containers of nField elements + Field wrapping_z_1; + libff::fp_from_fp(wrapping_z_1, z_1); + Field wrapping_z_2; + libff::fp_from_fp(wrapping_z_2, z_2); + std::vector wrapping_poly_1_evals(evaluations.s_1s.size()); + for (size_t i = 0; i < evaluations.s_1s.size(); ++i) { + libff::fp_from_fp(wrapping_poly_1_evals[i], evaluations.s_1s[i]); + } + std::vector wrapping_poly_2_evals(evaluations.s_2s.size()); + for (size_t i = 0; i < evaluations.s_2s.size(); ++i) { + libff::fp_from_fp(wrapping_poly_2_evals[i], evaluations.s_2s[i]); + } + Field wrapping_gamma_1; + libff::fp_from_fp(wrapping_gamma_1, gamma_1); + Field wrapping_gamma_2; + libff::fp_from_fp(wrapping_gamma_2, gamma_2); + Field wrapping_r; + libff::fp_from_fp(wrapping_r, r); + + // Assign witnesses to all parameters + pb.val(z_1_var) = wrapping_z_1; + pb.val(z_2_var) = wrapping_z_2; + poly_1_evals_var.fill_with_field_elements(pb, wrapping_poly_1_evals); + poly_2_evals_var.fill_with_field_elements(pb, wrapping_poly_2_evals); + srs_var.generate_r1cs_witness(srs); + pb.val(gamma_1_var) = wrapping_gamma_1; + pb.val(gamma_2_var) = wrapping_gamma_2; + eval_witness_var.generate_r1cs_witness(eval_witness); + for (size_t i = 0; i < cm_1s.size(); ++i) { + cm_1s_var[i].generate_r1cs_witness(cm_1s[i]); + } + for (size_t i = 0; i < cm_2s.size(); ++i) { + cm_2s_var[i].generate_r1cs_witness(cm_2s[i]); + } + pb.val(r_var) = wrapping_r; + + verifier_gadget.generate_r1cs_witness(); + + // Check intermediate values + + if (expected_result) { + const nG1 G_expect = internal::gamma_times_commit_minus_eval_sum( + gamma_1, evaluations.s_1s, cm_1s); + const nG1 H_expect = internal::gamma_times_commit_minus_eval_sum( + gamma_2, evaluations.s_2s, cm_2s); + const nG1 F_expect = G_expect + r * H_expect; + + const nG1 r_times_W_2_expect = r * eval_witness.W_2; + const nG1 A_expect = eval_witness.W_1 + r_times_W_2_expect; + + const nG1 r_times_z_2_times_W_2_expect = z_2 * r_times_W_2_expect; + const nG1 z_1_times_W_1_expect = z_1 * eval_witness.W_1; + const nG1 F_plus_z_1_times_W_1_expect = F_expect + z_1_times_W_1_expect; + const nG1 B_expect = + F_plus_z_1_times_W_1_expect + r_times_z_2_times_W_2_expect; + + ASSERT_EQ(G_expect, verifier_gadget.compute_G.result().get_element()); + ASSERT_EQ(H_expect, verifier_gadget.compute_H.result().get_element()); + ASSERT_EQ(F_expect, verifier_gadget.compute_F.result.get_element()); + ASSERT_EQ( + r_times_W_2_expect, + verifier_gadget.compute_r_times_W_2.result().get_element()); + ASSERT_EQ(A_expect, verifier_gadget.compute_A.result.get_element()); + ASSERT_EQ( + r_times_z_2_times_W_2_expect, + verifier_gadget.compute_r_times_z_2_times_W_2.result() + .get_element()); + ASSERT_EQ( + z_1_times_W_1_expect, + verifier_gadget.compute_z_1_times_W_1.result().get_element()); + ASSERT_EQ( + F_plus_z_1_times_W_1_expect, + verifier_gadget.compute_F_plus_z_1_times_W_1.result.get_element()); + ASSERT_EQ(B_expect, verifier_gadget.compute_B.result.get_element()); + } + + // Check the result. + + ASSERT_TRUE(pb.is_satisfied()); + ASSERT_EQ( + expected_result ? Field::one() : Field::zero(), pb.val(result_var)); + + // Test proof gen + + const r1cs_gg_ppzksnark_keypair keypair = + r1cs_gg_ppzksnark_generator(pb.get_constraint_system(), true); + const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover( + keypair.pk, pb.primary_input(), pb.auxiliary_input(), true); + ASSERT_TRUE(r1cs_gg_ppzksnark_verifier_strong_IC( + keypair.vk, pb.primary_input(), proof)); +} + +template void test_kzg10_batched_verifier_gadget() +{ + using npp = other_curve; + using scheme = kzg10; + + using nField = libff::Fr; + + // 2 polynomials to be evaluated at the first point, 3 at the second. + const size_t num_polynomials_1 = 2; + const size_t num_polynomials_2 = 3; + + // SRS + const typename scheme::srs srs = scheme::setup(POLYNOMIAL_MAX_DEGREE); + + // Generate polynomials and commitment + std::vector> polynomials_1; + std::vector> polynomials_2; + std::vector::commitment> cm_1s; + std::vector::commitment> cm_2s; + + for (size_t i = 0; i < num_polynomials_1; ++i) { + polynomials_1.push_back(gen_polynomial(POLYNOMIAL_SIZE)); + cm_1s.push_back(kzg10::commit(srs, polynomials_1.back())); + } + + for (size_t i = 0; i < num_polynomials_2; ++i) { + polynomials_2.push_back(gen_polynomial(POLYNOMIAL_SIZE)); + cm_2s.push_back(kzg10::commit(srs, polynomials_2.back())); + } + + std::vector::commitment> cm_1s_invalid{ + cm_1s[0] + cm_1s[0], cm_1s[1]}; + std::vector::commitment> cm_2s_invalid{ + cm_2s[0] + cm_2s[0], cm_2s[1], cm_2s[2]}; + + // Evaluations + const nField z_1 = nField("13"); + const nField z_2 = nField("17"); + + const typename kzg10_batched_2_point::evaluations evals = + kzg10_batched_2_point::evaluate_polynomials( + polynomials_1, polynomials_2, z_1, z_2); + + // Witness + const nField gamma_1 = nField("3"); + const nField gamma_2 = -nField("5"); + + const typename kzg10_batched_2_point::evaluation_witness eval_witness = + kzg10_batched_2_point::create_evaluation_witness( + polynomials_1, + polynomials_2, + z_1, + z_2, + evals, + srs, + gamma_1, + gamma_2); + + // Check evaluations and witness natively + const nField r = nField("7"); + ASSERT_TRUE(kzg10_batched_2_point::verify_evaluations( + z_1, z_2, evals, srs, gamma_1, gamma_2, eval_witness, cm_1s, cm_2s, r)); + + // Test gadget in the positive case + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1, + z_2, + evals, + srs, + gamma_1, + gamma_2, + eval_witness, + cm_1s, + cm_2s, + r, + true); + + // Test some failure cases: + + // Invalid cases + { + // Invalid evaluation point + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1 + z_1, + z_2, + evals, + srs, + gamma_1, + gamma_2, + eval_witness, + cm_1s, + cm_2s, + r, + false); + + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1, + z_2 + z_2, + evals, + srs, + gamma_1, + gamma_2, + eval_witness, + cm_1s, + cm_2s, + r, + false); + + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1, + z_2, + evals, + srs, + gamma_1 + gamma_1, + gamma_2, + eval_witness, + cm_1s, + cm_2s, + r, + false); + + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1, + z_2, + evals, + srs, + gamma_1, + gamma_2 + gamma_2, + eval_witness, + cm_1s, + cm_2s, + r, + false); + + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1, + z_2, + evals, + srs, + gamma_1, + gamma_2, + eval_witness, + cm_1s_invalid, + cm_2s, + r, + false); + + do_test_kzg10_batched_verifier_gadget< + wppT, + num_polynomials_1, + num_polynomials_2>( + z_1, + z_2, + evals, + srs, + gamma_1, + gamma_2, + eval_witness, + cm_1s, + cm_2s_invalid, + r, + false); + } +} + TEST(TestKZG10VerifierGadget, ValidEvaluation) { test_kzg10_verifier_gadget(); } +TEST(TestKZG10VerifierGadget, BatchedGammaPowersCommitMinusEvalSum) +{ + test_kzg10_batched_gamma_powers_commit_minus_eval_sum_gadget< + libff::bw6_761_pp>(); +} + +TEST(TestKZG10VerifierGadget, BatchedValidEvaluation) +{ + test_kzg10_batched_verifier_gadget(); +} + } // namespace int main(int argc, char **argv) diff --git a/libsnark/polynomial_commitments/kzg10_batched.tcc b/libsnark/polynomial_commitments/kzg10_batched.tcc index 49032436d..e1420a0d5 100644 --- a/libsnark/polynomial_commitments/kzg10_batched.tcc +++ b/libsnark/polynomial_commitments/kzg10_batched.tcc @@ -91,6 +91,35 @@ static polynomial polynomial_accumulate_with_power_factors( return f_accum; } +// Compute terms of the form: +// +// \sum_i \gamma^{i-1} (cm_i - [eval_i]_1) +template +static libff::G1 gamma_times_commit_minus_eval_sum( + const libff::Fr &gamma, + const std::vector> &evals, + const std::vector> &cms) +{ + // Compute: + // + // eval_accum = \sum_i gamma^{i-1} evals_i + // cm_accum = \sum_i gamma^{i-1} cm_i + // result = cm_accum - (eval_accum * G1::one()) + + const size_t t = evals.size(); + assert(cms.size() == t); + + libff::Fr eval_accum = evals[t - 1]; + libff::G1 cm_accum = cms[t - 1]; + // Note use of underflow to terminate after i = 0. + for (size_t i = t - 2; i < t; --i) { + cm_accum = (gamma * cm_accum) + cms[i]; + eval_accum = (eval_accum * gamma) + evals[i]; + } + + return cm_accum - eval_accum * libff::G1::one(); +} + } // namespace internal template @@ -222,50 +251,25 @@ bool kzg10_batched_2_point::verify_evaluations( { // See Section 3, p13 of [GWC19]. - const size_t t1 = cm_1s.size(); - const size_t t2 = cm_2s.size(); - assert(t1 == evaluations.s_1s.size()); - assert(t2 == evaluations.s_2s.size()); + assert(cm_1s.size() == evaluations.s_1s.size()); + assert(cm_2s.size() == evaluations.s_2s.size()); // Compute: // - // F = \sum_{i=1}^{t1} \gamma_1^{i-1} (cm_1[i] - [s_1[i]]_1) + (G) - // r \sum_{i=1}^{t2} \gamma_2^{i-1} (cm_2[i] - [s_2[i]]_1) (H) - - const std::vector &s_1s = evaluations.s_1s; - const std::vector &s_2s = evaluations.s_2s; - - // Compute: + // F = \sum_{i=1}^{t1} \gamma_1^{i-1} (cm_1[i] - [s_1[i]]_1) + + // r \sum_{i=1}^{t2} \gamma_2^{i-1} (cm_2[i] - [s_2[i]]_1) + // = G + r * H // - // s_1_accum = \sum_i \gamma_1^{i-1} s_1[i] (in the scalar field) - // cm_1_accum = \sum_i \gamma_1^{i-1} cm_1[i] (in G1) - // G = cm_1_accum - s_1_accum * G1::one() - - Field s_1_accum = s_1s[t1 - 1]; - libff::G1 cm_1_accum = cm_1s[t1 - 1]; - // Note use of underflow to terminate after i = 0. - for (size_t i = t1 - 2; i < t1; --i) { - cm_1_accum = (gamma_1 * cm_1_accum) + cm_1s[i]; - s_1_accum = (s_1_accum * gamma_1) + s_1s[i]; - } - const libff::G1 G = cm_1_accum - s_1_accum * libff::G1::one(); - - // Similarly: + // where: // - // s_2_accum = \sum_i \gamma_2^{i-1} s_2[i] (in the scalar field) - // cm_2_accum = \sum_i \gamma_2^{i-1} cm_2[i] (in G1) - // H = cm_2_accum - s_2_accum * G1::one() - - Field s_2_accum = s_2s[t2 - 1]; - libff::G1 cm_2_accum = cm_2s[t2 - 1]; - for (size_t i = t2 - 2; i < t2; --i) { - cm_2_accum = gamma_2 * cm_2_accum + cm_2s[i]; - s_2_accum = (s_2_accum * gamma_2) + s_2s[i]; - } - const libff::G1 H = - r * (cm_2_accum - s_2_accum * libff::G1::one()); - - const libff::G1 F = G + H; + // G = \sum_{i=1}^{t1} \gamma_1^{i-1} (cm_1[i] - [s_1[i]]_1) + // H = \sum_{i=1}^{t2} \gamma_2^{i-1} (cm_2[i] - [s_2[i]]_1) + + const libff::G1 G = internal::gamma_times_commit_minus_eval_sum( + gamma_1, evaluations.s_1s, cm_1s); + const libff::G1 H = internal::gamma_times_commit_minus_eval_sum( + gamma_2, evaluations.s_2s, cm_2s); + const libff::G1 F = G + r * H; // The pairing check takes the form: //