From 1b2283c2e736318e53376b28f329ec97d0d3c097 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:09:27 -0700 Subject: [PATCH 1/8] change SE SNARK verification key to contain e(G_alpha, H_beta) --- .../r1cs_se_ppzksnark/r1cs_se_ppzksnark.hpp | 15 +++++++++++++-- .../r1cs_se_ppzksnark/r1cs_se_ppzksnark.tcc | 16 ++++++++++------ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.hpp b/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.hpp index 50bacf44..c8116b4c 100644 --- a/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.hpp +++ b/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.hpp @@ -194,6 +194,9 @@ class r1cs_se_ppzksnark_verification_key { // H^{gamma} libff::G2 H_gamma; + // e (G^{alpha}, H^{beta}) + libff::Fqk G_alpha_H_beta; + // G^{gamma * A_i(t) + (alpha + beta) * A_i(t)} // for 0 <= i <= sap.num_inputs() libff::G1_vector query; @@ -210,7 +213,8 @@ class r1cs_se_ppzksnark_verification_key { H_beta(H_beta), G_gamma(G_gamma), H_gamma(H_gamma), - query(std::move(query)) + query(std::move(query)), + G_alpha_H_beta(ppT::reduced_pairing(G_alpha, H_beta)) {}; size_t G1_size() const @@ -223,10 +227,17 @@ class r1cs_se_ppzksnark_verification_key { return 3; } + size_t GT_size() const + { + return 1; + } + size_t size_in_bits() const { return (G1_size() * libff::G1::size_in_bits() + G2_size() * libff::G2::size_in_bits()); + // TODO: This depends on a change in libff: + // + GT_size() * libff::Fqk::size_in_bits() } void print_size() const @@ -269,7 +280,7 @@ class r1cs_se_ppzksnark_processed_verification_key { public: libff::G1 G_alpha; libff::G2 H_beta; - libff::Fqk G_alpha_H_beta_ml; + libff::Fqk G_alpha_H_beta; libff::G1_precomp G_gamma_pc; libff::G2_precomp H_gamma_pc; libff::G2_precomp H_pc; diff --git a/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.tcc b/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.tcc index 1e4a22f8..35866f77 100644 --- a/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.tcc +++ b/libsnark/zk_proof_systems/ppzksnark/r1cs_se_ppzksnark/r1cs_se_ppzksnark.tcc @@ -101,6 +101,7 @@ std::ostream& operator<<(std::ostream &out, const r1cs_se_ppzksnark_verification out << vk.H_beta << OUTPUT_NEWLINE; out << vk.G_gamma << OUTPUT_NEWLINE; out << vk.H_gamma << OUTPUT_NEWLINE; + out << vk.G_alpha_H_beta << OUTPUT_NEWLINE; out << vk.query << OUTPUT_NEWLINE; return out; @@ -119,6 +120,8 @@ std::istream& operator>>(std::istream &in, r1cs_se_ppzksnark_verification_key> vk.H_gamma; libff::consume_OUTPUT_NEWLINE(in); + in >> vk.G_alpha_H_beta; + libff::consume_OUTPUT_NEWLINE(in); in >> vk.query; libff::consume_OUTPUT_NEWLINE(in); @@ -130,7 +133,7 @@ bool r1cs_se_ppzksnark_processed_verification_key::operator==(const r1cs_se { return (this->G_alpha == other.G_alpha && this->H_beta == other.H_beta && - this->G_alpha_H_beta_ml == other.G_alpha_H_beta_ml && + this->G_alpha_H_beta == other.G_alpha_H_beta && this->G_gamma_pc == other.G_gamma_pc && this->H_gamma_pc == other.H_gamma_pc && this->H_pc == other.H_pc && @@ -142,7 +145,7 @@ std::ostream& operator<<(std::ostream &out, const r1cs_se_ppzksnark_processed_ve { out << pvk.G_alpha << OUTPUT_NEWLINE; out << pvk.H_beta << OUTPUT_NEWLINE; - out << pvk.G_alpha_H_beta_ml << OUTPUT_NEWLINE; + out << pvk.G_alpha_H_beta << OUTPUT_NEWLINE; out << pvk.G_gamma_pc << OUTPUT_NEWLINE; out << pvk.H_gamma_pc << OUTPUT_NEWLINE; out << pvk.H_pc << OUTPUT_NEWLINE; @@ -158,7 +161,7 @@ std::istream& operator>>(std::istream &in, r1cs_se_ppzksnark_processed_verificat libff::consume_OUTPUT_NEWLINE(in); in >> pvk.H_beta; libff::consume_OUTPUT_NEWLINE(in); - in >> pvk.G_alpha_H_beta_ml; + in >> pvk.G_alpha_H_beta; libff::consume_OUTPUT_NEWLINE(in); in >> pvk.G_gamma_pc; libff::consume_OUTPUT_NEWLINE(in); @@ -212,6 +215,7 @@ r1cs_se_ppzksnark_verification_key r1cs_se_ppzksnark_verification_key: result.H_beta = libff::Fr::random_element() * libff::G2::one(); result.G_gamma = libff::Fr::random_element() * libff::G1::one(); result.H_gamma = libff::Fr::random_element() * libff::G2::one(); + result.G_alpha_H_beta = ppT::reduced_pairing(result.G_alpha, result.H_beta); libff::G1_vector v; for (size_t i = 0; i < input_size + 1; ++i) @@ -582,7 +586,7 @@ r1cs_se_ppzksnark_processed_verification_key r1cs_se_ppzksnark_verifier_pro r1cs_se_ppzksnark_processed_verification_key pvk; pvk.G_alpha = vk.G_alpha; pvk.H_beta = vk.H_beta; - pvk.G_alpha_H_beta_ml = ppT::miller_loop(G_alpha_pc, H_beta_pc); + pvk.G_alpha_H_beta = ppT::final_exponentiation(ppT::miller_loop(G_alpha_pc, H_beta_pc)); pvk.G_gamma_pc = ppT::precompute_G1(vk.G_gamma); pvk.H_gamma_pc = ppT::precompute_G2(vk.H_gamma); pvk.H_pc = ppT::precompute_G2(vk.H); @@ -638,13 +642,13 @@ bool r1cs_se_ppzksnark_online_verifier_weak_IC(const r1cs_se_ppzksnark_processed libff::Fqk test1_l = ppT::miller_loop(ppT::precompute_G1(proof.A + pvk.G_alpha), ppT::precompute_G2(proof.B + pvk.H_beta)), - test1_r1 = pvk.G_alpha_H_beta_ml, + test1_r1 = pvk.G_alpha_H_beta, test1_r2 = ppT::miller_loop(ppT::precompute_G1(G_psi), pvk.H_gamma_pc), test1_r3 = ppT::miller_loop(ppT::precompute_G1(proof.C), pvk.H_pc); libff::GT test1 = ppT::final_exponentiation( - test1_l.unitary_inverse() * test1_r1 * test1_r2 * test1_r3); + test1_l.unitary_inverse() * test1_r2 * test1_r3) * test1_r1; if (test1 != libff::GT::one()) { From 02f80319e8277436b368516f13287e3172d4a78d Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:10:14 -0700 Subject: [PATCH 2/8] add a gadget for checking the equality of a vector of field elements --- libsnark/gadgetlib1/gadgets/basic_gadgets.hpp | 20 +++++++++ libsnark/gadgetlib1/gadgets/basic_gadgets.tcc | 44 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp b/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp index 6bb07bca..9d4b5c11 100644 --- a/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp +++ b/libsnark/gadgetlib1/gadgets/basic_gadgets.hpp @@ -218,6 +218,26 @@ class conjunction_gadget : public gadget { template void test_conjunction_gadget(const size_t n); + template +class field_vector_equals_gadget : public gadget { +public: + const pb_linear_combination_array X; + const pb_linear_combination_array Y; + const pb_variable result; + + pb_variable_array results; // boolean + pb_variable_array invs; + + std::shared_ptr> all_equal; + + field_vector_equals_gadget(protoboard &pb, + const pb_linear_combination_array &X, + const pb_linear_combination_array &Y, + const pb_variable &result, + const std::string &annotation_prefix=""); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + }; template class comparison_gadget : public gadget { private: diff --git a/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc b/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc index fdda31f3..083079fc 100644 --- a/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc +++ b/libsnark/gadgetlib1/gadgets/basic_gadgets.tcc @@ -110,7 +110,51 @@ size_t multipacking_num_chunks(const size_t num_bits) { return libff::div_ceil(num_bits, FieldT::capacity()); } +template +field_vector_equals_gadget::field_vector_equals_gadget( + protoboard &pb, + const pb_linear_combination_array &X, + const pb_linear_combination_array &Y, + const pb_variable &result, + const std::string &annotation_prefix) : +gadget(pb, annotation_prefix), X(X), Y(Y), result(result) +{ + assert(X.size() == Y.size()); + invs.allocate(pb, X.size(), FMT(this->annotation_prefix, " invs")); + results.allocate(pb, X.size(), FMT(this->annotation_prefix, " results")); + all_equal.reset(new conjunction_gadget(pb, results, result, FMT(this->annotation_prefix, " all_equal"))); +} + +template +void field_vector_equals_gadget::generate_r1cs_constraints() +{ + for (size_t i = 0; i < X.size(); ++i) + { + // If X[i] != Y[i], then results[i] = 0 + this->pb.add_r1cs_constraint(r1cs_constraint(results[i], X[i] - Y[i], 0), + FMT(this->annotation_prefix, " vector_equals_check_1_%zu", i)); + // If X[i] = Y[i], then this constraint forces + // 0 = 1 - results[i] + // so results[i] = 1. + this->pb.add_r1cs_constraint(r1cs_constraint(invs[i], X[i] - Y[i], 1 - results[i]), + FMT(this->annotation_prefix, " vector_equals_check_2_%zu", i)); + } + + all_equal->generate_r1cs_constraints(); +} +template +void field_vector_equals_gadget::generate_r1cs_witness() +{ + for (size_t i = 0; i < X.size(); ++i) { + FieldT x = this->pb.lc_val(X[i]); + FieldT y = this->pb.lc_val(Y[i]); + bool equal = x == y; + this->pb.val(results[i]) = equal ? FieldT::one() : FieldT::zero(); + this->pb.val(invs[i]) = equal ? FieldT::zero() : (x - y).inverse(); + } + all_equal->generate_r1cs_witness(); +} template field_vector_copy_gadget::field_vector_copy_gadget(protoboard &pb, const pb_variable_array &source, From 924a95cd9757d372f3eb3f35635670ab4bdb4bf9 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:11:06 -0700 Subject: [PATCH 3/8] some simple modifications to the field gadgets --- .../gadgetlib1/gadgets/fields/fp2_gadgets.hpp | 2 + .../gadgetlib1/gadgets/fields/fp2_gadgets.tcc | 17 +++- .../gadgetlib1/gadgets/fields/fp3_gadgets.hpp | 2 + .../gadgetlib1/gadgets/fields/fp3_gadgets.tcc | 18 +++- .../gadgetlib1/gadgets/fields/fp4_gadgets.hpp | 10 ++ .../gadgetlib1/gadgets/fields/fp4_gadgets.tcc | 76 ++++++++++++++ .../gadgetlib1/gadgets/fields/fp6_gadgets.hpp | 13 ++- .../gadgetlib1/gadgets/fields/fp6_gadgets.tcc | 99 +++++++++++++++++++ 8 files changed, 234 insertions(+), 3 deletions(-) diff --git a/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.hpp b/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.hpp index 24f79d7a..1da29acf 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.hpp +++ b/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.hpp @@ -55,6 +55,7 @@ class Fp2_variable : public gadget { Fp2_variable operator*(const FieldT &coeff) const; Fp2_variable operator+(const Fp2_variable &other) const; Fp2_variable operator+(const Fp2T &other) const; + Fp2_variable operator-(const Fp2_variable &other) const; Fp2_variable mul_by_X() const; void evaluate() const; bool is_constant() const; @@ -83,6 +84,7 @@ class Fp2_mul_gadget : public gadget { const Fp2_variable &result, const std::string &annotation_prefix); void generate_r1cs_constraints(); + void generate_r1cs_witness_internal(); void generate_r1cs_witness(); }; diff --git a/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.tcc b/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.tcc index 2b080c2a..3fb35b94 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.tcc +++ b/libsnark/gadgetlib1/gadgets/fields/fp2_gadgets.tcc @@ -116,6 +116,15 @@ Fp2_variable Fp2_variable::operator+(const Fp2_variable &other return Fp2_variable(this->pb, new_c0, new_c1, FMT(this->annotation_prefix, " operator+")); } +template +Fp2_variable Fp2_variable::operator-(const Fp2_variable &other) const +{ + pb_linear_combination new_c0, new_c1; + new_c0.assign(this->pb, this->c0 - other.c0); + new_c1.assign(this->pb, this->c1 - other.c1); + return Fp2_variable(this->pb, new_c0, new_c1, FMT(this->annotation_prefix, " operator-")); +} + template Fp2_variable Fp2_variable::operator+(const Fp2T &other) const { @@ -198,11 +207,17 @@ void Fp2_mul_gadget::generate_r1cs_constraints() FMT(this->annotation_prefix, " result.c1")); } +template +void Fp2_mul_gadget::generate_r1cs_witness_internal() +{ + this->pb.val(v1) = this->pb.lc_val(A.c1) * this->pb.lc_val(B.c1); +} + template void Fp2_mul_gadget::generate_r1cs_witness() { const FieldT aA = this->pb.lc_val(A.c0) * this->pb.lc_val(B.c0); - this->pb.val(v1) = this->pb.lc_val(A.c1) * this->pb.lc_val(B.c1); + this->generate_r1cs_witness_internal(); this->pb.lc_val(result.c0) = aA + Fp2T::non_residue * this->pb.val(v1); this->pb.lc_val(result.c1) = (this->pb.lc_val(A.c0) + this->pb.lc_val(A.c1)) * (this->pb.lc_val(B.c0) + this->pb.lc_val(B.c1)) - aA - this->pb.lc_val(v1); } diff --git a/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.hpp b/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.hpp index 24c3d66a..91791d3d 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.hpp +++ b/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.hpp @@ -57,6 +57,7 @@ class Fp3_variable : public gadget { Fp3_variable operator*(const FieldT &coeff) const; Fp3_variable operator+(const Fp3_variable &other) const; Fp3_variable operator+(const Fp3T &other) const; + Fp3_variable operator-(const Fp3_variable &other) const; Fp3_variable mul_by_X() const; void evaluate() const; bool is_constant() const; @@ -86,6 +87,7 @@ class Fp3_mul_gadget : public gadget { const Fp3_variable &result, const std::string &annotation_prefix); void generate_r1cs_constraints(); + void generate_r1cs_witness_internal(); void generate_r1cs_witness(); }; diff --git a/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.tcc b/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.tcc index 22b1df5a..e68fda84 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.tcc +++ b/libsnark/gadgetlib1/gadgets/fields/fp3_gadgets.tcc @@ -132,6 +132,16 @@ Fp3_variable Fp3_variable::operator+(const Fp3_variable &other return Fp3_variable(this->pb, new_c0, new_c1, new_c2, FMT(this->annotation_prefix, " operator+")); } +template +Fp3_variable Fp3_variable::operator-(const Fp3_variable &other) const +{ + pb_linear_combination new_c0, new_c1, new_c2; + new_c0.assign(this->pb, this->c0 - other.c0); + new_c1.assign(this->pb, this->c1 - other.c1); + new_c2.assign(this->pb, this->c2 - other.c2); + return Fp3_variable(this->pb, new_c0, new_c1, new_c2, FMT(this->annotation_prefix, " operator-")); +} + template Fp3_variable Fp3_variable::operator+(const Fp3T &other) const { @@ -248,10 +258,16 @@ void Fp3_mul_gadget::generate_r1cs_constraints() } template -void Fp3_mul_gadget::generate_r1cs_witness() +void Fp3_mul_gadget::generate_r1cs_witness_internal() { this->pb.val(v0) = this->pb.lc_val(A.c0) * this->pb.lc_val(B.c0); this->pb.val(v4) = this->pb.lc_val(A.c2) * this->pb.lc_val(B.c2); +} + +template +void Fp3_mul_gadget::generate_r1cs_witness() +{ + this->generate_r1cs_witness_internal(); const Fp3T Aval = A.get_element(); const Fp3T Bval = B.get_element(); diff --git a/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.hpp b/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.hpp index 14a03b67..bea55af5 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.hpp +++ b/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.hpp @@ -32,15 +32,25 @@ class Fp4_variable : public gadget { Fp2_variable c0; Fp2_variable c1; + pb_linear_combination_array all_vars; + + Fp4_variable(protoboard &pb, const std::string &annotation_prefix); Fp4_variable(protoboard &pb, const Fp4T &el, const std::string &annotation_prefix); Fp4_variable(protoboard &pb, const Fp2_variable &c0, const Fp2_variable &c1, const std::string &annotation_prefix); + + void generate_r1cs_equals_constraints(const Fp4_variable &other); + void generate_r1cs_equals_unitary_inverse_constraints(const Fp4_variable &other); void generate_r1cs_equals_const_constraints(const Fp4T &el); void generate_r1cs_witness(const Fp4T &el); Fp4T get_element(); Fp4_variable Frobenius_map(const size_t power) const; void evaluate() const; + + static size_t __attribute__((noinline)) size_in_bits(); + static size_t num_variables(); + }; /** diff --git a/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.tcc b/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.tcc index 5ba1727e..fd749774 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.tcc +++ b/libsnark/gadgetlib1/gadgets/fields/fp4_gadgets.tcc @@ -20,6 +20,11 @@ template Fp4_variable::Fp4_variable(protoboard &pb, const std::string &annotation_prefix) : gadget(pb, annotation_prefix), c0(pb, FMT(annotation_prefix, " c0")), c1(pb, FMT(annotation_prefix, " c1")) { + all_vars.emplace_back(c0.c0); + all_vars.emplace_back(c0.c1); + all_vars.emplace_back(c1.c0); + all_vars.emplace_back(c1.c1); + } template @@ -28,12 +33,71 @@ Fp4_variable::Fp4_variable(protoboard &pb, const std::string &annotation_prefix) : gadget(pb, annotation_prefix), c0(pb, el.c0, FMT(annotation_prefix, " c0")), c1(pb, el.c1, FMT(annotation_prefix, " c1")) { + all_vars.emplace_back(c0.c0); + all_vars.emplace_back(c0.c1); + all_vars.emplace_back(c1.c0); + all_vars.emplace_back(c1.c1); + } template Fp4_variable::Fp4_variable(protoboard &pb, const Fp2_variable &c0, const Fp2_variable &c1, const std::string &annotation_prefix) : gadget(pb, annotation_prefix), c0(c0), c1(c1) { + all_vars.emplace_back(c0.c0); + all_vars.emplace_back(c0.c1); + all_vars.emplace_back(c1.c0); + all_vars.emplace_back(c1.c1); +} + +template +void Fp4_variable::generate_r1cs_equals_constraints( + const Fp4_variable &other) +{ + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c0, other.c0.c0), + FMT(this->annotation_prefix, " c0.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c1, other.c0.c1), + FMT(this->annotation_prefix, " c0.c1")); + + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c1.c0, other.c1.c0), + FMT(this->annotation_prefix, " c1.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c1.c1, other.c1.c1), + FMT(this->annotation_prefix, " c1.c1")); +} + +template +void Fp4_variable::generate_r1cs_equals_unitary_inverse_constraints( + const Fp4_variable &other) +{ + /* + this.c0 = other.c0 + this.c1 = - other.c1 + + iff + + this.c0.c0 = other.c0.c0 + this.c0.c1 = other.c0.c1 + + this.c1.c0 = - other.c1.c0 + this.c1.c1 = - other.c1.c1 + */ + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c0, other.c0.c0), + FMT(this->annotation_prefix, " c0.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c1, other.c0.c1), + FMT(this->annotation_prefix, " c0.c1")); + + this->pb.add_r1cs_constraint( + r1cs_constraint(-1, this->c1.c0, other.c1.c0), + FMT(this->annotation_prefix, " c1.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(-1, this->c1.c1, other.c1.c1), + FMT(this->annotation_prefix, " c1.c1")); } template @@ -59,6 +123,18 @@ Fp4T Fp4_variable::get_element() return el; } +template +size_t Fp4_variable::size_in_bits() +{ + return 4 * FieldT::size_in_bits(); +} + +template +size_t Fp4_variable::num_variables() +{ + return 4; +} + template Fp4_variable Fp4_variable::Frobenius_map(const size_t power) const { diff --git a/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.hpp b/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.hpp index 3cac027f..2865726a 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.hpp +++ b/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.hpp @@ -33,14 +33,25 @@ class Fp6_variable : public gadget { Fp3_variable c0; Fp3_variable c1; + pb_linear_combination_array all_vars; + + Fp6_variable(protoboard &pb, const std::string &annotation_prefix); Fp6_variable(protoboard &pb, const Fp6T &el, const std::string &annotation_prefix); Fp6_variable(protoboard &pb, const Fp3_variable &c0, const Fp3_variable &c1, const std::string &annotation_prefix); - void generate_r1cs_equals_const_constraints(const Fp6T &el); + + void generate_r1cs_equals_constraints(const Fp6_variable &other); + void generate_r1cs_equals_unitary_inverse_constraints(const Fp6_variable &other); + void generate_r1cs_equals_const_constraints(const Fp6T &el); void generate_r1cs_witness(const Fp6T &el); Fp6T get_element(); Fp6_variable Frobenius_map(const size_t power) const; void evaluate() const; + + static size_t __attribute__((noinline)) size_in_bits(); + static size_t num_variables(); + + }; /** diff --git a/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.tcc b/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.tcc index 62cb43b2..d8708591 100644 --- a/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.tcc +++ b/libsnark/gadgetlib1/gadgets/fields/fp6_gadgets.tcc @@ -20,6 +20,13 @@ template Fp6_variable::Fp6_variable(protoboard &pb, const std::string &annotation_prefix) : gadget(pb, annotation_prefix), c0(pb, FMT(annotation_prefix, " c0")), c1(pb, FMT(annotation_prefix, " c1")) { + all_vars.emplace_back(c0.c0); + all_vars.emplace_back(c0.c1); + all_vars.emplace_back(c0.c2); + all_vars.emplace_back(c1.c0); + all_vars.emplace_back(c1.c1); + all_vars.emplace_back(c1.c2); + } template @@ -28,12 +35,90 @@ Fp6_variable::Fp6_variable(protoboard &pb, const std::string &annotation_prefix) : gadget(pb, annotation_prefix), c0(pb, el.c0, FMT(annotation_prefix, " c0")), c1(pb, el.c1, FMT(annotation_prefix, " c1")) { + all_vars.emplace_back(c0.c0); + all_vars.emplace_back(c0.c1); + all_vars.emplace_back(c0.c2); + all_vars.emplace_back(c1.c0); + all_vars.emplace_back(c1.c1); + all_vars.emplace_back(c1.c2); + } template Fp6_variable::Fp6_variable(protoboard &pb, const Fp3_variable &c0, const Fp3_variable &c1, const std::string &annotation_prefix) : gadget(pb, annotation_prefix), c0(c0), c1(c1) { + all_vars.emplace_back(c0.c0); + all_vars.emplace_back(c0.c1); + all_vars.emplace_back(c0.c2); + all_vars.emplace_back(c1.c0); + all_vars.emplace_back(c1.c1); + all_vars.emplace_back(c1.c2); + +} + +template +void Fp6_variable::generate_r1cs_equals_constraints( + const Fp6_variable &other) +{ + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c0, other.c0.c0), + FMT(this->annotation_prefix, " c0.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c1, other.c0.c1), + FMT(this->annotation_prefix, " c0.c1")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c2, other.c0.c2), + FMT(this->annotation_prefix, " c0.c2")); + + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c1.c0, other.c1.c0), + FMT(this->annotation_prefix, " c1.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c1.c1, other.c1.c1), + FMT(this->annotation_prefix, " c1.c1")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c1.c2, other.c1.c2), + FMT(this->annotation_prefix, " c1.c2")); +} + +template +void Fp6_variable::generate_r1cs_equals_unitary_inverse_constraints( + const Fp6_variable &other) +{ + /* + this.c0 = other.c0 + this.c1 = - other.c1 + + iff + + this.c0.c0 = other.c0.c0 + this.c0.c1 = other.c0.c1 + this.c0.c2 = other.c0.c2 + + this.c1.c0 = - other.c1.c0 + this.c1.c1 = - other.c1.c1 + this.c1.c2 = - other.c1.c2 + */ + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c0, other.c0.c0), + FMT(this->annotation_prefix, " c0.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c1, other.c0.c1), + FMT(this->annotation_prefix, " c0.c1")); + this->pb.add_r1cs_constraint( + r1cs_constraint(1, this->c0.c2, other.c0.c2), + FMT(this->annotation_prefix, " c0.c2")); + + this->pb.add_r1cs_constraint( + r1cs_constraint(-1, this->c1.c0, other.c1.c0), + FMT(this->annotation_prefix, " c1.c0")); + this->pb.add_r1cs_constraint( + r1cs_constraint(-1, this->c1.c1, other.c1.c1), + FMT(this->annotation_prefix, " c1.c1")); + this->pb.add_r1cs_constraint( + r1cs_constraint(-1, this->c1.c2, other.c1.c2), + FMT(this->annotation_prefix, " c1.c2")); } template @@ -59,6 +144,20 @@ Fp6T Fp6_variable::get_element() return el; } + +template +size_t Fp6_variable::size_in_bits() +{ + return 6 * FieldT::size_in_bits(); +} + +template +size_t Fp6_variable::num_variables() +{ + return 6; +} + + template Fp6_variable Fp6_variable::Frobenius_map(const size_t power) const { From 06b346af803d2949a32b560aca09545f6d36c233 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:11:34 -0700 Subject: [PATCH 4/8] a G2 addition gadget to mirror the G1 addition gadget --- .../gadgets/curves/weierstrass_g2_gadget.hpp | 37 +++++++ .../gadgets/curves/weierstrass_g2_gadget.tcc | 96 +++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp index a1049d09..9f291a8b 100644 --- a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp +++ b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.hpp @@ -81,6 +81,43 @@ class G2_checker_gadget : public gadget > { void generate_r1cs_witness(); }; +/** + * Gadget that creates constraints for G2 addition. + */ +template +class G2_add_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + typedef libff::Fqe > FqeT; + + std::shared_ptr> lambda; + std::shared_ptr> inv; + + G2_variable A; + G2_variable B; + G2_variable C; + + std::shared_ptr > B_x_sub_A_x; + std::shared_ptr > B_y_sub_A_y; + std::shared_ptr > C_x_add_A_x_add_B_x; + std::shared_ptr > A_x_sub_C_x; + std::shared_ptr > C_y_add_A_y; + std::shared_ptr > Fqe_one; + + std::shared_ptr > calc_lambda; + std::shared_ptr > calc_X; + std::shared_ptr > calc_Y; + std::shared_ptr > no_special_cases; + + G2_add_gadget(protoboard &pb, + const G2_variable &A, + const G2_variable &B, + const G2_variable &C, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + } // libsnark #include diff --git a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.tcc b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.tcc index 6342da7d..9e4e73a7 100644 --- a/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.tcc +++ b/libsnark/gadgetlib1/gadgets/curves/weierstrass_g2_gadget.tcc @@ -125,6 +125,102 @@ void test_G2_checker_gadget(const std::string &annotation) printf("number of constraints for G2 checker (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); } +template +G2_add_gadget::G2_add_gadget(protoboard &pb, + const G2_variable &A, + const G2_variable &B, + const G2_variable &C, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + A(A), + B(B), + C(C) +{ + /* + lambda = (B.y - A.y)/(B.x - A.x) + C.x = lambda^2 - A.x - B.x + C.y = lambda(A.x - C.x) - A.y + + Special cases: + + doubling: if B.y = A.y and B.x = A.x then lambda is unbound and + C = (lambda^2, lambda^3) + + addition of negative point: if B.y = -A.y and B.x = A.x then no + lambda can satisfy the first equation unless B.y - A.y = 0. But + then this reduces to doubling. + + So we need to check that A.x - B.x != 0, which can be done by + enforcing I * (B.x - A.x) = 1 + */ + + lambda.reset( + new Fqe_variable(pb, FMT(annotation_prefix, " lambda"))); + inv.reset( + new Fqe_variable(pb, FMT(annotation_prefix, " lambda"))); + B_x_sub_A_x.reset( + new Fqe_variable(*(B.X) - *(A.X))); + B_y_sub_A_y.reset( + new Fqe_variable(*(B.Y) - *(A.Y))); + C_x_add_A_x_add_B_x.reset( + new Fqe_variable(*(C.X) + *(A.X) + *(B.X))); + A_x_sub_C_x.reset( + new Fqe_variable(*(A.X) - *(C.X))); + C_y_add_A_y.reset( + new Fqe_variable(*(C.Y) + *(A.Y))); + Fqe_one.reset( + new Fqe_variable(pb, FqeT::one(), FMT(annotation_prefix, "Fqe_one"))); + + calc_lambda.reset(new Fqe_mul_gadget(pb, + *lambda, *B_x_sub_A_x, *B_y_sub_A_y, + FMT(annotation_prefix, " calc_lambda"))); + calc_X.reset(new Fqe_mul_gadget(pb, + *lambda, *lambda, *C_x_add_A_x_add_B_x, + FMT(annotation_prefix, " calc_X"))); + calc_Y.reset(new Fqe_mul_gadget(pb, + *lambda, *A_x_sub_C_x, *C_y_add_A_y, + FMT(annotation_prefix, " calc_Y"))); + no_special_cases.reset(new Fqe_mul_gadget(pb, + *inv, *B_x_sub_A_x, *Fqe_one, + FMT(annotation_prefix, " no_special_cases"))); +} + +template +void G2_add_gadget::generate_r1cs_constraints() +{ + calc_lambda->generate_r1cs_constraints(); + calc_X->generate_r1cs_constraints(); + calc_Y->generate_r1cs_constraints(); + no_special_cases->generate_r1cs_constraints(); +} + +template +void G2_add_gadget::generate_r1cs_witness() +{ + inv->generate_r1cs_witness( + ( B.X->get_element() - A.X->get_element() ).inverse()); + lambda->generate_r1cs_witness( + (B.Y->get_element() - A.Y->get_element()) + * inv->get_element() ); + + C.X->generate_r1cs_witness( + lambda->get_element().squared() - A.X->get_element() - B.X->get_element()); + C.Y->generate_r1cs_witness( + lambda->get_element() * ( A.X->get_element() - C.X->get_element()) - A.Y->get_element()); + + B_x_sub_A_x->evaluate(); + B_y_sub_A_y->evaluate(); + C_x_add_A_x_add_B_x->evaluate(); + A_x_sub_C_x->evaluate(); + C_y_add_A_y->evaluate(); + Fqe_one->evaluate(); + + calc_lambda->generate_r1cs_witness_internal(); + calc_X->generate_r1cs_witness_internal(); + calc_Y->generate_r1cs_witness_internal(); + no_special_cases->generate_r1cs_witness_internal(); +} + } // libsnark #endif // WEIERSTRASS_G2_GADGET_TCC_ From 76601e7bdb8a2947434c00dcaec04e9620f36938 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:12:43 -0700 Subject: [PATCH 5/8] add the flip at the end of the weierstrass miller loop gadget if the loop count is supposed to be negative --- .../pairing/weierstrass_miller_loop.hpp | 6 ++ .../pairing/weierstrass_miller_loop.tcc | 78 ++++++++++++++++--- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.hpp b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.hpp index 96a9aa73..de2c58b5 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.hpp @@ -118,6 +118,8 @@ class mnt_miller_loop_gadget : public gadget > { std::vector > > dbl_sqrs; std::vector > > add_muls; + std::shared_ptr > result_before_potential_negation; + size_t f_count; size_t add_count; size_t dbl_count; @@ -165,6 +167,8 @@ class mnt_e_over_e_miller_loop_gadget : public gadget > { std::vector > > dbl_muls2; std::vector > > add_muls2; + std::shared_ptr > result_before_potential_negation; + size_t f_count; size_t add_count; size_t dbl_count; @@ -222,6 +226,8 @@ class mnt_e_times_e_over_e_miller_loop_gadget : public gadget > { std::vector > > dbl_muls3; std::vector > > add_muls3; + std::shared_ptr > result_before_potential_negation; + size_t f_count; size_t add_count; size_t dbl_count; diff --git a/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.tcc b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.tcc index c825d26c..90df8694 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.tcc +++ b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_miller_loop.tcc @@ -176,6 +176,8 @@ mnt_miller_loop_gadget::mnt_miller_loop_gadget(protoboard &pb, f_count = add_count = dbl_count = 0; + result_before_potential_negation.reset(new Fqk_variable(pb, FMT(annotation_prefix, " result_before_potential_negation"))); + bool found_nonzero = false; std::vector NAF = find_wnaf(1, loop_count); for (long i = NAF.size()-1; i >= 0; --i) @@ -234,7 +236,7 @@ mnt_miller_loop_gadget::mnt_miller_loop_gadget(protoboard &pb, ++prec_id; dbl_sqrs[dbl_id].reset(new Fqk_sqr_gadget(pb, *fs[f_id], *fs[f_id+1], FMT(annotation_prefix, " dbl_sqrs_%zu", dbl_id))); ++f_id; - dbl_muls[dbl_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RR_at_Ps[dbl_id], (f_id + 1 == f_count ? result : *fs[f_id+1]), FMT(annotation_prefix, " dbl_muls_%zu", dbl_id))); + dbl_muls[dbl_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RR_at_Ps[dbl_id], (f_id + 1 == f_count ? *result_before_potential_negation : *fs[f_id+1]), FMT(annotation_prefix, " dbl_muls_%zu", dbl_id))); ++f_id; ++dbl_id; @@ -246,7 +248,7 @@ mnt_miller_loop_gadget::mnt_miller_loop_gadget(protoboard &pb, g_RQ_at_Ps[add_id], FMT(annotation_prefix, " addition_steps_%zu", add_id))); ++prec_id; - add_muls[add_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RQ_at_Ps[add_id], (f_id + 1 == f_count ? result : *fs[f_id+1]), FMT(annotation_prefix, " add_muls_%zu", add_id))); + add_muls[add_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RQ_at_Ps[add_id], (f_id + 1 == f_count ? *result_before_potential_negation : *fs[f_id+1]), FMT(annotation_prefix, " add_muls_%zu", add_id))); ++f_id; ++add_id; } @@ -258,6 +260,8 @@ void mnt_miller_loop_gadget::generate_r1cs_constraints() { fs[0]->generate_r1cs_equals_const_constraints(FqkT::one()); + bool is_neg = pairing_selector::is_loop_count_neg; + for (size_t i = 0; i < dbl_count; ++i) { doubling_steps[i]->generate_r1cs_constraints(); @@ -270,12 +274,20 @@ void mnt_miller_loop_gadget::generate_r1cs_constraints() addition_steps[i]->generate_r1cs_constraints(); add_muls[i]->generate_r1cs_constraints(); } + + if (is_neg) { + result.generate_r1cs_equals_unitary_inverse_constraints(*result_before_potential_negation); + } else { + result.generate_r1cs_equals_constraints(*result_before_potential_negation); + } + } template void mnt_miller_loop_gadget::generate_r1cs_witness() { fs[0]->generate_r1cs_witness(FqkT::one()); + bool is_neg = pairing_selector::is_loop_count_neg; size_t add_id = 0; size_t dbl_id = 0; @@ -305,6 +317,13 @@ void mnt_miller_loop_gadget::generate_r1cs_witness() ++add_id; } } + + if (is_neg) { + result.generate_r1cs_witness(result_before_potential_negation->get_element().unitary_inverse()); + } else { + result.generate_r1cs_witness(result_before_potential_negation->get_element()); + } + } template @@ -350,7 +369,6 @@ void test_mnt_miller_loop(const std::string &annotation) libff::affine_ate_G1_precomp > native_prec_P = other_curve::affine_ate_precompute_G1(P_val); libff::affine_ate_G2_precomp > native_prec_Q = other_curve::affine_ate_precompute_G2(Q_val); libff::Fqk > native_result = other_curve::affine_ate_miller_loop(native_prec_P, native_prec_Q); - assert(result.get_element() == native_result); printf("number of constraints for Miller loop (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); } @@ -366,6 +384,8 @@ mnt_e_over_e_miller_loop_gadget::mnt_e_over_e_miller_loop_gadget(protoboard gadget(pb, annotation_prefix), prec_P1(prec_P1), prec_Q1(prec_Q1), prec_P2(prec_P2), prec_Q2(prec_Q2), result(result) { const auto &loop_count = pairing_selector::pairing_loop_count; + result_before_potential_negation.reset(new Fqk_variable(pb, FMT(annotation_prefix, " result_before_potential_negation"))); + f_count = add_count = dbl_count = 0; @@ -440,7 +460,7 @@ gadget(pb, annotation_prefix), prec_P1(prec_P1), prec_Q1(prec_Q1), prec_ ++f_id; dbl_muls1[dbl_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RR_at_P1s[dbl_id], *fs[f_id+1], FMT(annotation_prefix, " dbl_mul1s_%zu", dbl_id))); ++f_id; - dbl_muls2[dbl_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? result : *fs[f_id+1]), *g_RR_at_P2s[dbl_id], *fs[f_id], FMT(annotation_prefix, " dbl_mul2s_%zu", dbl_id))); + dbl_muls2[dbl_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? *result_before_potential_negation : *fs[f_id+1]), *g_RR_at_P2s[dbl_id], *fs[f_id], FMT(annotation_prefix, " dbl_mul2s_%zu", dbl_id))); ++f_id; ++dbl_id; @@ -459,7 +479,7 @@ gadget(pb, annotation_prefix), prec_P1(prec_P1), prec_Q1(prec_Q1), prec_ ++prec_id; add_muls1[add_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RQ_at_P1s[add_id], *fs[f_id+1], FMT(annotation_prefix, " add_mul1s_%zu", add_id))); ++f_id; - add_muls2[add_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? result : *fs[f_id+1]), *g_RQ_at_P2s[add_id], *fs[f_id], FMT(annotation_prefix, " add_mul2s_%zu", add_id))); + add_muls2[add_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? *result_before_potential_negation : *fs[f_id+1]), *g_RQ_at_P2s[add_id], *fs[f_id], FMT(annotation_prefix, " add_mul2s_%zu", add_id))); ++f_id; ++add_id; } @@ -471,6 +491,8 @@ void mnt_e_over_e_miller_loop_gadget::generate_r1cs_constraints() { fs[0]->generate_r1cs_equals_const_constraints(FqkT::one()); + bool is_neg = pairing_selector::is_loop_count_neg; + for (size_t i = 0; i < dbl_count; ++i) { doubling_steps1[i]->generate_r1cs_constraints(); @@ -487,6 +509,13 @@ void mnt_e_over_e_miller_loop_gadget::generate_r1cs_constraints() add_muls1[i]->generate_r1cs_constraints(); add_muls2[i]->generate_r1cs_constraints(); } + + if (is_neg) { + result.generate_r1cs_equals_unitary_inverse_constraints(*result_before_potential_negation); + } else { + result.generate_r1cs_equals_constraints(*result_before_potential_negation); + } + } template @@ -494,6 +523,8 @@ void mnt_e_over_e_miller_loop_gadget::generate_r1cs_witness() { fs[0]->generate_r1cs_witness(FqkT::one()); + bool is_neg = pairing_selector::is_loop_count_neg; + size_t add_id = 0; size_t dbl_id = 0; size_t f_id = 0; @@ -517,7 +548,7 @@ void mnt_e_over_e_miller_loop_gadget::generate_r1cs_witness() ++f_id; dbl_muls1[dbl_id]->generate_r1cs_witness(); ++f_id; - (f_id+1 == f_count ? result : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RR_at_P2s[dbl_id]->get_element().inverse()); + (f_id+1 == f_count ? *result_before_potential_negation : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RR_at_P2s[dbl_id]->get_element().inverse()); dbl_muls2[dbl_id]->generate_r1cs_witness(); ++f_id; ++dbl_id; @@ -528,12 +559,19 @@ void mnt_e_over_e_miller_loop_gadget::generate_r1cs_witness() addition_steps2[add_id]->generate_r1cs_witness(); add_muls1[add_id]->generate_r1cs_witness(); ++f_id; - (f_id+1 == f_count ? result : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RQ_at_P2s[add_id]->get_element().inverse()); + (f_id+1 == f_count ? *result_before_potential_negation : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RQ_at_P2s[add_id]->get_element().inverse()); add_muls2[add_id]->generate_r1cs_witness(); ++f_id; ++add_id; } } + + if (is_neg) { + result.generate_r1cs_witness(result_before_potential_negation->get_element().unitary_inverse()); + } else { + result.generate_r1cs_witness(result_before_potential_negation->get_element()); + } + } template @@ -617,6 +655,8 @@ gadget(pb, annotation_prefix), prec_P1(prec_P1), prec_Q1(prec_Q1), prec_ f_count = add_count = dbl_count = 0; + result_before_potential_negation.reset(new Fqk_variable(pb, FMT(annotation_prefix, " result_before_potential_negation"))); + bool found_nonzero = false; std::vector NAF = find_wnaf(1, loop_count); for (long i = NAF.size()-1; i >= 0; --i) @@ -700,7 +740,7 @@ gadget(pb, annotation_prefix), prec_P1(prec_P1), prec_Q1(prec_Q1), prec_ ++f_id; dbl_muls2[dbl_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RR_at_P2s[dbl_id], *fs[f_id+1], FMT(annotation_prefix, " dbl_muls2_%zu", dbl_id))); ++f_id; - dbl_muls3[dbl_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? result : *fs[f_id+1]), *g_RR_at_P3s[dbl_id], *fs[f_id], FMT(annotation_prefix, " dbl_muls3_%zu", dbl_id))); + dbl_muls3[dbl_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? *result_before_potential_negation : *fs[f_id+1]), *g_RR_at_P3s[dbl_id], *fs[f_id], FMT(annotation_prefix, " dbl_muls3_%zu", dbl_id))); ++f_id; ++dbl_id; @@ -726,7 +766,7 @@ gadget(pb, annotation_prefix), prec_P1(prec_P1), prec_Q1(prec_Q1), prec_ ++f_id; add_muls2[add_id].reset(new Fqk_special_mul_gadget(pb, *fs[f_id], *g_RQ_at_P2s[add_id], *fs[f_id+1], FMT(annotation_prefix, " add_muls2_%zu", add_id))); ++f_id; - add_muls3[add_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? result : *fs[f_id+1]), *g_RQ_at_P3s[add_id], *fs[f_id], FMT(annotation_prefix, " add_muls3_%zu", add_id))); + add_muls3[add_id].reset(new Fqk_special_mul_gadget(pb, (f_id + 1 == f_count ? *result_before_potential_negation : *fs[f_id+1]), *g_RQ_at_P3s[add_id], *fs[f_id], FMT(annotation_prefix, " add_muls3_%zu", add_id))); ++f_id; ++add_id; } @@ -737,6 +777,7 @@ template void mnt_e_times_e_over_e_miller_loop_gadget::generate_r1cs_constraints() { fs[0]->generate_r1cs_equals_const_constraints(FqkT::one()); + bool is_neg = pairing_selector::is_loop_count_neg; for (size_t i = 0; i < dbl_count; ++i) { @@ -758,6 +799,12 @@ void mnt_e_times_e_over_e_miller_loop_gadget::generate_r1cs_constraints() add_muls2[i]->generate_r1cs_constraints(); add_muls3[i]->generate_r1cs_constraints(); } + + if (is_neg) { + result.generate_r1cs_equals_unitary_inverse_constraints(*result_before_potential_negation); + } else { + result.generate_r1cs_equals_constraints(*result_before_potential_negation); + } } template @@ -770,6 +817,7 @@ void mnt_e_times_e_over_e_miller_loop_gadget::generate_r1cs_witness() size_t f_id = 0; const auto &loop_count = pairing_selector::pairing_loop_count; + bool is_neg = pairing_selector::is_loop_count_neg; bool found_nonzero = false; std::vector NAF = find_wnaf(1, loop_count); @@ -791,7 +839,7 @@ void mnt_e_times_e_over_e_miller_loop_gadget::generate_r1cs_witness() ++f_id; dbl_muls2[dbl_id]->generate_r1cs_witness(); ++f_id; - (f_id+1 == f_count ? result : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RR_at_P3s[dbl_id]->get_element().inverse()); + (f_id+1 == f_count ? *result_before_potential_negation : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RR_at_P3s[dbl_id]->get_element().inverse()); dbl_muls3[dbl_id]->generate_r1cs_witness(); ++f_id; ++dbl_id; @@ -805,12 +853,18 @@ void mnt_e_times_e_over_e_miller_loop_gadget::generate_r1cs_witness() ++f_id; add_muls2[add_id]->generate_r1cs_witness(); ++f_id; - (f_id+1 == f_count ? result : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RQ_at_P3s[add_id]->get_element().inverse()); + (f_id+1 == f_count ? *result_before_potential_negation : *fs[f_id+1]).generate_r1cs_witness(fs[f_id]->get_element() * g_RQ_at_P3s[add_id]->get_element().inverse()); add_muls3[add_id]->generate_r1cs_witness(); ++f_id; ++add_id; } } + + if (is_neg) { + result.generate_r1cs_witness(result_before_potential_negation->get_element().unitary_inverse()); + } else { + result.generate_r1cs_witness(result_before_potential_negation->get_element()); + } } template @@ -891,8 +945,8 @@ void test_mnt_e_times_e_over_e_miller_loop(const std::string &annotation) libff::Fqk > native_result = (other_curve::affine_ate_miller_loop(native_prec_P1, native_prec_Q1) * other_curve::affine_ate_miller_loop(native_prec_P2, native_prec_Q2) * other_curve::affine_ate_miller_loop(native_prec_P3, native_prec_Q3).inverse()); - assert(result.get_element() == native_result); + printf("number of constraints for e times e over e Miller loop (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); } From 16831eb3860e61223a6b2b5cb6ce94d7a34c3c96 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:13:15 -0700 Subject: [PATCH 6/8] add a gadget for computing the value of a final exponenation --- ...weierstrass_final_exponentiation_value.hpp | 106 ++++++++++ ...weierstrass_final_exponentiation_value.tcc | 194 ++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.hpp create mode 100644 libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.tcc diff --git a/libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.hpp b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.hpp new file mode 100644 index 00000000..039621a4 --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.hpp @@ -0,0 +1,106 @@ +/** @file + ***************************************************************************** + Declaration of interfaces for final exponentiation gadgets. + The gadgets verify final exponentiation for Weiersrass curves with embedding + degrees 4 and 6. + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef WEIERSTRASS_FINAL_EXPONENTIATION_VALUE_HPP_ +#define WEIERSTRASS_FINAL_EXPONENTIATION_VALUE_HPP_ + +#include + +#include +#include + +namespace libsnark { + +/** + * Gadget for final exponentiation with embedding degree 4. + */ +template +class mnt4_final_exp_value_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + + Fqk_variable el; + std::shared_ptr > one; + std::shared_ptr > el_inv; + std::shared_ptr > el_q_3; + std::shared_ptr > el_q_3_minus_1; + std::shared_ptr > alpha; + std::shared_ptr > beta; + std::shared_ptr > beta_q; + std::shared_ptr > el_inv_q_3; + std::shared_ptr > el_inv_q_3_minus_1; + std::shared_ptr > inv_alpha; + std::shared_ptr > inv_beta; + std::shared_ptr > w1; + std::shared_ptr > w0; + + std::shared_ptr > compute_el_inv; + std::shared_ptr > compute_el_q_3_minus_1; + std::shared_ptr > compute_beta; + std::shared_ptr > compute_el_inv_q_3_minus_1; + std::shared_ptr > compute_inv_beta; + + std::shared_ptr, Fp6_variable, Fp6_mul_gadget, Fp6_cyclotomic_sqr_gadget, libff::mnt6_q_limbs> > compute_w1; + std::shared_ptr, Fp6_variable, Fp6_mul_gadget, Fp6_cyclotomic_sqr_gadget, libff::mnt6_q_limbs> > compute_w0; + std::shared_ptr > compute_result; + + Fqk_variable result; + + mnt4_final_exp_value_gadget(protoboard &pb, + const Fqk_variable &el, + const Fqk_variable &result, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +/** + * Gadget for final exponentiation with embedding degree 6. + */ +template +class mnt6_final_exp_value_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + + Fqk_variable el; + std::shared_ptr > one; + std::shared_ptr > el_inv; + std::shared_ptr > el_q_2; + std::shared_ptr > el_q_2_minus_1; + std::shared_ptr > el_q_3_minus_q; + std::shared_ptr > el_inv_q_2; + std::shared_ptr > el_inv_q_2_minus_1; + std::shared_ptr > w1; + std::shared_ptr > w0; + + std::shared_ptr > compute_el_inv; + std::shared_ptr > compute_el_q_2_minus_1; + std::shared_ptr > compute_el_inv_q_2_minus_1; + + std::shared_ptr, Fp4_variable, Fp4_mul_gadget, Fp4_cyclotomic_sqr_gadget, libff::mnt4_q_limbs> > compute_w1; + std::shared_ptr, Fp4_variable, Fp4_mul_gadget, Fp4_cyclotomic_sqr_gadget, libff::mnt4_q_limbs> > compute_w0; + std::shared_ptr > compute_result; + + Fqk_variable result; + + mnt6_final_exp_value_gadget(protoboard &pb, + const Fqk_variable &el, + const Fqk_variable &result, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +} // libsnark + +#include + +#endif // WEIERSTRASS_FINAL_EXPONENTIATION_VALUE_HPP_ diff --git a/libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.tcc b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.tcc new file mode 100644 index 00000000..1dc0a45a --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/pairing/weierstrass_final_exponentiation_value.tcc @@ -0,0 +1,194 @@ +/** @file + ***************************************************************************** + Implementation of interfaces for final exponentiation gadgets. + See weierstrass_final_exponentiation.hpp . + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef WEIERSTRASS_FINAL_EXPONENTIATION_VALUE_TCC_ +#define WEIERSTRASS_FINAL_EXPONENTIATION_VALUE_TCC_ + +#include +#include + +#include + +namespace libsnark { + +template +mnt4_final_exp_value_gadget::mnt4_final_exp_value_gadget(protoboard &pb, + const Fqk_variable &el, + const Fqk_variable &result, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + el(el), + result(result) +{ + one.reset(new Fqk_variable(pb, FMT(annotation_prefix, " one"))); + el_inv.reset(new Fqk_variable(pb, FMT(annotation_prefix, " el_inv"))); + el_q_3.reset(new Fqk_variable(el.Frobenius_map(3))); + el_q_3_minus_1.reset(new Fqk_variable(pb, FMT(annotation_prefix, " el_q_3_minus_1"))); + alpha.reset(new Fqk_variable(el_q_3_minus_1->Frobenius_map(1))); + beta.reset(new Fqk_variable(pb, FMT(annotation_prefix, " beta"))); + beta_q.reset(new Fqk_variable(beta->Frobenius_map(1))); + + el_inv_q_3.reset(new Fqk_variable(el_inv->Frobenius_map(3))); + el_inv_q_3_minus_1.reset(new Fqk_variable(pb, FMT(annotation_prefix, " el_inv_q_3_minus_1"))); + inv_alpha.reset(new Fqk_variable(el_inv_q_3_minus_1->Frobenius_map(1))); + inv_beta.reset(new Fqk_variable(pb, FMT(annotation_prefix, " inv_beta"))); + w1.reset(new Fqk_variable(pb, FMT(annotation_prefix, " w1"))); + w0.reset(new Fqk_variable(pb, FMT(annotation_prefix, " w0"))); + + compute_el_inv.reset(new Fqk_mul_gadget(pb, el, *el_inv, *one, FMT(annotation_prefix, " compute_el_inv"))); + compute_el_q_3_minus_1.reset(new Fqk_mul_gadget(pb, *el_q_3, *el_inv, *el_q_3_minus_1, FMT(annotation_prefix, " compute_el_q_3_minus_1"))); + compute_beta.reset(new Fqk_mul_gadget(pb, *alpha, *el_q_3_minus_1, *beta, FMT(annotation_prefix, " compute_beta"))); + + compute_el_inv_q_3_minus_1.reset(new Fqk_mul_gadget(pb, *el_inv_q_3, el, *el_inv_q_3_minus_1, FMT(annotation_prefix, " compute_el_inv__q_3_minus_1"))); + compute_inv_beta.reset(new Fqk_mul_gadget(pb, *inv_alpha, *el_inv_q_3_minus_1, *inv_beta, FMT(annotation_prefix, " compute_inv_beta"))); + + compute_w1.reset(new exponentiation_gadget, Fp6_variable, Fp6_mul_gadget, Fp6_cyclotomic_sqr_gadget, libff::mnt6_q_limbs>( + pb, *beta_q, libff::mnt6_final_exponent_last_chunk_w1, *w1, FMT(annotation_prefix, " compute_w1"))); + + compute_w0.reset(new exponentiation_gadget, Fp6_variable, Fp6_mul_gadget, Fp6_cyclotomic_sqr_gadget, libff::mnt6_q_limbs>( + pb, (libff::mnt6_final_exponent_last_chunk_is_w0_neg ? *inv_beta : *beta), libff::mnt6_final_exponent_last_chunk_abs_of_w0, *w0, FMT(annotation_prefix, " compute_w0"))); + + compute_result.reset(new Fqk_mul_gadget(pb, *w1, *w0, result, FMT(annotation_prefix, " compute_result"))); +} + +template +void mnt4_final_exp_value_gadget::generate_r1cs_constraints() +{ + one->generate_r1cs_equals_const_constraints(libff::Fqk >::one()); + + compute_el_inv->generate_r1cs_constraints(); + compute_el_q_3_minus_1->generate_r1cs_constraints(); + compute_beta->generate_r1cs_constraints(); + + compute_el_inv_q_3_minus_1->generate_r1cs_constraints(); + compute_inv_beta->generate_r1cs_constraints(); + + compute_w0->generate_r1cs_constraints(); + compute_w1->generate_r1cs_constraints(); + compute_result->generate_r1cs_constraints(); +} + +template +void mnt4_final_exp_value_gadget::generate_r1cs_witness() +{ + one->generate_r1cs_witness(libff::Fqk >::one()); + el_inv->generate_r1cs_witness(el.get_element().inverse()); + + compute_el_inv->generate_r1cs_witness(); + el_q_3->evaluate(); + compute_el_q_3_minus_1->generate_r1cs_witness(); + alpha->evaluate(); + compute_beta->generate_r1cs_witness(); + beta_q->evaluate(); + + el_inv_q_3->evaluate(); + compute_el_inv_q_3_minus_1->generate_r1cs_witness(); + inv_alpha->evaluate(); + compute_inv_beta->generate_r1cs_witness(); + + compute_w0->generate_r1cs_witness(); + compute_w1->generate_r1cs_witness(); + compute_result->generate_r1cs_witness(); +} + +template +mnt6_final_exp_value_gadget::mnt6_final_exp_value_gadget(protoboard &pb, + const Fqk_variable &el, + const Fqk_variable &result, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + el(el), + result(result) +{ + one.reset(new Fqk_variable(pb, FMT(annotation_prefix, " one"))); + el_inv.reset(new Fqk_variable(pb, FMT(annotation_prefix, " el_inv"))); + el_q_2.reset(new Fqk_variable(el.Frobenius_map(2))); + el_q_2_minus_1.reset(new Fqk_variable(pb, FMT(annotation_prefix, " el_q_2_minus_1"))); + el_q_3_minus_q.reset(new Fqk_variable(el_q_2_minus_1->Frobenius_map(1))); + el_inv_q_2.reset(new Fqk_variable(el_inv->Frobenius_map(2))); + el_inv_q_2_minus_1.reset(new Fqk_variable(pb, FMT(annotation_prefix, " el_inv_q_2_minus_1"))); + w1.reset(new Fqk_variable(pb, FMT(annotation_prefix, " w1"))); + w0.reset(new Fqk_variable(pb, FMT(annotation_prefix, " w0"))); + + compute_el_inv.reset(new Fqk_mul_gadget(pb, el, *el_inv, *one, FMT(annotation_prefix, " compute_el_inv"))); + compute_el_q_2_minus_1.reset(new Fqk_mul_gadget(pb, *el_q_2, *el_inv, *el_q_2_minus_1, FMT(annotation_prefix, " compute_el_q_2_minus_1"))); + compute_el_inv_q_2_minus_1.reset(new Fqk_mul_gadget(pb, *el_inv_q_2, el, *el_inv_q_2_minus_1, FMT(annotation_prefix, " compute_el_inv_q_2_minus_1"))); + + compute_w1.reset(new exponentiation_gadget, Fp4_variable, Fp4_mul_gadget, Fp4_cyclotomic_sqr_gadget, libff::mnt4_q_limbs>( + pb, *el_q_3_minus_q, libff::mnt4_final_exponent_last_chunk_w1, *w1, FMT(annotation_prefix, " compute_w1"))); + compute_w0.reset(new exponentiation_gadget, Fp4_variable, Fp4_mul_gadget, Fp4_cyclotomic_sqr_gadget, libff::mnt4_q_limbs>( + pb, (libff::mnt4_final_exponent_last_chunk_is_w0_neg ? *el_inv_q_2_minus_1 : *el_q_2_minus_1), libff::mnt4_final_exponent_last_chunk_abs_of_w0, *w0, FMT(annotation_prefix, " compute_w0"))); + compute_result.reset(new Fqk_mul_gadget(pb, *w1, *w0, result, FMT(annotation_prefix, " compute_result"))); +} + +template +void mnt6_final_exp_value_gadget::generate_r1cs_constraints() +{ + one->generate_r1cs_equals_const_constraints(libff::Fqk >::one()); + + compute_el_inv->generate_r1cs_constraints(); + compute_el_q_2_minus_1->generate_r1cs_constraints(); + compute_el_inv_q_2_minus_1->generate_r1cs_constraints(); + compute_w1->generate_r1cs_constraints(); + compute_w0->generate_r1cs_constraints(); + compute_result->generate_r1cs_constraints(); +} + +template +void mnt6_final_exp_value_gadget::generate_r1cs_witness() +{ + one->generate_r1cs_witness(libff::Fqk >::one()); + el_inv->generate_r1cs_witness(el.get_element().inverse()); + + compute_el_inv->generate_r1cs_witness(); + el_q_2->evaluate(); + compute_el_q_2_minus_1->generate_r1cs_witness(); + el_q_3_minus_q->evaluate(); + el_inv_q_2->evaluate(); + compute_el_inv_q_2_minus_1->generate_r1cs_witness(); + compute_w1->generate_r1cs_witness(); + compute_w0->generate_r1cs_witness(); + compute_result->generate_r1cs_witness(); +} + +template +void test_mnt_final_exp_value(const std::string &annotation) +{ + protoboard > pb; + libff::Fqk> x = libff::Fqk>::random_element(); + + Fqk_variable el(pb, "el"); + Fqk_variable result(pb, "result"); + + final_exp_value_gadget finexp(pb, el, result, "miller"); + + PROFILE_CONSTRAINTS(pb, "Final exp") + { + finexp.generate_r1cs_constraints(); + } + PRINT_CONSTRAINT_PROFILING(); + + el.generate_r1cs_witness(x); + finexp.generate_r1cs_witness(); + assert(pb.is_satisfied()); + + libff::Fqk > native_finexp_result = other_curve::final_exponentiation(x); + + printf("Must match:\n"); + result.get_element().print(); + native_finexp_result.print(); + + assert(result.get_element() == native_finexp_result); + printf("number of constraints for final exponentiation (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); +} + +} // libsnark + +#endif // WEIERSTRASS_FINAL_EXPONENTIATION_VALUE_TCC_ From 1d708e46e1f91f83330c2f9777d893f48b0ab602 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:13:49 -0700 Subject: [PATCH 7/8] some bureaucratic changes to pairing gadgets --- .../gadgets/pairing/mnt_pairing_params.hpp | 10 +++++ .../gadgets/pairing/pairing_checks.hpp | 42 +++++++++++++++++ .../gadgets/pairing/pairing_checks.tcc | 45 +++++++++++++++++++ .../gadgets/pairing/pairing_params.hpp | 3 +- 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp b/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp index 74f75ff9..9ba0b574 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp @@ -39,6 +39,12 @@ class mnt4_final_exp_gadget; template class mnt6_final_exp_gadget; +template +class mnt4_final_exp_value_gadget; + +template +class mnt6_final_exp_value_gadget; + /** * Specialization for MNT4. */ @@ -64,8 +70,10 @@ class pairing_selector { typedef mnt_e_over_e_miller_loop_gadget e_over_e_miller_loop_gadget_type; typedef mnt_e_times_e_over_e_miller_loop_gadget e_times_e_over_e_miller_loop_gadget_type; typedef mnt4_final_exp_gadget final_exp_gadget_type; + typedef mnt4_final_exp_value_gadget final_exp_value_gadget_type; static const constexpr libff::bigint &pairing_loop_count = libff::mnt6_ate_loop_count; + static const constexpr bool &is_loop_count_neg = libff::mnt6_ate_is_loop_count_neg; }; /** @@ -94,8 +102,10 @@ class pairing_selector { typedef mnt_e_over_e_miller_loop_gadget e_over_e_miller_loop_gadget_type; typedef mnt_e_times_e_over_e_miller_loop_gadget e_times_e_over_e_miller_loop_gadget_type; typedef mnt6_final_exp_gadget final_exp_gadget_type; + typedef mnt6_final_exp_value_gadget final_exp_value_gadget_type; static const constexpr libff::bigint &pairing_loop_count = libff::mnt4_ate_loop_count; + static const constexpr bool &is_loop_count_neg = libff::mnt4_ate_is_loop_count_neg; }; } // libsnark diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp index 162611cb..cf138687 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp @@ -20,6 +20,7 @@ #include #include +#include #include namespace libsnark { @@ -88,6 +89,47 @@ class check_e_equals_ee_gadget : public gadget > { void generate_r1cs_witness(); }; +template +class check_e_times_e_over_e_equals_value_gadget : public gadget > { +public: + + typedef libff::Fr FieldT; + + std::shared_ptr > result; + std::shared_ptr > ratio; + std::shared_ptr > compute_ratio; + std::shared_ptr > check_finexp; + + std::shared_ptr> check_is_expected; + + G1_precomputation lhs1_G1; + G2_precomputation lhs1_G2; + G1_precomputation lhs2_G1; + G2_precomputation lhs2_G2; + G1_precomputation rhs_G1; + G2_precomputation rhs_G2; + + Fqk_variable expected_result; + + // Boolean + pb_variable result_is_expected; + + check_e_times_e_over_e_equals_value_gadget(protoboard &pb, + const G1_precomputation &lhs1_G1, + const G2_precomputation &lhs1_G2, + const G1_precomputation &lhs2_G1, + const G2_precomputation &lhs2_G2, + const G1_precomputation &rhs_G1, + const G2_precomputation &rhs_G2, + const Fqk_variable &expected_result, + const pb_variable &result_is_expected, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + + void generate_r1cs_witness(); +}; + } // libsnark #include diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc index 65aa4a7b..5deeaf6d 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc @@ -88,6 +88,51 @@ void check_e_equals_ee_gadget::generate_r1cs_witness() check_finexp->generate_r1cs_witness(); } +template +check_e_times_e_over_e_equals_value_gadget::check_e_times_e_over_e_equals_value_gadget( + protoboard &pb, + const G1_precomputation &lhs1_G1, + const G2_precomputation &lhs1_G2, + const G1_precomputation &lhs2_G1, + const G2_precomputation &lhs2_G2, + const G1_precomputation &rhs_G1, + const G2_precomputation &rhs_G2, + const Fqk_variable &expected_result, + const pb_variable &result_is_expected, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + lhs1_G1(lhs1_G1), + lhs1_G2(lhs1_G2), + lhs2_G1(lhs2_G1), + lhs2_G2(lhs2_G2), + rhs_G1(rhs_G1), + rhs_G2(rhs_G2), + expected_result(expected_result), + result_is_expected(result_is_expected) +{ + result.reset(new Fqk_variable(pb, FMT(annotation_prefix, " result"))); + ratio.reset(new Fqk_variable(pb, FMT(annotation_prefix, " ratio"))); + compute_ratio.reset(new e_times_e_over_e_miller_loop_gadget(pb, lhs1_G1, lhs1_G2, lhs2_G1, lhs2_G2, rhs_G1, rhs_G2, *ratio, FMT(annotation_prefix, " compute_ratio"))); + check_finexp.reset(new final_exp_value_gadget(pb, *ratio, *result, FMT(annotation_prefix, " check_finexp"))); + check_is_expected.reset(new field_vector_equals_gadget(pb, result->all_vars, expected_result.all_vars, result_is_expected, FMT(annotation_prefix, " check_is_expected"))); +} + +template +void check_e_times_e_over_e_equals_value_gadget::generate_r1cs_constraints() +{ + compute_ratio->generate_r1cs_constraints(); + check_finexp->generate_r1cs_constraints(); + check_is_expected->generate_r1cs_constraints(); +} + +template +void check_e_times_e_over_e_equals_value_gadget::generate_r1cs_witness() +{ + compute_ratio->generate_r1cs_witness(); + check_finexp->generate_r1cs_witness(); + check_is_expected->generate_r1cs_witness(); +} + } // libsnark #endif // PAIRING_CHECKS_TCC_ diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp b/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp index e71c07a0..30636f61 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp @@ -110,7 +110,8 @@ template using e_times_e_over_e_miller_loop_gadget = typename pairing_selector::e_times_e_over_e_miller_loop_gadget_type; template using final_exp_gadget = typename pairing_selector::final_exp_gadget_type; - +template +using final_exp_value_gadget = typename pairing_selector::final_exp_value_gadget_type; } // libsnark #endif // PAIRING_PARAMS_HPP_ From cf113f5cf08d92dfec93bfd9f599d7fa770d09b5 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Wed, 8 Aug 2018 23:14:38 -0700 Subject: [PATCH 8/8] add the SE verifier gadget --- libsnark/CMakeLists.txt | 12 + .../r1cs_se_ppzksnark_verifier_gadget.hpp | 217 ++++++++ .../r1cs_se_ppzksnark_verifier_gadget.tcc | 475 ++++++++++++++++++ ...test_r1cs_se_ppzksnark_verifier_gadget.cpp | 427 ++++++++++++++++ 4 files changed, 1131 insertions(+) create mode 100644 libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.hpp create mode 100644 libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.tcc create mode 100644 libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_se_ppzksnark_verifier_gadget.cpp diff --git a/libsnark/CMakeLists.txt b/libsnark/CMakeLists.txt index 2e127940..e49b7a1b 100644 --- a/libsnark/CMakeLists.txt +++ b/libsnark/CMakeLists.txt @@ -386,6 +386,18 @@ target_link_libraries( snark ) +add_executable( + gadgetlib1_r1cs_se_ppzksnark_verifier_gadget_test + EXCLUDE_FROM_ALL + + gadgetlib1/gadgets/verifiers/tests/test_r1cs_se_ppzksnark_verifier_gadget.cpp +) +target_link_libraries( + gadgetlib1_r1cs_se_ppzksnark_verifier_gadget_test + + snark +) + add_executable( gadgetlib2_adapters_test EXCLUDE_FROM_ALL diff --git a/libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.hpp b/libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.hpp new file mode 100644 index 00000000..bbdf7021 --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.hpp @@ -0,0 +1,217 @@ +/** @file + ***************************************************************************** + + Declaration of interfaces for the the R1CS ppzkSNARK verifier gadget. + + The gadget r1cs_ppzksnark_verifier_gadget verifiers correct computation of r1cs_ppzksnark_verifier_strong_IC. + The gadget is built from two main sub-gadgets: + - r1cs_ppzksnark_verifier_process_vk_gadget, which verifies correct computation of r1cs_ppzksnark_verifier_process_vk, and + - r1cs_ppzksnark_online_verifier_gadget, which verifies correct computation of r1cs_ppzksnark_online_verifier_strong_IC. + See r1cs_ppzksnark.hpp for description of the aforementioned functions. + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_SE_PPZKSNARK_VERIFIER_GADGET_HPP_ +#define R1CS_SE_PPZKSNARK_VERIFIER_GADGET_HPP_ + +#include +#include +#include +#include +#include +#include + +namespace libsnark { + +template +class r1cs_se_ppzksnark_proof_variable : public gadget > { +public: + typedef libff::Fr FieldT; + + std::shared_ptr > A; + std::shared_ptr > B; + std::shared_ptr > C; + + std::vector > > all_G1_vars; + std::vector > > all_G2_vars; + + std::vector > > all_G1_checkers; + std::shared_ptr > G2_checker; + + pb_variable_array proof_contents; + + r1cs_se_ppzksnark_proof_variable(protoboard &pb, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(const r1cs_se_ppzksnark_proof > &proof); + static size_t size(); +}; + +template +class r1cs_se_ppzksnark_verification_key_variable : public gadget > { +public: + typedef libff::Fr FieldT; + + std::shared_ptr > H; + std::shared_ptr > G_alpha; + std::shared_ptr > H_beta; + std::shared_ptr > G_gamma; + std::shared_ptr > H_gamma; + + std::shared_ptr> G_alpha_H_beta_inv; + + std::vector > > query; + std::shared_ptr > query_base; + + pb_linear_combination_array all_vars; + size_t input_size; + + std::vector > > all_G1_vars; + std::vector > > all_G2_vars; + std::vector > > all_GT_vars; + + // Unfortunately, g++ 4.9 and g++ 5.0 have a bug related to + // incorrect inlining of small functions: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65307, which + // produces wrong assembly even at -O1. The test case at the bug + // report is directly derived from this code here. As a temporary + // work-around we mark the key functions noinline to hint compiler + // that inlining should not be performed. + + // TODO: remove later, when g++ developers fix the bug. + + __attribute__((noinline)) r1cs_se_ppzksnark_verification_key_variable(protoboard &pb, + const size_t input_size, + const std::string &annotation_prefix); + void generate_r1cs_witness(const r1cs_se_ppzksnark_verification_key > &vk); +}; + +template +class r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable { +public: + typedef libff::Fr FieldT; + + std::shared_ptr > G_alpha; + std::shared_ptr > H_beta; + std::shared_ptr> G_alpha_H_beta_inv; + + std::shared_ptr > G_gamma_pc; + std::shared_ptr > H_gamma_pc; + std::shared_ptr > H_pc; + + std::shared_ptr > query_base; + std::vector > > query; + + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable(); + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable(protoboard &pb, + const r1cs_se_ppzksnark_verification_key > &r1cs_vk, + const std::string &annotation_prefix); +}; + +template +class r1cs_se_ppzksnark_verifier_process_vk_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + + std::shared_ptr > compute_G_gamma_pc; + std::shared_ptr > compute_H_gamma_pc; + std::shared_ptr > compute_H_pc; + + r1cs_se_ppzksnark_verification_key_variable vk; + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable &pvk; // important to have a reference here + + r1cs_se_ppzksnark_verifier_process_vk_gadget(protoboard &pb, + const r1cs_se_ppzksnark_verification_key_variable &vk, + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable &pvk, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +class r1cs_se_ppzksnark_online_verifier_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable pvk; + + pb_variable_array input; + size_t elt_size; + r1cs_se_ppzksnark_proof_variable proof; + pb_variable result; + const size_t input_len; + + std::shared_ptr > acc; + std::shared_ptr > accumulate_input; + + std::shared_ptr > proof_A_G_alpha_precomp; + std::shared_ptr > compute_proof_A_G_alpha_precomp; + std::shared_ptr > proof_B_H_beta_precomp; + std::shared_ptr > compute_proof_B_H_beta_precomp; + + std::shared_ptr> acc_precomp; + std::shared_ptr > compute_acc_precomp; + + std::shared_ptr > proof_A_precomp; + std::shared_ptr > compute_proof_A_precomp; + std::shared_ptr > proof_B_precomp; + std::shared_ptr > compute_proof_B_precomp; + std::shared_ptr > proof_C_precomp; + std::shared_ptr > compute_proof_C_precomp; + + std::shared_ptr > proof_A_G_alpha; + std::shared_ptr > compute_proof_A_G_alpha; + + std::shared_ptr > proof_B_H_beta; + std::shared_ptr > compute_proof_B_H_beta; + + std::shared_ptr> first_check; + std::shared_ptr> second_check; + + pb_variable first_check_passed; + pb_variable second_check_passed; + + pb_variable_array all_test_results; + std::shared_ptr > all_tests_pass; + + r1cs_se_ppzksnark_online_verifier_gadget(protoboard &pb, + const r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable &pvk, + const pb_variable_array &input, + const size_t elt_size, + const r1cs_se_ppzksnark_proof_variable &proof, + const pb_variable &result, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +template +class r1cs_se_ppzksnark_verifier_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + + std::shared_ptr > pvk; + std::shared_ptr > compute_pvk; + std::shared_ptr > online_verifier; + + r1cs_se_ppzksnark_verifier_gadget(protoboard &pb, + const r1cs_se_ppzksnark_verification_key_variable &vk, + const pb_variable_array &input, + const size_t elt_size, + const r1cs_se_ppzksnark_proof_variable &proof, + const pb_variable &result, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); +}; + +} // libsnark + +#include + +#endif // R1CS_SE_PPZKSNARK_VERIFIER_GADGET_HPP_ diff --git a/libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.tcc b/libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.tcc new file mode 100644 index 00000000..78980015 --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/verifiers/r1cs_se_ppzksnark_verifier_gadget.tcc @@ -0,0 +1,475 @@ +/** @file + ***************************************************************************** + + Implementation of interfaces for the the R1CS ppzkSNARK verifier gadget. + + See r1cs_se_ppzksnark_verifier_gadget.hpp . + + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ + +#ifndef R1CS_SE_PPZKSNARK_VERIFIER_GADGET_TCC_ +#define R1CS_SE_PPZKSNARK_VERIFIER_GADGET_TCC_ + +#include + +namespace libsnark { + +template +r1cs_se_ppzksnark_proof_variable::r1cs_se_ppzksnark_proof_variable(protoboard &pb, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix) +{ + const size_t num_G1 = 2; + const size_t num_G2 = 1; + + A.reset(new G1_variable(pb, FMT(annotation_prefix, " A"))); + B.reset(new G2_variable(pb, FMT(annotation_prefix, " B"))); + C.reset(new G1_variable(pb, FMT(annotation_prefix, " C"))); + + all_G1_vars = { A, C }; + all_G2_vars = { B }; + + all_G1_checkers.resize(all_G1_vars.size()); + + for (size_t i = 0; i < all_G1_vars.size(); ++i) + { + all_G1_checkers[i].reset(new G1_checker_gadget(pb, *all_G1_vars[i], FMT(annotation_prefix, " all_G1_checkers_%zu", i))); + } + G2_checker.reset(new G2_checker_gadget(pb, *B, FMT(annotation_prefix, " G2_checker"))); + + assert(all_G1_vars.size() == num_G1); + assert(all_G2_vars.size() == num_G2); +} + +template +void r1cs_se_ppzksnark_proof_variable::generate_r1cs_constraints() +{ + for (auto &G1_checker : all_G1_checkers) + { + G1_checker->generate_r1cs_constraints(); + } + + G2_checker->generate_r1cs_constraints(); +} + +template +void r1cs_se_ppzksnark_proof_variable::generate_r1cs_witness(const r1cs_se_ppzksnark_proof > &proof) +{ + std::vector > > G1_elems; + std::vector > > G2_elems; + + G1_elems = { proof.A, proof.C }; + G2_elems = { proof.B }; + + assert(G1_elems.size() == all_G1_vars.size()); + assert(G2_elems.size() == all_G2_vars.size()); + + for (size_t i = 0; i < G1_elems.size(); ++i) + { + all_G1_vars[i]->generate_r1cs_witness(G1_elems[i]); + } + + for (size_t i = 0; i < G2_elems.size(); ++i) + { + all_G2_vars[i]->generate_r1cs_witness(G2_elems[i]); + } + + for (auto &G1_checker : all_G1_checkers) + { + G1_checker->generate_r1cs_witness(); + } + + G2_checker->generate_r1cs_witness(); +} + +template +size_t r1cs_se_ppzksnark_proof_variable::size() +{ + const size_t num_G1 = 2; + const size_t num_G2 = 1; + return (num_G1 * G1_variable::num_field_elems + num_G2 * G2_variable::num_field_elems); +} + +template +r1cs_se_ppzksnark_verification_key_variable::r1cs_se_ppzksnark_verification_key_variable(protoboard &pb, + const size_t input_size, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + input_size(input_size) +{ + const size_t num_G1 = 2 + (input_size + 1); + const size_t num_G2 = 3; + const size_t num_GT = 1; + + this->H.reset(new G2_variable(pb, FMT(annotation_prefix, " H"))); + this->G_alpha.reset(new G1_variable(pb, FMT(annotation_prefix, " G_alpha"))); + this->H_beta.reset(new G2_variable(pb, FMT(annotation_prefix, " H_beta"))); + this->G_gamma.reset(new G1_variable(pb, FMT(annotation_prefix, " G_gamma"))); + this->H_gamma.reset(new G2_variable(pb, FMT(annotation_prefix, " H_gamma"))); + this->G_alpha_H_beta_inv.reset(new Fqk_variable(pb, FMT(annotation_prefix, "G_alpha_H_beta_inv"))); + // assert(all_bits.size() == (G1_variable::size_in_bits() * num_G1 + G2_variable::size_in_bits() * num_G2)); + + all_G1_vars = { this->G_alpha, this->G_gamma }; + all_G2_vars = { this->H, this->H_beta, this->H_gamma }; + all_GT_vars = { this->G_alpha_H_beta_inv }; + + this->query.resize(input_size); + this->query_base.reset(new G1_variable(pb, FMT(annotation_prefix, " query_base"))); + this->all_G1_vars.emplace_back(this->query_base); + + for (size_t i = 0; i < input_size; ++i) + { + this->query[i].reset(new G1_variable(pb, FMT(annotation_prefix, " query_%zu", i))); + all_G1_vars.emplace_back(this->query[i]); + } + + for (auto &G1_var : all_G1_vars) + { + all_vars.insert(all_vars.end(), G1_var->all_vars.begin(), G1_var->all_vars.end()); + } + + for (auto &G2_var : all_G2_vars) + { + all_vars.insert(all_vars.end(), G2_var->all_vars.begin(), G2_var->all_vars.end()); + } + + for (auto >_var : all_GT_vars) + { + all_vars.insert(all_vars.end(), GT_var->all_vars.begin(), GT_var->all_vars.end()); + } + + assert(all_G1_vars.size() == num_G1); + assert(all_G2_vars.size() == num_G2); + assert(all_GT_vars.size() == num_GT); + assert(all_vars.size() == (num_G1 * G1_variable::num_variables() + num_G2 * G2_variable::num_variables() + num_GT * Fqk_variable::num_variables())); +} + +template +void r1cs_se_ppzksnark_verification_key_variable::generate_r1cs_witness(const r1cs_se_ppzksnark_verification_key > &vk) +{ + std::vector > > G1_elems; + std::vector > > G2_elems; + std::vector>> GT_elems; + + G1_elems = { vk.G_alpha, vk.G_gamma }; + G2_elems = { vk.H, vk.H_beta, vk.H_gamma }; + + // TODO: We should really have this take a processed verification key so we don't have + // to do a pairing here (or just stick the final exp'd value in the vk, which is probably + // better anyway). + libff::Fqk< other_curve > G_alpha_H_beta_inv = vk.G_alpha_H_beta.unitary_inverse(); + GT_elems = { G_alpha_H_beta_inv }; + + assert(vk.query.size() == input_size + 1); + G1_elems.emplace_back(vk.query[0]); + for (size_t i = 0; i < input_size; ++i) + { + G1_elems.emplace_back(vk.query[i+1]); + } + + assert(G1_elems.size() == all_G1_vars.size()); + assert(G2_elems.size() == all_G2_vars.size()); + assert(GT_elems.size() == all_GT_vars.size()); + + for (size_t i = 0; i < G1_elems.size(); ++i) + { + all_G1_vars[i]->generate_r1cs_witness(G1_elems[i]); + } + + for (size_t i = 0; i < G2_elems.size(); ++i) + { + all_G2_vars[i]->generate_r1cs_witness(G2_elems[i]); + } + + for (size_t i = 0; i < GT_elems.size(); ++i) + { + all_GT_vars[i]->generate_r1cs_witness(GT_elems[i]); + } +} + +template +r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable::r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable() +{ + // will be allocated outside +} + +template +r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable::r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable(protoboard &pb, + const r1cs_se_ppzksnark_verification_key > &r1cs_vk, + const std::string &annotation_prefix) +{ + query_base.reset(new G1_variable(pb, r1cs_vk.query[0], FMT(annotation_prefix, " query_base"))); + + size_t input_size = r1cs_vk.query.size() - 1; + query.resize(input_size); + for (size_t i = 0; i < input_size; ++i) + { + query[i].reset(new G1_variable(pb, r1cs_vk.query[i + 1], FMT(annotation_prefix, " query"))); + } + + G_alpha.reset(new G1_variable(pb, r1cs_vk.G_alpha, FMT(annotation_prefix, " G_alpha"))); + H_beta.reset(new G2_variable(pb, r1cs_vk.H_beta, FMT(annotation_prefix, " G_alpha"))); + G_alpha_H_beta_inv.reset( + new Fqk_variable(pb, r1cs_vk.G_alpha_H_beta.unitary_inverse(), + FMT(annotation_prefix, " G_alpha_H_beta_inv"))); + + G_gamma_pc.reset( + new G1_precomputation(pb, + r1cs_vk.G_gamma, FMT(annotation_prefix, " G_gamma_pc"))); + H_gamma_pc.reset( + new G2_precomputation(pb, + r1cs_vk.H_gamma, FMT(annotation_prefix, " H_gamma_pc"))); + H_pc.reset( + new G2_precomputation(pb, + r1cs_vk.H, FMT(annotation_prefix, " H_pc"))); +} + +template +r1cs_se_ppzksnark_verifier_process_vk_gadget::r1cs_se_ppzksnark_verifier_process_vk_gadget(protoboard &pb, + const r1cs_se_ppzksnark_verification_key_variable &vk, + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable &pvk, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + vk(vk), + pvk(pvk) +{ + pvk.query_base = vk.query_base; + pvk.query = vk.query; + + pvk.G_alpha = vk.G_alpha; + pvk.H_beta = vk.H_beta; + pvk.G_alpha_H_beta_inv = vk.G_alpha_H_beta_inv; + + pvk.G_gamma_pc.reset(new G1_precomputation()); + pvk.H_gamma_pc.reset(new G2_precomputation()); + pvk.H_pc.reset(new G2_precomputation()); + + compute_G_gamma_pc.reset( + new precompute_G1_gadget(pb, *vk.G_gamma, *pvk.G_gamma_pc, + FMT(annotation_prefix, " compute_G_gamma_pc"))); + compute_H_gamma_pc.reset( + new precompute_G2_gadget(pb, *vk.H_gamma, *pvk.H_gamma_pc, + FMT(annotation_prefix, " compute_H_gamma_pc"))); + compute_H_pc.reset( + new precompute_G2_gadget(pb, *vk.H, *pvk.H_pc, + FMT(annotation_prefix, " compute_H_pc"))); +} + +template +void r1cs_se_ppzksnark_verifier_process_vk_gadget::generate_r1cs_constraints() +{ + compute_G_gamma_pc->generate_r1cs_constraints(); + compute_H_gamma_pc->generate_r1cs_constraints(); + compute_H_pc->generate_r1cs_constraints(); +} + +template +void r1cs_se_ppzksnark_verifier_process_vk_gadget::generate_r1cs_witness() +{ + compute_G_gamma_pc->generate_r1cs_witness(); + compute_H_gamma_pc->generate_r1cs_witness(); + compute_H_pc->generate_r1cs_witness(); +} + +template +r1cs_se_ppzksnark_online_verifier_gadget::r1cs_se_ppzksnark_online_verifier_gadget(protoboard &pb, + const r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable &pvk, + const pb_variable_array &input, + const size_t elt_size, + const r1cs_se_ppzksnark_proof_variable &proof, + const pb_variable &result, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix), + pvk(pvk), + input(input), + elt_size(elt_size), + proof(proof), + result(result), + input_len(input.size()) +{ + // accumulate input and store base in acc + // TODO: Check why base (i.e., pvk.query[0]) goes in this vector + acc.reset(new G1_variable(pb, FMT(annotation_prefix, " acc"))); + std::vector > IC_terms; + for (size_t i = 0; i < pvk.query.size(); ++i) + { + IC_terms.emplace_back(*(pvk.query[i])); + } + accumulate_input.reset( + new G1_multiscalar_mul_gadget(pb, *(pvk.query_base), input, elt_size, IC_terms, *acc, FMT(annotation_prefix, " accumulate_input"))); + + // allocate results for precomputation + proof_A_G_alpha_precomp.reset(new G1_precomputation()); + proof_B_H_beta_precomp.reset(new G2_precomputation()); + + acc_precomp.reset(new G1_precomputation()); + + proof_A_precomp.reset(new G1_precomputation()); + proof_B_precomp.reset(new G2_precomputation()); + proof_C_precomp.reset(new G1_precomputation()); + + // do the necessary precomputations + proof_A_G_alpha.reset(new G1_variable(pb, FMT(annotation_prefix, " proof_A_G_alpha"))); + compute_proof_A_G_alpha.reset(new G1_add_gadget(pb, *(proof.A), *pvk.G_alpha , *proof_A_G_alpha, FMT(annotation_prefix, " compute_proof_A_G_alpha"))); + proof_B_H_beta.reset(new G2_variable(pb, FMT(annotation_prefix, " proof_B_H_beta"))); + compute_proof_B_H_beta.reset( + new G2_add_gadget(pb, + *(proof.B), + *pvk.H_beta, + *proof_B_H_beta, + FMT(annotation_prefix, " compute_proof_B_H_beta"))); + + compute_proof_A_G_alpha_precomp.reset( + new precompute_G1_gadget(pb, *proof_A_G_alpha, *proof_A_G_alpha_precomp, + FMT(annotation_prefix, " compute_proof_A_G_alpha_precomp"))); + compute_proof_B_H_beta_precomp.reset( + new precompute_G2_gadget(pb, *proof_B_H_beta, *proof_B_H_beta_precomp, + FMT(annotation_prefix, " compute_proof_B_H_beta_precomp"))); + + compute_acc_precomp.reset( + new precompute_G1_gadget(pb, *acc, *acc_precomp, + FMT(annotation_prefix, " compute_acc_precomp"))); + + compute_proof_A_precomp.reset( + new precompute_G1_gadget(pb, *(proof.A), *proof_A_precomp, + FMT(annotation_prefix, " compute_proof_A_precomp"))); + compute_proof_B_precomp.reset( + new precompute_G2_gadget(pb, *(proof.B), *proof_B_precomp, + FMT(annotation_prefix, " compute_proof_B_precomp"))); + compute_proof_C_precomp.reset( + new precompute_G1_gadget(pb, *(proof.C), *proof_C_precomp, + FMT(annotation_prefix, " compute_proof_C_precomp"))); + + // Now do the pairing checks + /** + * e(A*G^{alpha}, B*H^{beta}) = e(G^{alpha}, H^{beta}) * e(G^{acc}, H^{gamma}) * e(C, H) + * where acc = \sum_{i=0}^l input_i pvk.query[i] + * + * We check instead (the equivalent condition) + * + * e(G^{acc}, H^{gamma}) * e(C, H) / e(A*G^{alpha}, B*H^{beta}) = 1 / e(G^{alpha}, H^{beta}) + */ + first_check_passed.allocate(pb, FMT(annotation_prefix, " first_check_passed")); + first_check.reset( + new check_e_times_e_over_e_equals_value_gadget(pb, + *acc_precomp, *(pvk.H_gamma_pc), + *proof_C_precomp, *(pvk.H_pc), + *proof_A_G_alpha_precomp, *proof_B_H_beta_precomp, + *(pvk.G_alpha_H_beta_inv), + first_check_passed, + FMT(annotation_prefix, " first_check"))); + + /** + * e(A, H^{gamma}) = e(G^{gamma}, B) + */ + second_check_passed.allocate(pb, FMT(annotation_prefix, " second_check_passed")); + second_check.reset( + new check_e_equals_e_gadget(pb, + *proof_A_precomp, *(pvk.H_gamma_pc), + *(pvk.G_gamma_pc), *proof_B_precomp, + second_check_passed, + FMT(annotation_prefix, " second_check"))); + + // final constraint + all_test_results.emplace_back(first_check_passed); + all_test_results.emplace_back(second_check_passed); + + all_tests_pass.reset(new conjunction_gadget(pb, all_test_results, result, FMT(annotation_prefix, " all_tests_pass"))); +} + +template +void r1cs_se_ppzksnark_online_verifier_gadget::generate_r1cs_constraints() +{ + PROFILE_CONSTRAINTS(this->pb, "accumulate verifier input") + { + libff::print_indent(); printf("* Number of bits as an input to verifier gadget: %zu\n", input.size()); + accumulate_input->generate_r1cs_constraints(); + } + + PROFILE_CONSTRAINTS(this->pb, "rest of the verifier") + { + compute_proof_A_G_alpha->generate_r1cs_constraints(); + compute_proof_B_H_beta->generate_r1cs_constraints(); + + compute_proof_A_G_alpha_precomp->generate_r1cs_constraints(); + compute_proof_B_H_beta_precomp->generate_r1cs_constraints(); + + compute_acc_precomp->generate_r1cs_constraints(); + + compute_proof_A_precomp->generate_r1cs_constraints(); + compute_proof_B_precomp->generate_r1cs_constraints(); + compute_proof_C_precomp->generate_r1cs_constraints(); + + first_check->generate_r1cs_constraints(); + second_check->generate_r1cs_constraints(); + + all_tests_pass->generate_r1cs_constraints(); + } +} + +template +void r1cs_se_ppzksnark_online_verifier_gadget::generate_r1cs_witness() +{ + accumulate_input->generate_r1cs_witness(); + + compute_proof_A_G_alpha->generate_r1cs_witness(); + compute_proof_B_H_beta->generate_r1cs_witness(); + + compute_proof_A_G_alpha_precomp->generate_r1cs_witness(); + compute_proof_B_H_beta_precomp->generate_r1cs_witness(); + + compute_acc_precomp->generate_r1cs_witness(); + + compute_proof_A_precomp->generate_r1cs_witness(); + compute_proof_B_precomp->generate_r1cs_witness(); + compute_proof_C_precomp->generate_r1cs_witness(); + + first_check->generate_r1cs_witness(); + second_check->generate_r1cs_witness(); + + all_tests_pass->generate_r1cs_witness(); +} + +template +r1cs_se_ppzksnark_verifier_gadget::r1cs_se_ppzksnark_verifier_gadget(protoboard &pb, + const r1cs_se_ppzksnark_verification_key_variable &vk, + const pb_variable_array &input, + const size_t elt_size, + const r1cs_se_ppzksnark_proof_variable &proof, + const pb_variable &result, + const std::string &annotation_prefix) : + gadget(pb, annotation_prefix) +{ + pvk.reset(new r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable()); + compute_pvk.reset(new r1cs_se_ppzksnark_verifier_process_vk_gadget(pb, vk, *pvk, FMT(annotation_prefix, " compute_pvk"))); + online_verifier.reset(new r1cs_se_ppzksnark_online_verifier_gadget(pb, *pvk, input, elt_size, proof, result, FMT(annotation_prefix, " online_verifier"))); +} + +template +void r1cs_se_ppzksnark_verifier_gadget::generate_r1cs_constraints() +{ + PROFILE_CONSTRAINTS(this->pb, "precompute pvk") + { + compute_pvk->generate_r1cs_constraints(); + } + + PROFILE_CONSTRAINTS(this->pb, "online verifier") + { + online_verifier->generate_r1cs_constraints(); + } +} + +template +void r1cs_se_ppzksnark_verifier_gadget::generate_r1cs_witness() +{ + compute_pvk->generate_r1cs_witness(); + online_verifier->generate_r1cs_witness(); +} + +} // libsnark + +#endif // R1CS_SE_PPZKSNARK_VERIFIER_GADGET_TCC_ diff --git a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_se_ppzksnark_verifier_gadget.cpp b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_se_ppzksnark_verifier_gadget.cpp new file mode 100644 index 00000000..12e7f253 --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_se_ppzksnark_verifier_gadget.cpp @@ -0,0 +1,427 @@ +/** + ***************************************************************************** + * @author This file is part of libsnark, developed by SCIPR Lab + * and contributors (see AUTHORS). + * @copyright MIT license (see LICENSE file) + *****************************************************************************/ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace libsnark; + +template +void dump_constraints(const protoboard &pb) +{ +#ifdef DEBUG + for (auto s : pb.constraint_system.constraint_annotations) + { + printf("constraint: %s\n", s.second.c_str()); + } +#endif +} + +template +void test_verifier(const std::string &annotation_A, const std::string &annotation_B) +{ + typedef libff::Fr FieldT_A; + typedef libff::Fr FieldT_B; + + const size_t num_constraints = 50; + const size_t primary_input_size = 3; + + r1cs_example example = generate_r1cs_example_with_field_input(num_constraints, primary_input_size); + assert(example.primary_input.size() == primary_input_size); + + assert(example.constraint_system.is_satisfied(example.primary_input, example.auxiliary_input)); + const r1cs_se_ppzksnark_keypair keypair = r1cs_se_ppzksnark_generator(example.constraint_system); + const r1cs_se_ppzksnark_proof pi = r1cs_se_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input); + bool bit = r1cs_se_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, pi); + assert(bit); + + const size_t elt_size = FieldT_A::size_in_bits(); + const size_t primary_input_size_in_bits = elt_size * primary_input_size; + + protoboard pb; + + pb_variable_array primary_input_bits; + primary_input_bits.allocate(pb, primary_input_size_in_bits, "primary_input_bits"); + + r1cs_se_ppzksnark_proof_variable proof(pb, "proof"); + + r1cs_se_ppzksnark_verification_key_variable vk(pb, primary_input_size, "vk"); + + pb_variable result; + result.allocate(pb, "result"); + + r1cs_se_ppzksnark_verifier_gadget verifier(pb, vk, primary_input_bits, elt_size, proof, result, "verifier"); + + PROFILE_CONSTRAINTS(pb, "check that proofs lies on the curve") + { + proof.generate_r1cs_constraints(); + } + verifier.generate_r1cs_constraints(); + + libff::bit_vector input_as_bits; + for (const FieldT_A &el : example.primary_input) + { + libff::bit_vector v = libff::convert_field_element_to_bit_vector(el, elt_size); + input_as_bits.insert(input_as_bits.end(), v.begin(), v.end()); + } + + primary_input_bits.fill_with_bits(pb, input_as_bits); + + vk.generate_r1cs_witness(keypair.vk); + proof.generate_r1cs_witness(pi); + verifier.generate_r1cs_witness(); + pb.val(result) = FieldT_B::one(); + + printf("positive test:\n"); + assert(pb.is_satisfied()); + + pb.val(primary_input_bits[0]) = FieldT_B::one() - pb.val(primary_input_bits[0]); + verifier.generate_r1cs_witness(); + pb.val(result) = FieldT_B::one(); + + printf("negative test:\n"); + assert(!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()); +} + +template +void test_hardcoded_verifier(const std::string &annotation_A, const std::string &annotation_B) +{ + typedef libff::Fr FieldT_A; + typedef libff::Fr FieldT_B; + + const size_t num_constraints = 50; + const size_t primary_input_size = 3; + + r1cs_example example = generate_r1cs_example_with_field_input(num_constraints, primary_input_size); + assert(example.primary_input.size() == primary_input_size); + + assert(example.constraint_system.is_satisfied(example.primary_input, example.auxiliary_input)); + const r1cs_se_ppzksnark_keypair keypair = r1cs_se_ppzksnark_generator(example.constraint_system); + const r1cs_se_ppzksnark_proof pi = r1cs_se_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input); + bool bit = r1cs_se_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, pi); + assert(bit); + + const size_t elt_size = FieldT_A::size_in_bits(); + const size_t primary_input_size_in_bits = elt_size * primary_input_size; + + protoboard pb; + r1cs_se_ppzksnark_preprocessed_r1cs_se_ppzksnark_verification_key_variable hardcoded_vk(pb, keypair.vk, "hardcoded_vk"); + pb_variable_array primary_input_bits; + primary_input_bits.allocate(pb, primary_input_size_in_bits, "primary_input_bits"); + + r1cs_se_ppzksnark_proof_variable proof(pb, "proof"); + + pb_variable result; + result.allocate(pb, "result"); + + r1cs_se_ppzksnark_online_verifier_gadget online_verifier(pb, hardcoded_vk, primary_input_bits, elt_size, proof, result, "online_verifier"); + + PROFILE_CONSTRAINTS(pb, "check that proofs lies on the curve") + { + proof.generate_r1cs_constraints(); + } + online_verifier.generate_r1cs_constraints(); + + libff::bit_vector input_as_bits; + for (const FieldT_A &el : example.primary_input) + { + libff::bit_vector v = libff::convert_field_element_to_bit_vector(el, elt_size); + input_as_bits.insert(input_as_bits.end(), v.begin(), v.end()); + } + + primary_input_bits.fill_with_bits(pb, input_as_bits); + + proof.generate_r1cs_witness(pi); + online_verifier.generate_r1cs_witness(); + pb.val(result) = FieldT_B::one(); + + printf("positive test:\n"); + assert(pb.is_satisfied()); + + pb.val(primary_input_bits[0]) = FieldT_B::one() - pb.val(primary_input_bits[0]); + online_verifier.generate_r1cs_witness(); + pb.val(result) = FieldT_B::one(); + + printf("negative test:\n"); + assert(!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()); +} + +template class VarT, template class MulT> +void test_mul(const std::string &annotation) +{ + typedef typename FpExtT::my_Fp FieldT; + + protoboard pb; + VarT x(pb, "x"); + VarT y(pb, "y"); + VarT xy(pb, "xy"); + MulT mul(pb, x, y, xy, "mul"); + mul.generate_r1cs_constraints(); + + for (size_t i = 0; i < 10; ++i) + { + const FpExtT x_val = FpExtT::random_element(); + const FpExtT y_val = FpExtT::random_element(); + x.generate_r1cs_witness(x_val); + y.generate_r1cs_witness(y_val); + mul.generate_r1cs_witness(); + const FpExtT res = xy.get_element(); + assert(res == x_val*y_val); + assert(pb.is_satisfied()); + } + printf("number of constraints for %s_mul = %zu\n", annotation.c_str(), pb.num_constraints()); +} + +template class VarT, template class SqrT> +void test_sqr(const std::string &annotation) +{ + typedef typename FpExtT::my_Fp FieldT; + + protoboard pb; + VarT x(pb, "x"); + VarT xsq(pb, "xsq"); + SqrT sqr(pb, x, xsq, "sqr"); + sqr.generate_r1cs_constraints(); + + for (size_t i = 0; i < 10; ++i) + { + const FpExtT x_val = FpExtT::random_element(); + x.generate_r1cs_witness(x_val); + sqr.generate_r1cs_witness(); + const FpExtT res = xsq.get_element(); + assert(res == x_val.squared()); + assert(pb.is_satisfied()); + } + printf("number of constraints for %s_sqr = %zu\n", annotation.c_str(), pb.num_constraints()); +} + +template class VarT, template class CycloSqrT> +void test_cyclotomic_sqr(const std::string &annotation) +{ + typedef libff::Fqk FpExtT; + typedef typename FpExtT::my_Fp FieldT; + + + protoboard pb; + VarT x(pb, "x"); + VarT xsq(pb, "xsq"); + CycloSqrT sqr(pb, x, xsq, "sqr"); + sqr.generate_r1cs_constraints(); + + for (size_t i = 0; i < 10; ++i) + { + FpExtT x_val = FpExtT::random_element(); + x_val = ppT::final_exponentiation(x_val); + + x.generate_r1cs_witness(x_val); + sqr.generate_r1cs_witness(); + const FpExtT res = xsq.get_element(); + assert(res == x_val.squared()); + assert(pb.is_satisfied()); + } + printf("number of constraints for %s_cyclotomic_sqr = %zu\n", annotation.c_str(), pb.num_constraints()); +} + +template class VarT> +void test_Frobenius(const std::string &annotation) +{ + typedef typename FpExtT::my_Fp FieldT; + + for (size_t i = 0; i < 100; ++i) + { + protoboard pb; + VarT x(pb, "x"); + VarT x_frob = x.Frobenius_map(i); + + const FpExtT x_val = FpExtT::random_element(); + x.generate_r1cs_witness(x_val); + x_frob.evaluate(); + const FpExtT res = x_frob.get_element(); + assert(res == x_val.Frobenius_map(i)); + assert(pb.is_satisfied()); + } + + printf("Frobenius map for %s correct\n", annotation.c_str()); +} + +template +void test_full_pairing(const std::string &annotation) +{ + typedef libff::Fr FieldT; + + protoboard pb; + libff::G1 > P_val = libff::Fr >::random_element() * libff::G1 >::one(); + libff::G2 > Q_val = libff::Fr >::random_element() * libff::G2 >::one(); + + G1_variable P(pb, "P"); + G2_variable Q(pb, "Q"); + G1_precomputation prec_P; + G2_precomputation prec_Q; + + precompute_G1_gadget compute_prec_P(pb, P, prec_P, "compute_prec_P"); + precompute_G2_gadget compute_prec_Q(pb, Q, prec_Q, "compute_prec_Q"); + + Fqk_variable miller_result(pb, "miller_result"); + mnt_miller_loop_gadget miller(pb, prec_P, prec_Q, miller_result, "miller"); + pb_variable result_is_one; + result_is_one.allocate(pb, "result_is_one"); + final_exp_gadget finexp(pb, miller_result, result_is_one, "finexp"); + + PROFILE_CONSTRAINTS(pb, "precompute P") + { + compute_prec_P.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "precompute Q") + { + compute_prec_Q.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "Miller loop") + { + miller.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "final exp") + { + finexp.generate_r1cs_constraints(); + } + PRINT_CONSTRAINT_PROFILING(); + + P.generate_r1cs_witness(P_val); + compute_prec_P.generate_r1cs_witness(); + Q.generate_r1cs_witness(Q_val); + compute_prec_Q.generate_r1cs_witness(); + miller.generate_r1cs_witness(); + finexp.generate_r1cs_witness(); + assert(pb.is_satisfied()); + + libff::affine_ate_G1_precomp > native_prec_P = other_curve::affine_ate_precompute_G1(P_val); + libff::affine_ate_G2_precomp > native_prec_Q = other_curve::affine_ate_precompute_G2(Q_val); + libff::Fqk > native_miller_result = other_curve::affine_ate_miller_loop(native_prec_P, native_prec_Q); + + libff::Fqk > native_finexp_result = other_curve::final_exponentiation(native_miller_result); + printf("Must match:\n"); + finexp.result->get_element().print(); + native_finexp_result.print(); + + assert(finexp.result->get_element() == native_finexp_result); + + printf("number of constraints for full pairing (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); +} + +template +void test_full_precomputed_pairing(const std::string &annotation) +{ + typedef libff::Fr FieldT; + + protoboard pb; + libff::G1 > P_val = libff::Fr >::random_element() * libff::G1 >::one(); + libff::G2 > Q_val = libff::Fr >::random_element() * libff::G2 >::one(); + + G1_precomputation prec_P(pb, P_val, "prec_P"); + G2_precomputation prec_Q(pb, Q_val, "prec_Q"); + + Fqk_variable miller_result(pb, "miller_result"); + mnt_miller_loop_gadget miller(pb, prec_P, prec_Q, miller_result, "miller"); + pb_variable result_is_one; + result_is_one.allocate(pb, "result_is_one"); + final_exp_gadget finexp(pb, miller_result, result_is_one, "finexp"); + + PROFILE_CONSTRAINTS(pb, "Miller loop") + { + miller.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "final exp") + { + finexp.generate_r1cs_constraints(); + } + PRINT_CONSTRAINT_PROFILING(); + + miller.generate_r1cs_witness(); + finexp.generate_r1cs_witness(); + assert(pb.is_satisfied()); + + libff::affine_ate_G1_precomp > native_prec_P = other_curve::affine_ate_precompute_G1(P_val); + libff::affine_ate_G2_precomp > native_prec_Q = other_curve::affine_ate_precompute_G2(Q_val); + libff::Fqk > native_miller_result = other_curve::affine_ate_miller_loop(native_prec_P, native_prec_Q); + + libff::Fqk > native_finexp_result = other_curve::final_exponentiation(native_miller_result); + printf("Must match:\n"); + finexp.result->get_element().print(); + native_finexp_result.print(); + + assert(finexp.result->get_element() == native_finexp_result); + + printf("number of constraints for full precomputed pairing (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); +} + +int main(void) +{ + libff::start_profiling(); + libff::mnt4_pp::init_public_params(); + libff::mnt6_pp::init_public_params(); + + test_mul("mnt4_Fp2"); + test_sqr("mnt4_Fp2"); + + test_mul("mnt4_Fp4"); + test_sqr("mnt4_Fp4"); + test_cyclotomic_sqr("mnt4_Fp4"); + test_exponentiation_gadget(libff::mnt4_final_exponent_last_chunk_abs_of_w0, "mnt4_Fq4"); + test_Frobenius("mnt4_Fq4"); + + test_mul("mnt6_Fp3"); + test_sqr("mnt6_Fp3"); + + test_mul("mnt6_Fp6"); + test_sqr("mnt6_Fp6"); + test_cyclotomic_sqr("mnt6_Fp6"); + test_exponentiation_gadget(libff::mnt6_final_exponent_last_chunk_abs_of_w0, "mnt6_Fq6"); + test_Frobenius("mnt6_Fq6"); + + test_G2_checker_gadget("mnt4"); + test_G2_checker_gadget("mnt6"); + + test_G1_variable_precomp("mnt4"); + test_G1_variable_precomp("mnt6"); + + test_G2_variable_precomp("mnt4"); + test_G2_variable_precomp("mnt6"); + + test_mnt_miller_loop("mnt4"); + test_mnt_miller_loop("mnt6"); + + test_mnt_e_over_e_miller_loop("mnt4"); + test_mnt_e_over_e_miller_loop("mnt6"); + + test_mnt_e_times_e_over_e_miller_loop("mnt4"); + test_mnt_e_times_e_over_e_miller_loop("mnt6"); + + test_full_pairing("mnt4"); + test_full_pairing("mnt6"); + + test_full_precomputed_pairing("mnt4"); + test_full_precomputed_pairing("mnt6"); + + test_verifier("mnt4", "mnt6"); + test_verifier("mnt6", "mnt4"); + + test_hardcoded_verifier("mnt4", "mnt6"); + test_hardcoded_verifier("mnt6", "mnt4"); +}