diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79801fd14..2fd725fe5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,4 +36,4 @@ jobs: - name: build run: cd build && make -j $(($(nproc)+1)) - name: test - run: cd build && make check -j $(($(nproc)+1)) + run: cd build && CTEST_OUTPUT_ON_FAILURE=1 make check -j $(($(nproc)+1)) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12176bfdc..165eb9acc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,10 +133,12 @@ if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "^(Apple)?Clan endif() # Default optimizations flags (to override, use -DOPT_FLAGS=...) if("${OPT_FLAGS}" STREQUAL "") - set( - OPT_FLAGS - "-ggdb3 -O2 -march=native -mtune=native" - ) + if (NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")) + set( + OPT_FLAGS + "-ggdb3 -O2 -march=native -mtune=native" + ) + endif() endif() endif() diff --git a/depends/libff b/depends/libff index 5f9a4bdd9..87d8b4357 160000 --- a/depends/libff +++ b/depends/libff @@ -1 +1 @@ -Subproject commit 5f9a4bdd9bea61036b57cfe972354fc97c497457 +Subproject commit 87d8b435798f54918b355e4f47607f6a197561ff diff --git a/libsnark/common/constraints_tracker/constraints_tracker.cpp b/libsnark/common/constraints_tracker/constraints_tracker.cpp new file mode 100644 index 000000000..8574f0792 --- /dev/null +++ b/libsnark/common/constraints_tracker/constraints_tracker.cpp @@ -0,0 +1,61 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by Clearmatics Ltd + * (originally developed by SCIPR Lab) and contributors + * (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#include "libsnark/common/constraints_tracker/constraints_tracker.hpp" + +#include +#include + +namespace libsnark +{ + +constraints_tracker::~constraints_tracker() +{ + // For now, just dump all entries to stdout + + std::cout << "====================\n" + << " CONSTRAINTS\n" + << "====================\n"; + + for (const auto &curve_it : _measurements) { + std::cout << "\nCURVE " << curve_it.first << ":\n"; + for (const auto &entry_it : curve_it.second) { + std::cout << " " << entry_it.first << ": " << entry_it.second + << "\n"; + } + } + + // Ensure everything is output before the process terminates. + std::cout.flush(); +} + +void constraints_tracker::add_measurement_for_curve( + const std::string &curve_name, + const std::string &name, + size_t num_constraints) +{ + std::map::iterator it = + _measurements.find(curve_name); + if (it == _measurements.end()) { + _measurements[curve_name] = {{name, num_constraints}}; + return; + } + +#ifndef NDEBUG + for (const auto &entry_it : it->second) { + if (entry_it.first == name) { + throw std::runtime_error( + "duplicate entry: " + name + " (curve: " + curve_name + ")"); + } + } +#endif + + it->second.emplace_back(name, num_constraints); +} + +} // namespace libsnark diff --git a/libsnark/common/constraints_tracker/constraints_tracker.hpp b/libsnark/common/constraints_tracker/constraints_tracker.hpp new file mode 100644 index 000000000..a85a9ebff --- /dev/null +++ b/libsnark/common/constraints_tracker/constraints_tracker.hpp @@ -0,0 +1,72 @@ +/** @file + ***************************************************************************** + * @author This file is part of libsnark, developed by Clearmatics Ltd + * (originally developed by SCIPR Lab) and contributors + * (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef LIBSNARK_COMMON_CONSTRAINTS_TRACKER_HPP_ +#define LIBSNARK_COMMON_CONSTRAINTS_TRACKER_HPP_ + +#include +#include +#include + +namespace libsnark +{ + +/// Simple class to track a set of measurements (initially the number of +/// constraints required by a given gadget) during test or profiling code, and +/// present them together in a sensible format (rather than interspersed with +/// other output to stdout). +/// +/// Intended usage is in a test file, create a static global: +/// +/// static constraints_tracker constraints_tracker; +/// +/// and then later register measurements: +/// +/// TEST(TestSuite, SomeTest) +/// { +/// ... +/// constraints_tracker.add_measurement( +/// "some_gadget", num_constraints); +/// ... +/// } +/// +/// if the unit tests exits cleanly, all measurements should be printed to +/// stdout. +/// +/// In the future, this may be expanded to collect more data, or write it in +/// other formats to different places. +class constraints_tracker +{ +public: + ~constraints_tracker(); + + template + void add_measurement(const std::string &name, size_t num_constraints); + +protected: + using measurement = std::pair; + using measurements_for_curve = std::vector; + + void add_measurement_for_curve( + const std::string &curve_name, + const std::string &name, + size_t num_constraints); + + std::map _measurements; +}; + +template +void constraints_tracker::add_measurement( + const std::string &name, size_t num_constraints) +{ + add_measurement_for_curve(ppT::name, name, num_constraints); +} + +} // namespace libsnark + +#endif // LIBSNARK_COMMON_CONSTRAINTS_TRACKER_HPP_ diff --git a/libsnark/gadgetlib1/gadget.hpp b/libsnark/gadgetlib1/gadget.hpp index 2f3ad20ed..0dbcf2966 100644 --- a/libsnark/gadgetlib1/gadget.hpp +++ b/libsnark/gadgetlib1/gadget.hpp @@ -20,7 +20,7 @@ template class gadget const std::string annotation_prefix; public: - gadget(protoboard &pb, const std::string &annotation_prefix = ""); + gadget(protoboard &pb, const std::string &annotation_prefix); }; } // namespace libsnark diff --git a/libsnark/gadgetlib1/gadget.tcc b/libsnark/gadgetlib1/gadget.tcc index 654141d89..0d874e426 100644 --- a/libsnark/gadgetlib1/gadget.tcc +++ b/libsnark/gadgetlib1/gadget.tcc @@ -16,6 +16,8 @@ gadget::gadget( protoboard &pb, const std::string &annotation_prefix) : pb(pb), annotation_prefix(annotation_prefix) { + // Anotations may appear as "" (even if set by the calling code) unless + // DEBUG is set. See pb_variable.tcc. #ifdef DEBUG assert(annotation_prefix != ""); #endif 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/pb_variable.hpp b/libsnark/gadgetlib1/pb_variable.hpp index 5c4b05906..ef540b9b6 100644 --- a/libsnark/gadgetlib1/pb_variable.hpp +++ b/libsnark/gadgetlib1/pb_variable.hpp @@ -26,7 +26,7 @@ template class pb_variable : public variable public: pb_variable(const var_index_t index = 0) : variable(index){}; - void allocate(protoboard &pb, const std::string &annotation = ""); + void allocate(protoboard &pb, const std::string &annotation); }; /// A utility function which creates and allocates a variable in a single step @@ -77,7 +77,7 @@ class pb_variable_array : private std::vector> void allocate( protoboard &pb, const size_t n, - const std::string &annotation_prefix = ""); + const std::string &annotation_prefix); void fill_with_field_elements( protoboard &pb, const std::vector &vals) const; @@ -94,17 +94,6 @@ class pb_variable_array : private std::vector> FieldT get_field_element_from_bits(const protoboard &pb) const; }; -/// A utility function which creates and allocates a variable in a single step -/// (and can therefore be used in initalizer lists, which greatly simplifies -/// many constructors). -/// -/// TODO: Why does pb_variable not have an allocating constructor of this form, -/// even further simplifying a lot of code. Move this to an appropriate -/// constructor if there are no issues. -template -pb_variable pb_variable_allocate( - protoboard &pb, const std::string &annotation); - /* index 0 corresponds to the constant term (used in legacy code) */ #define ONE pb_variable(0) diff --git a/libsnark/gadgetlib1/pb_variable.tcc b/libsnark/gadgetlib1/pb_variable.tcc index 15b8a0161..3bf2e810a 100644 --- a/libsnark/gadgetlib1/pb_variable.tcc +++ b/libsnark/gadgetlib1/pb_variable.tcc @@ -28,6 +28,13 @@ void pb_variable_array::allocate( const size_t n, const std::string &annotation_prefix) { + // The DEBUG variable controls whether or not the FMT macro actually + // performs string concatenation or simply returns "". Therefore, even + // though gadget code may always set an annotation, it may appear as "" + // unless DEBUG is set. + // + // TODO: control annotations via a variable such as + // LIBSNARK_ENABLE_ANNOTATIONS. #ifdef DEBUG assert(annotation_prefix != ""); #endif diff --git a/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp b/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp index 2042089be..ceb85a3ea 100644 --- a/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp +++ b/libsnark/gadgetlib1/tests/test_curve_gadgets.cpp @@ -6,6 +6,7 @@ * @copyright MIT license (see LICENSE file) *****************************************************************************/ +#include "libsnark/common/constraints_tracker/constraints_tracker.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bw6_761_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/mnt/mnt_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp" @@ -25,13 +26,18 @@ using npp = other_curve; namespace { +static constraints_tracker constraints; + template -void generate_and_check_proof(protoboard> &pb) +void generate_and_check_proof( + protoboard> &pb, const std::string &test_name) { // Generate and check the proof ASSERT_TRUE(pb.is_satisfied()); const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(pb.get_constraint_system(), true); + + constraints.add_measurement(test_name, pb.num_constraints()); r1cs_primary_input> primary_input = pb.primary_input(); r1cs_auxiliary_input> auxiliary_input = pb.auxiliary_input(); r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover( @@ -48,20 +54,16 @@ void test_G2_checker_gadget(const std::string &annotation) G2_checker_gadget g_check(pb, g, "g_check"); g_check.generate_r1cs_constraints(); - printf("positive test\n"); g.generate_r1cs_witness(libff::G2>::one()); g_check.generate_r1cs_witness(); assert(pb.is_satisfied()); - printf("negative test\n"); g.generate_r1cs_witness(libff::G2>::zero()); g_check.generate_r1cs_witness(); assert(!pb.is_satisfied()); - printf( - "number of constraints for G2 checker (Fr is %s) = %zu\n", - annotation.c_str(), - pb.num_constraints()); + constraints.add_measurement( + "G2_checker_gadget - " + annotation, pb.num_constraints()); } template< @@ -72,7 +74,10 @@ template< typename VarR, typename AddGadgetT> void test_add_gadget( - const GroupT &a_val, const GroupT &b_val, const GroupT &expect_val) + const GroupT &a_val, + const GroupT &b_val, + const GroupT &expect_val, + const std::string &test_name) { ASSERT_EQ(expect_val, a_val + b_val); @@ -91,10 +96,13 @@ void test_add_gadget( const GroupT result_val = result.get_element(); ASSERT_TRUE(pb.is_satisfied()); ASSERT_EQ(expect_val, result_val); + + constraints.add_measurement(test_name, pb.num_constraints()); } template -void test_dbl_gadget(const GroupT &a_val, const GroupT &expect_val) +void test_dbl_gadget( + const GroupT &a_val, const GroupT &expect_val, const std::string &test_name) { ASSERT_EQ(expect_val, a_val + a_val); @@ -111,6 +119,8 @@ void test_dbl_gadget(const GroupT &a_val, const GroupT &expect_val) const GroupT result_val = result.get_element(); ASSERT_TRUE(pb.is_satisfied()); ASSERT_EQ(expect_val, result_val); + + constraints.add_measurement(test_name, pb.num_constraints()); } template< @@ -120,7 +130,8 @@ template< typename VarB, typename VarR, typename SelectorGadgetT> -void test_selector_gadget(GroupT a_val, GroupT b_val) +void test_selector_gadget( + GroupT a_val, GroupT b_val, const std::string &test_name) { a_val.to_affine_coordinates(); b_val.to_affine_coordinates(); @@ -171,11 +182,11 @@ void test_selector_gadget(GroupT a_val, GroupT b_val) ASSERT_EQ(b_val, one_result.get_element()); // Verify in a proof - generate_and_check_proof(pb); + generate_and_check_proof(pb, test_name); } template -void test_variable_or_identity_selector_gadget() +void test_variable_or_identity_selector_gadget(const std::string &test_name) { auto test_selector = test_selector_gadget; @@ -183,10 +194,10 @@ void test_variable_or_identity_selector_gadget() const GroupT a_val = GroupT::one() + GroupT::one(); const GroupT b_val = -GroupT::one(); - test_selector(a_val, b_val); - test_selector(GroupT::zero(), b_val); - test_selector(a_val, GroupT::zero()); - test_selector(GroupT::zero(), GroupT::zero()); + test_selector(a_val, b_val, "N_N_" + test_name); + test_selector(GroupT::zero(), b_val, "Z_N_" + test_name); + test_selector(a_val, GroupT::zero(), "N_Z_" + test_name); + test_selector(GroupT::zero(), GroupT::zero(), "Z_Z_" + test_name); } template< @@ -196,7 +207,8 @@ template< typename VarB, typename VarR, typename SelectorGadgetT> -void test_variable_and_variable_or_identity_selector_gadget() +void test_variable_and_variable_or_identity_selector_gadget( + const std::string &test_name) { auto test_selector = test_selector_gadget; @@ -204,8 +216,8 @@ void test_variable_and_variable_or_identity_selector_gadget() const GroupT a_val = GroupT::one() + GroupT::one(); const GroupT b_val = -GroupT::one(); - test_selector(a_val, b_val); - test_selector(GroupT::zero(), b_val); + test_selector(a_val, b_val, "V_V_" + test_name); + test_selector(GroupT::zero(), b_val, "N_V_" + test_name); } TEST(TestCurveGadgets, G2Checker) @@ -225,7 +237,8 @@ TEST(TestCurveGadgets, G1SelectorGadget) G1_variable, G1_variable_selector_gadget>; - test_selector(Group::one() + Group::one(), -Group::one()); + test_selector( + Group::one() + Group::one(), -Group::one(), "test_selector_g1_bw6_761"); } TEST(TestCurveGadgets, G2SelectorGadget) @@ -239,7 +252,8 @@ TEST(TestCurveGadgets, G2SelectorGadget) G2_variable, G2_variable_selector_gadget>; - test_selector(Group::one() + Group::one(), -Group::one()); + test_selector( + Group::one() + Group::one(), -Group::one(), "test_selector_g2_bw6_761"); } TEST(TestCurveGadgets, G1VarOrIdentitySelectorGadget) @@ -248,7 +262,8 @@ TEST(TestCurveGadgets, G1VarOrIdentitySelectorGadget) wpp, libff::G1, G1_variable_or_identity, - G1_variable_or_identity_selector_gadget>(); + G1_variable_or_identity_selector_gadget>( + "test_var_or_id_selector_gadget_g1_bw6_761"); } TEST(TestCurveGadgets, G2VarOrIdentitySelectorGadget) @@ -257,7 +272,8 @@ TEST(TestCurveGadgets, G2VarOrIdentitySelectorGadget) wpp, libff::G2, G2_variable_or_identity, - G2_variable_or_identity_selector_gadget>(); + G2_variable_or_identity_selector_gadget>( + "test_var_or_id_selector_gadget_g2_bw6_761"); } TEST(TestCurveGadgets, G1VarAndVarOrIdentitySelectorGadget) @@ -268,7 +284,8 @@ TEST(TestCurveGadgets, G1VarAndVarOrIdentitySelectorGadget) G1_variable_or_identity, G1_variable, G1_variable_or_identity, - G1_variable_and_variable_or_identity_selector_gadget>(); + G1_variable_and_variable_or_identity_selector_gadget>( + "test_var_and_var_or_id_selector_gasgete_g1_bw6_761"); } TEST(TestCurveGadgets, G2VarAndVarOrIdentitySelectorGadget) @@ -279,7 +296,8 @@ TEST(TestCurveGadgets, G2VarAndVarOrIdentitySelectorGadget) G2_variable_or_identity, G2_variable, G2_variable_or_identity, - G2_variable_and_variable_or_identity_selector_gadget>(); + G2_variable_and_variable_or_identity_selector_gadget>( + "test_var_and_var_or_id_selector_gasgete_g2_bw6_761"); } TEST(TestCurveGadgets, G1AddGadget) @@ -293,7 +311,8 @@ TEST(TestCurveGadgets, G1AddGadget) G1_add_gadget>( libff::Fr(13) * libff::G1::one(), libff::Fr(12) * libff::G1::one(), - libff::Fr(12 + 13) * libff::G1::one()); + libff::Fr(12 + 13) * libff::G1::one(), + "test_add_gadget_g1_bw6_761"); } TEST(TestCurveGadgets, G2AddGadget) @@ -307,7 +326,8 @@ TEST(TestCurveGadgets, G2AddGadget) G2_add_gadget>( libff::Fr(13) * libff::G2::one(), libff::Fr(12) * libff::G2::one(), - libff::Fr(12 + 13) * libff::G2::one()); + libff::Fr(12 + 13) * libff::G2::one(), + "test_add_gadget_g2_bw6_761"); } TEST(TestCurveGadgets, G1AddVarOrIdentityGadget) @@ -323,17 +343,20 @@ TEST(TestCurveGadgets, G1AddVarOrIdentityGadget) test_add_variable_or_identity( libff::Fr(13) * libff::G1::one(), libff::Fr(12) * libff::G1::one(), - libff::Fr(12 + 13) * libff::G1::one()); + libff::Fr(12 + 13) * libff::G1::one(), + "test_add_var_or_id_gadget_N_N_g1_bw6_761"); test_add_variable_or_identity( libff::Fr(0) * libff::G1::one(), libff::Fr(12) * libff::G1::one(), - libff::Fr(12) * libff::G1::one()); + libff::Fr(12) * libff::G1::one(), + "test_add_var_or_id_gadget_Z_N_g1_bw6_761"); test_add_variable_or_identity( libff::Fr(13) * libff::G1::one(), libff::Fr(0) * libff::G1::one(), - libff::Fr(13) * libff::G1::one()); + libff::Fr(13) * libff::G1::one(), + "test_add_var_or_id_gadget_N_Z_g1_bw6_761"); // Note, the 0 + 0 case is not supported. } @@ -351,17 +374,20 @@ TEST(TestCurveGadgets, G2AddVarOrIdentityGadget) test_add_variable_or_identity( libff::Fr(13) * libff::G2::one(), libff::Fr(12) * libff::G2::one(), - libff::Fr(12 + 13) * libff::G2::one()); + libff::Fr(12 + 13) * libff::G2::one(), + "test_add_var_or_id_gadget_N_N_g2_bw6_761"); test_add_variable_or_identity( libff::Fr(0) * libff::G2::one(), libff::Fr(12) * libff::G2::one(), - libff::Fr(12) * libff::G2::one()); + libff::Fr(12) * libff::G2::one(), + "test_add_var_or_id_gadget_Z_N_g2_bw6_761"); test_add_variable_or_identity( libff::Fr(13) * libff::G2::one(), libff::Fr(0) * libff::G2::one(), - libff::Fr(13) * libff::G2::one()); + libff::Fr(13) * libff::G2::one(), + "test_add_var_or_id_gadget_N_Z_g2_bw6_761"); // Note, the 0 + 0 case is not supported. } @@ -379,12 +405,14 @@ TEST(TestCurveGadgets, G1AddVarAndVarOrIdentityGadget) test_add_variable_and_variable_or_identity( libff::Fr(13) * libff::G1::one(), libff::Fr(12) * libff::G1::one(), - libff::Fr(12 + 13) * libff::G1::one()); + libff::Fr(12 + 13) * libff::G1::one(), + "test_add_var_and_var_or_id_N_g1_bw6_761"); test_add_variable_and_variable_or_identity( libff::Fr(0) * libff::G1::one(), libff::Fr(12) * libff::G1::one(), - libff::Fr(12) * libff::G1::one()); + libff::Fr(12) * libff::G1::one(), + "test_add_var_and_var_or_id_Z_g1_bw6_761"); // Note, the 0 + 0 case is not supported. } @@ -402,12 +430,14 @@ TEST(TestCurveGadgets, G2AddVarAndVarOrIdentityGadget) test_add_variable_and_variable_or_identity( libff::Fr(13) * libff::G2::one(), libff::Fr(12) * libff::G2::one(), - libff::Fr(12 + 13) * libff::G2::one()); + libff::Fr(12 + 13) * libff::G2::one(), + "test_add_var_and_var_or_id_N_g2_bw6_761"); test_add_variable_and_variable_or_identity( libff::Fr(0) * libff::G2::one(), libff::Fr(12) * libff::G2::one(), - libff::Fr(12) * libff::G2::one()); + libff::Fr(12) * libff::G2::one(), + "test_add_var_and_var_or_id_Z_g2_bw6_761"); // Note, the 0 + 0 case is not supported. } @@ -416,14 +446,16 @@ TEST(TestCurveGadgets, G1DblGadget) { test_dbl_gadget, G1_variable, G1_dbl_gadget>( libff::Fr(13) * libff::G1::one(), - libff::Fr(13 + 13) * libff::G1::one()); + libff::Fr(13 + 13) * libff::G1::one(), + "test_dbl_g1_bw6_761"); } TEST(TestCurveGadgets, G2DblGadget) { test_dbl_gadget, G2_variable, G2_dbl_gadget>( libff::Fr(13) * libff::G2::one(), - libff::Fr(13 + 13) * libff::G2::one()); + libff::Fr(13 + 13) * libff::G2::one(), + "test_dbl_g2_bw6_761"); } TEST(TestCurveGadgets, G1DblVarOrIdentityGadget) @@ -436,10 +468,13 @@ TEST(TestCurveGadgets, G1DblVarOrIdentityGadget) test_dbl_variable_or_identity( libff::Fr(13) * libff::G1::one(), - libff::Fr(13 + 13) * libff::G1::one()); + libff::Fr(13 + 13) * libff::G1::one(), + "test_dbl_var_or_id_N_g1_bw6_761"); test_dbl_variable_or_identity( - libff::G1::zero(), libff::G1::zero()); + libff::G1::zero(), + libff::G1::zero(), + "test_dbl_var_or_id_Z_g1_bw6_761"); } TEST(TestCurveGadgets, G2DblVarOrIdentityGadget) @@ -452,10 +487,13 @@ TEST(TestCurveGadgets, G2DblVarOrIdentityGadget) test_dbl_variable_or_identity( libff::Fr(13) * libff::G2::one(), - libff::Fr(13 + 13) * libff::G2::one()); + libff::Fr(13 + 13) * libff::G2::one(), + "test_dbl_var_or_id_N_g2_bw6_761"); test_dbl_variable_or_identity( - libff::G2::zero(), libff::G2::zero()); + libff::G2::zero(), + libff::G2::zero(), + "test_dbl_var_or_id_Z_g2_bw6_761"); } TEST(TestCurveGadgets, G1MulByConstScalar) @@ -469,10 +507,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"); @@ -644,7 +682,8 @@ template< typename scalarMulGadgetT> void test_mul_by_scalar_gadget( const libff::Fr> &base_scalar, - const libff::Fr> &scalar) + const libff::Fr> &scalar, + const std::string &test_name) { using nFr = libff::Fr>; @@ -697,10 +736,10 @@ void test_mul_by_scalar_gadget( // Check circuit satisfaction and proof generation. ASSERT_TRUE(pb.is_satisfied()); - generate_and_check_proof(pb); + generate_and_check_proof(pb, test_name); } -TEST(TestCurveGadgets, G1MulScalarVar) +TEST(TestCurveGadgets, MulScalarVar) { auto test_g1_mul_by_scalar_gadget = test_mul_by_scalar_gadget< wpp, @@ -716,13 +755,84 @@ TEST(TestCurveGadgets, G1MulScalarVar) G2_variable_or_identity, G2_mul_by_scalar_gadget>; - test_g1_mul_by_scalar_gadget(libff::Fr(13), libff::Fr::zero()); - test_g1_mul_by_scalar_gadget(libff::Fr(13), libff::Fr(127)); - test_g1_mul_by_scalar_gadget(libff::Fr(13), -libff::Fr::one()); - - test_g2_mul_by_scalar_gadget(libff::Fr(13), libff::Fr::zero()); - test_g2_mul_by_scalar_gadget(libff::Fr(13), libff::Fr(127)); - test_g2_mul_by_scalar_gadget(libff::Fr(13), -libff::Fr::one()); + test_g1_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr::zero(), + "test_mul_scalar_var_Z_g1_bw6_761"); + test_g1_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr(127), + "test_mul_scalar_var_N_g1_bw6_761"); + test_g1_mul_by_scalar_gadget( + libff::Fr(13), + -libff::Fr::one(), + "test_mul_scalar_var_minus_1_g1_bw6_761"); + + test_g2_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr::zero(), + "test_mul_scalar_var_Z_g2_bw6_761"); + test_g2_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr(127), + "test_mul_scalar_var_N_g2_bw6_761"); + test_g2_mul_by_scalar_gadget( + libff::Fr(13), + -libff::Fr::one(), + "test_mul_scalar_var_minus_1_g2_bw6_761"); +} + +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(), + "G1_var_or_identity_mul_by_scalar_gadget (0*[13]_1)"); + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr::zero(), + libff::Fr(13), + "G1_var_or_identity_mul_by_scalar_gadget (13*[0]_1)"); + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr(127), + "G1_var_or_identity_mul_by_scalar_gadget (127*[13]_1)"); + test_g1_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), + -libff::Fr::one(), + "G1_var_or_identity_mul_by_scalar_gadget (-1*[13]_1)"); + + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr::zero(), + "G2_var_or_identity_mul_by_scalar_gadget (0*[13]_2)"); + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr::zero(), + libff::Fr(13), + "G2_var_or_identity_mul_by_scalar_gadget (13*[0]_2)"); + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), + libff::Fr(127), + "G2_var_or_identity_mul_by_scalar_gadget (127*[13]_2)"); + test_g2_var_or_identity_mul_by_scalar_gadget( + libff::Fr(13), + -libff::Fr::one(), + "G2_var_or_identity_mul_by_scalar_gadget (-1*[13]_2)"); } } // namespace @@ -733,6 +843,8 @@ int main(int argc, char **argv) libff::bw6_761_pp::init_public_params(); libff::mnt4_pp::init_public_params(); libff::mnt6_pp::init_public_params(); + libff::inhibit_profiling_info = true; + libff::inhibit_profiling_counters = true; ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp b/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp index 85c0e66e6..159368d55 100644 --- a/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp +++ b/libsnark/gadgetlib1/tests/test_kzg10_verifier_gadget.cpp @@ -6,9 +6,11 @@ * @copyright MIT license (see LICENSE file) *****************************************************************************/ +#include "libsnark/common/constraints_tracker/constraints_tracker.hpp" #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" @@ -25,6 +27,8 @@ static const size_t POLYNOMIAL_SIZE = 5; namespace { +static constraints_tracker constraints; + template void test_polynomial_commitment_verifier_gadget( const typename scheme::srs &srs, @@ -32,7 +36,8 @@ void test_polynomial_commitment_verifier_gadget( const libff::Fr> &i, const libff::Fr> &evaluation, const typename kzg10>::evaluation_witness &eval_witness, - const bool expected_result) + const bool expected_result, + const std::string &test_name) { using Field = libff::Fr; using npp = other_curve; @@ -82,6 +87,9 @@ void test_polynomial_commitment_verifier_gadget( witness_var.generate_r1cs_witness(eval_witness); verifier_gadget.generate_r1cs_witness(); + constraints.add_measurement( + "kzg10_verifier_gadget - " + test_name, pb.num_constraints()); + // Check some members of verifier_gadget { const libff::G2 i_in_G2_val = i * libff::G2::one(); @@ -134,7 +142,7 @@ template void test_kzg10_verifier_gadget() ASSERT_TRUE(scheme::verify_evaluation(i, evaluation, srs, eval_witness, C)); test_polynomial_commitment_verifier_gadget( - srs, C, i, evaluation, eval_witness, true); + srs, C, i, evaluation, eval_witness, true, "valid"); // Test some failure cases: @@ -142,16 +150,516 @@ template void test_kzg10_verifier_gadget() { // Invalid evaluation point test_polynomial_commitment_verifier_gadget( - srs, C, i + 1, evaluation, eval_witness, false); + srs, + C, + i + 1, + evaluation, + eval_witness, + false, + "invalid_eval_point"); // Invalid evaluation test_polynomial_commitment_verifier_gadget( - srs, C, i, evaluation + 1, eval_witness, false); + srs, C, i, evaluation + 1, eval_witness, false, "invalid_eval"); // Invalid evaluation witness test_polynomial_commitment_verifier_gadget( - srs, C, i, evaluation, eval_witness + eval_witness, false); + srs, + C, + i, + evaluation, + eval_witness + eval_witness, + false, + "invalid_witness"); // Invalid commitment test_polynomial_commitment_verifier_gadget( - srs, C + C, i, evaluation, eval_witness, false); + srs, C + C, i, evaluation, eval_witness, false, "invalid_commit"); + } +} + +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, + const std::string &test_name) +{ + 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(); + + constraints.add_measurement( + "kzg10_batched_compute_gamma_powers_commit_minus_eval_sum - " + + test_name, + pb.num_constraints()); + + // 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, + "n=3"); + + // 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, "n=4"); +} + +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, + const std::string &test_name) +{ + 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(); + + constraints.add_measurement( + "kzg10_batched_verifier_gadget - " + test_name, pb.num_constraints()); + + // 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, + "valid"); + + // 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, + "invalid_z1"); + + 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, + "invalid_z2"); + + 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, + "invalid_gamma_1"); + + 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, + "invalid_gamma_2"); + + 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, + "invalid_cm_1s"); + + 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, + "invalid_cm_2s"); } } @@ -160,6 +668,17 @@ 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/gadgetlib1/tests/test_pairing.cpp b/libsnark/gadgetlib1/tests/test_pairing.cpp index 7487dfd31..094a18222 100644 --- a/libsnark/gadgetlib1/tests/test_pairing.cpp +++ b/libsnark/gadgetlib1/tests/test_pairing.cpp @@ -6,6 +6,7 @@ * @copyright MIT license (see LICENSE file) *****************************************************************************/ +#include "libsnark/common/constraints_tracker/constraints_tracker.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bw6_761_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/mnt/mnt_pairing_params.hpp" #include "libsnark/gadgetlib1/protoboard.hpp" @@ -19,6 +20,8 @@ namespace libsnark { +static constraints_tracker constraints; + template void test_G1_variable_precomp(const std::string &annotation) { @@ -47,10 +50,8 @@ void test_G1_variable_precomp(const std::string &annotation) const_precomp.PY_twist_squared->get_element(), native_precomp.PY_twist_squared); - printf( - "number of constraints for G1 precomp (Fr is %s) = %zu\n", - annotation.c_str(), - pb.num_constraints()); + constraints.add_measurement( + "G1 precomp - " + annotation, pb.num_constraints()); } template @@ -91,10 +92,8 @@ void test_G2_variable_precomp(const std::string &annotation) native_precomp.coeffs[i].gamma_X); } - printf( - "number of constraints for G2 precomp (Fr is %s) = %zu\n", - annotation.c_str(), - pb.num_constraints()); + constraints.add_measurement( + "G2 precomp - " + annotation, pb.num_constraints()); } template @@ -137,13 +136,12 @@ void test_miller_loop( Q.generate_r1cs_witness(Q_val); compute_prec_Q.generate_r1cs_witness(); miller.generate_r1cs_witness(); - ASSERT_TRUE(pb.is_satisfied()); + constraints.add_measurement( + "Miller loop + precomp - " + annotation, pb.num_constraints()); + + ASSERT_TRUE(pb.is_satisfied()); ASSERT_EQ(expect_result, result.get_element()); - printf( - "number of constraints for Miller loop (Fr is %s) = %zu\n", - annotation.c_str(), - pb.num_constraints()); } template void test_mnt_miller_loop(const std::string &annotation) @@ -244,13 +242,10 @@ void test_e_over_e_miller_loop( compute_prec_Q2.generate_r1cs_witness(); miller.generate_r1cs_witness(); - ASSERT_TRUE(pb.is_satisfied()); - - printf( - "number of constraints for e over e Miller loop (Fr is %s) = %zu\n", - annotation.c_str(), - pb.num_constraints()); + constraints.add_measurement( + "e_over_e_miller_loop + precomp - " + annotation, pb.num_constraints()); + ASSERT_TRUE(pb.is_satisfied()); ASSERT_EQ(expect_result, result.get_element()); } @@ -441,12 +436,11 @@ void test_mnt_e_times_e_over_e_miller_loop(const std::string &annotation) native_prec_P3, native_prec_Q3) .inverse()); - ASSERT_EQ(native_result, result.get_element()); - printf( - "number of constraints for e times e over e Miller loop (Fr is %s) = " - "%zu\n", - annotation.c_str(), + constraints.add_measurement( + "e_times_e_over_e_Miller_loop + precomp - " + annotation, pb.num_constraints()); + + ASSERT_EQ(native_result, result.get_element()); } template @@ -551,14 +545,11 @@ void test_e_times_e_times_e_over_e_miller_loop( compute_prec_Q4.generate_r1cs_witness(); miller.generate_r1cs_witness(); - ASSERT_TRUE(pb.is_satisfied()); - - printf( - "number of constraints for e times e times e over e Miller loop (Fr is " - "%s) = %zu\n", - annotation.c_str(), + constraints.add_measurement( + "e_times_e_times_e_over_e Miller loop + precomp - " + annotation, pb.num_constraints()); + ASSERT_TRUE(pb.is_satisfied()); ASSERT_EQ(expect_result, result.get_element()); } diff --git a/libsnark/gadgetlib1/tests/test_pairing_checks.cpp b/libsnark/gadgetlib1/tests/test_pairing_checks.cpp index b127ccf30..f9a25d0f0 100644 --- a/libsnark/gadgetlib1/tests/test_pairing_checks.cpp +++ b/libsnark/gadgetlib1/tests/test_pairing_checks.cpp @@ -6,6 +6,7 @@ * @copyright MIT license (see LICENSE file) *****************************************************************************/ +#include "libsnark/common/constraints_tracker/constraints_tracker.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bw6_761_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/mnt/mnt_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp" @@ -19,6 +20,87 @@ namespace libsnark { +static constraints_tracker constraints; + +/// Carry out a pairing check between elements of G1 and G2 defined over +/// other_curve, in a circuit defined over Fr. +template +bool test_check_e_equals_e_gadget( + libff::G1> l_P, + libff::G2> l_Q, + libff::G1> r_P, + libff::G2> r_Q, + libff::Fr expected_result) +{ + protoboard> pb; + G1_variable lhs_P(pb, "lhs_P"); + G2_variable lhs_Q(pb, "lhs_Q"); + G1_variable rhs_P(pb, "rhs_P"); + G2_variable rhs_Q(pb, "rhs_Q"); + + G1_precomputation lhs_prec_P; + precompute_G1_gadget compute_lhs_prec_P( + pb, lhs_P, lhs_prec_P, "compute_lhs_prec_P"); + G2_precomputation lhs_prec_Q; + precompute_G2_gadget compute_lhs_prec_Q( + pb, lhs_Q, lhs_prec_Q, "compute_lhs_prec_Q"); + + G1_precomputation rhs_prec_P; + precompute_G1_gadget compute_rhs_prec_P( + pb, rhs_P, rhs_prec_P, "compute_rhs_prec1_P"); + G2_precomputation rhs_prec_Q; + precompute_G2_gadget compute_rhs_prec_Q( + pb, rhs_Q, rhs_prec_Q, "compute_rhs_prec1_Q"); + + pb_variable> result; + result.allocate(pb, "result"); + + check_e_equals_e_gadget pairing_check( + pb, + lhs_prec_P, + lhs_prec_Q, + rhs_prec_P, + rhs_prec_Q, + result, + "pairing_check"); + + PROFILE_CONSTRAINTS(pb, "precompute P") + { + compute_lhs_prec_P.generate_r1cs_constraints(); + compute_rhs_prec_P.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "precompute Q") + { + compute_lhs_prec_Q.generate_r1cs_constraints(); + compute_rhs_prec_Q.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "Pairing check") + { + pairing_check.generate_r1cs_constraints(); + } + PRINT_CONSTRAINT_PROFILING(); + + generate_r1cs_equals_const_constraint>( + pb, result, expected_result, "result"); + + lhs_P.generate_r1cs_witness(l_P); + compute_lhs_prec_P.generate_r1cs_witness(); + lhs_Q.generate_r1cs_witness(l_Q); + compute_lhs_prec_Q.generate_r1cs_witness(); + + rhs_P.generate_r1cs_witness(r_P); + compute_rhs_prec_P.generate_r1cs_witness(); + rhs_Q.generate_r1cs_witness(r_Q); + compute_rhs_prec_Q.generate_r1cs_witness(); + + pairing_check.generate_r1cs_witness(); + + constraints.add_measurement( + "check_e_equals_e_gadget", pb.num_constraints()); + + return pb.is_satisfied() && (pb.val(result) == expected_result); +} + /// In this test we carry out - via a circuit defined over Fr - a pairing /// check between elements of G1 and G2 defined over other_curve template @@ -166,15 +248,62 @@ bool test_check_e_equals_eee_gadget( pairing_check.generate_r1cs_witness(); - assert(pb.is_satisfied()); - printf( - "number of constraints for check_e_equals_eee_gadget (Fr is " - "%s) = %zu\n", - annotation_prefix.c_str(), + constraints.add_measurement( + "check_e_equals_eee_gadget - " + annotation_prefix, pb.num_constraints()); - bool test_success = (pb.val(result) == expected_result); - return test_success; + return pb.is_satisfied() && (pb.val(result) == expected_result); +} + +template void test_valid_pairing_check_e_equals_e_gadget() +{ + const libff::G1> G1_base = + libff::G1>::one(); + const libff::G2> G2_base = + libff::G2>::one(); + + const libff::Fr> rhs_scalar1 = + libff::Fr>::random_element(); + const libff::Fr> rhs_scalar2 = + libff::Fr>::random_element(); + + const libff::G1> rhs_pairing_P = rhs_scalar1 * G1_base; + const libff::G2> rhs_pairing_Q = rhs_scalar2 * G2_base; + + // Set the LHS group elements such that the pairing check passes + const libff::G1> lhs_pairing_P = + (rhs_scalar1 * rhs_scalar2) * G1_base; + const libff::G2> lhs_pairing_Q = G2_base; + + // Compute pairings "outside the circuit" to check the value of the LHS + // against the value of the RHS, and see if the pairing check + // is succesfull + libff::GT> expected_pairing_lhs = + other_curve::final_exponentiation(other_curve::miller_loop( + other_curve::precompute_G1(lhs_pairing_P), + other_curve::precompute_G2(lhs_pairing_Q))); + + libff::GT> expected_pairing_rhs = + other_curve::final_exponentiation(other_curve::miller_loop( + other_curve::precompute_G1(rhs_pairing_P), + other_curve::precompute_G2(rhs_pairing_Q))); + + // Make sure that the pairing check succeeds and the gadget is tested + // with the right expected value + const bool check_result = (expected_pairing_lhs == expected_pairing_rhs); + ASSERT_TRUE(check_result); + + // Set the value of the expected value of the "output wire" + // of the pairing check gadget. + const libff::Fr expected_result = + check_result ? libff::Fr::one() : libff::Fr::zero(); + bool res = test_check_e_equals_e_gadget( + lhs_pairing_P, + lhs_pairing_Q, + rhs_pairing_P, + rhs_pairing_Q, + expected_result); + ASSERT_TRUE(res); } /// Create VALID test case by instantiating points from G1 and G2 @@ -265,7 +394,7 @@ template void test_valid_pairing_check_e_equals_eee_gadget() rhs_pairing3_P, rhs_pairing3_Q, expected_result, - " test_check_e_equals_eee_gadget"); + "valid"); // Check that the pairing check circuit returns the same result as // the one carried out "outside" the circuit (see above) @@ -358,13 +487,24 @@ template void test_invalid_pairing_check_e_equals_eee_gadget() rhs_pairing3_P, rhs_pairing3_Q, expected_result, - " test_check_e_equals_eee_gadget"); + "invalid"); // Check that the pairing check circuit returns the same result as // the one carried out "outside" the circuit (see above) ASSERT_TRUE(res); } +TEST(MainTests, TestMntValidCheckEequalsEgadget) +{ + test_valid_pairing_check_e_equals_e_gadget(); + test_valid_pairing_check_e_equals_e_gadget(); +} + +TEST(MainTests, TestBlsValidCheckEequalsEgadget) +{ + test_valid_pairing_check_e_equals_e_gadget(); +} + TEST(MainTests, TestMntValidCheckEequalsEEEgadget) { test_valid_pairing_check_e_equals_eee_gadget(); @@ -395,6 +535,8 @@ int main(int argc, char **argv) libff::mnt6_pp::init_public_params(); libff::bw6_761_pp::init_public_params(); libff::bls12_377_pp::init_public_params(); + libff::inhibit_profiling_info = true; + libff::inhibit_profiling_counters = true; ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/libsnark/gadgetlib1/tests/test_r1cs_gg_ppzksnark_verifier_gadget.cpp b/libsnark/gadgetlib1/tests/test_r1cs_gg_ppzksnark_verifier_gadget.cpp index 211145853..f572f6c3c 100644 --- a/libsnark/gadgetlib1/tests/test_r1cs_gg_ppzksnark_verifier_gadget.cpp +++ b/libsnark/gadgetlib1/tests/test_r1cs_gg_ppzksnark_verifier_gadget.cpp @@ -6,6 +6,7 @@ * @copyright MIT license (see LICENSE file) *****************************************************************************/ +#include "libsnark/common/constraints_tracker/constraints_tracker.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/bw6_761_bls12_377/bw6_761_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/pairing/mnt/mnt_pairing_params.hpp" #include "libsnark/gadgetlib1/gadgets/verifiers/r1cs_gg_ppzksnark_verifier_gadget.hpp" @@ -21,11 +22,11 @@ using namespace libsnark; namespace { +static constraints_tracker constraints; + /// This test generates a valid proof and checks that this valid proof /// is succesfully verified by the groth16 verifier gadget -template -void test_verifier( - const std::string &annotation_A, const std::string &annotation_B) +template void test_verifier() { using FieldT_A = libff::Fr; using FieldT_B = libff::Fr; @@ -101,17 +102,12 @@ void test_verifier( std::cout << "Negative test case" << std::endl; ASSERT_FALSE(pb.is_satisfied()); PRINT_CONSTRAINT_PROFILING(); - printf( - "number of constraints for verifier: %zu (verifier is implemented in " - "%s constraints and verifies %s proofs))\n", - pb.num_constraints(), - annotation_B.c_str(), - annotation_A.c_str()); + + constraints.add_measurement( + "r1cs_gg_ppzksnark_verifier - " + ppT_A::name, pb.num_constraints()); } -template -void test_hardcoded_verifier( - const std::string &annotation_A, const std::string &annotation_B) +template void test_hardcoded_verifier() { using FieldT_A = libff::Fr; using FieldT_B = libff::Fr; @@ -189,30 +185,26 @@ void test_hardcoded_verifier( printf("Negative test:\n"); ASSERT_FALSE(pb.is_satisfied()); PRINT_CONSTRAINT_PROFILING(); - printf( - "number of constraints for verifier: %zu (verifier is implemented in " - "%s constraints and verifies %s proofs))\n", - pb.num_constraints(), - annotation_B.c_str(), - annotation_A.c_str()); + + constraints.add_measurement( + "r1cs_gg_ppzksnark_verifier - hardcoded - " + ppT_A::name, + pb.num_constraints()); } TEST(Groth16VerifierGadgetTests, MntGroth16VerifierGadget) { - test_verifier("mnt4", "mnt6"); - test_verifier("mnt6", "mnt4"); + test_verifier(); + test_verifier(); - test_hardcoded_verifier("mnt4", "mnt6"); - test_hardcoded_verifier("mnt6", "mnt4"); + test_hardcoded_verifier(); + test_hardcoded_verifier(); } TEST(Groth16VerifierGadgetTests, BlsGroth16VerifierGadget) { - test_verifier( - "bls12-377", "bw6-761"); + test_verifier(); - test_hardcoded_verifier( - "bls12-377", "bw6-761"); + test_hardcoded_verifier(); } } // namespace 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: //