From 7875f95a227b254335f32649880c5a8d366fcf81 Mon Sep 17 00:00:00 2001 From: HarryR Date: Thu, 24 Oct 2019 20:43:59 +0100 Subject: [PATCH 1/3] Pairing product gadget for Groth16 verification --- CMakeLists.txt | 13 ++ .../gadgets/pairing/mnt_pairing_params.hpp | 3 + .../gadgets/pairing/pairing_checks.hpp | 50 ++++++++ .../gadgets/pairing/pairing_checks.tcc | 111 ++++++++++++++++++ .../gadgets/pairing/pairing_params.hpp | 3 + .../test_r1cs_ppzksnark_verifier_gadget.cpp | 69 +++++++++++ 6 files changed, 249 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4d4e22..f775fea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,12 @@ option( ON ) +option( + USE_ASAN + "Enable Address Sanitizer" + OFF +) + set( OPT_FLAGS "" @@ -220,6 +226,13 @@ if("${USE_ASM}") add_definitions(-DUSE_ASM) endif() +if("${USE_ASAN}") + set( + CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -fsanitize=address" + ) +endif() + if("${USE_LINKED_LIBRARIES}") # libfqfft find_path(LIBFQFFT_INCLUDE_DIR NAMES libfqfft) diff --git a/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp b/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp index 74f75ff..4eafb0f 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/mnt_pairing_params.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace libsnark { @@ -61,6 +62,7 @@ class pairing_selector { typedef libff::mnt6_pp other_curve_type; + typedef mnt_miller_loop_gadget miller_loop_gadget; 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; @@ -91,6 +93,7 @@ class pairing_selector { typedef libff::mnt4_pp other_curve_type; + typedef mnt_miller_loop_gadget miller_loop_gadget; 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; diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp index 162611c..1ed9324 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp @@ -88,6 +88,56 @@ class check_e_equals_ee_gadget : public gadget > { void generate_r1cs_witness(); }; + +/** Pair of precomputed inputs for pairing */ +template +class pairing_input_pair +{ +public: + const G1_precomputation &g1; + const G2_precomputation &g2; + + pairing_input_pair( const G1_precomputation &_g1, const G2_precomputation &_g2 ) + : g1(_g1), g2(_g2) + {} +}; + + +/** +* Compute the product of multiple pairings +* +* e(P1,Q1) * e(P2,Q2) * ... * e(Pn,Qn) +* +* Equivalent to Ethereum ECPAIRING opcode. +* +* Caller must precompute the inputs. +*/ +template +class pairing_product_gadget : public gadget > { +public: + typedef libff::Fr FieldT; + + std::vector > m_miller_results; + std::vector > m_miller_loops; + std::vector > m_product_results; + std::vector > m_product; + std::shared_ptr > m_final_exp; + pb_variable result_is_one; + + pairing_product_gadget(protoboard &pb, + const std::vector> &pairs, + const std::string &annotation_prefix); + + void generate_r1cs_constraints(); + void generate_r1cs_witness(); + + Fqk_variable& result(); + + /** before final exponentiation */ + Fqk_variable& raw_result(); +}; + + } // libsnark #include diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc index 65aa4a7..6d7827d 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc @@ -14,6 +14,7 @@ #ifndef PAIRING_CHECKS_TCC_ #define PAIRING_CHECKS_TCC_ + namespace libsnark { template @@ -88,6 +89,116 @@ void check_e_equals_ee_gadget::generate_r1cs_witness() check_finexp->generate_r1cs_witness(); } + + +template +pairing_product_gadget::pairing_product_gadget( + protoboard &pb, + const std::vector> &pairs, + const std::string &annotation_prefix +) : + gadget(pb, annotation_prefix) +{ + assert( pairs.size() > 0 ); + result_is_one.allocate(pb, FMT(annotation_prefix, ".result_is_one")); + + // XXX: must be reserved, otherwise emplace_back will call destructor on miller loop during move which invalidates shared_ptr + m_miller_results.reserve(pairs.size()); + m_miller_loops.reserve(pairs.size()); + if( pairs.size() > 1 ) { + m_product_results.reserve(pairs.size() - 1); + m_product.reserve(pairs.size() - 1); + } + + int i = 0; + for( const auto &p_ref : pairs ) + { + m_miller_results.emplace_back(pb, FMT(annotation_prefix, ".result_%d", i)); + m_miller_loops.emplace_back( + pb, + p_ref.g1, + p_ref.g2, + m_miller_results[i], + FMT(annotation_prefix, ".miller_loop_%d", i)); + + if( i > 0 ) + { + m_product_results.emplace_back(pb, FMT(annotation_prefix, ".product_result_%d", i)); + + if( m_product_results.size() == 1 ) + { + assert( m_miller_results.size() == 2 ); + // pr[0] = result[0] * result[1] + m_product.emplace_back( + pb, + m_miller_results[0], + m_miller_results.back(), + m_product_results.back(), + FMT(annotation_prefix, ".product_%d", i)); + } + else { + // pr[i] = pr[i-1] * result[i] + m_product.emplace_back( + pb, + m_product_results[ m_product_results.size() - 2 ] , // Previous product + m_miller_results.back(), + m_product_results.back(), + FMT(annotation_prefix, ".product_%d", i)); + } + } + + i += 1; + } + + m_final_exp.reset(new final_exp_gadget(pb, raw_result(), result_is_one, FMT(annotation_prefix, ".check_is_one"))); +} + + +template +void pairing_product_gadget::generate_r1cs_constraints() +{ + for( auto &m : m_miller_loops ) + m.generate_r1cs_constraints(); + + for( auto &p : m_product ) + p.generate_r1cs_constraints(); + + m_final_exp->generate_r1cs_constraints(); +} + + +template +void pairing_product_gadget::generate_r1cs_witness() +{ + for( auto &m : m_miller_loops ) + m.generate_r1cs_witness(); + + for( auto &p : m_product ) + p.generate_r1cs_witness(); + + m_final_exp->generate_r1cs_witness(); +} + + +template +Fqk_variable& pairing_product_gadget::result() +{ + return *m_final_exp->result; +} + + +template +Fqk_variable& pairing_product_gadget::raw_result() +{ + if( m_product_results.size() > 0 ) { + return m_product_results.back(); + } + + // When there is only one pairing, and no product... + return m_miller_results.back(); +} + + } // libsnark #endif // PAIRING_CHECKS_TCC_ diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp b/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp index e71c07a..1525e81 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_params.hpp @@ -104,6 +104,9 @@ using Fqk_sqr_gadget = typename pairing_selector::Fqk_sqr_gadget_type; template using other_curve = typename pairing_selector::other_curve_type; +template +using miller_loop_gadget = typename pairing_selector::miller_loop_gadget; + template using e_over_e_miller_loop_gadget = typename pairing_selector::e_over_e_miller_loop_gadget_type; template diff --git a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp index 5f3b642..51d9e43 100644 --- a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp +++ b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp @@ -328,6 +328,72 @@ void test_full_pairing(const std::string &annotation) printf("number of constraints for full pairing (Fr is %s) = %zu\n", annotation.c_str(), pb.num_constraints()); } + +template +void test_pairing_product_gadget(const std::string &annotation) +{ + typedef libff::Fr FieldT; + + protoboard pb; + const auto P_val = libff::Fr >::random_element() * libff::G1 >::one(); + const auto nP_val = libff::Fr >::random_element() * libff::G1 >::one(); + const auto Q_val = libff::Fr >::random_element() * libff::G2 >::one(); + //const auto nP_val = -P_val; + + G1_variable P(pb, "P"); + G1_variable nP(pb, "nP"); + G2_variable Q(pb, "Q"); + G1_precomputation prec_P; + G1_precomputation prec_nP; + G2_precomputation prec_Q; + + precompute_G1_gadget compute_prec_P(pb, P, prec_P, "compute_prec_P"); + precompute_G1_gadget compute_prec_nP(pb, nP, prec_nP, "compute_prec_nP"); + precompute_G2_gadget compute_prec_Q(pb, Q, prec_Q, "compute_prec_Q"); + + std::vector> pairs; + pairs.emplace_back(prec_P, prec_Q); + pairs.emplace_back(prec_nP, prec_Q); + pairing_product_gadget ppg(pb, pairs, "ppg"); + + PROFILE_CONSTRAINTS(pb, "Precompute points") { + compute_prec_P.generate_r1cs_constraints(); + compute_prec_nP.generate_r1cs_constraints(); + compute_prec_Q.generate_r1cs_constraints(); + } + + PROFILE_CONSTRAINTS(pb, "Pairing Product Gadget") + { + ppg.generate_r1cs_constraints(); + } + PRINT_CONSTRAINT_PROFILING(); + + P.generate_r1cs_witness(P_val); + compute_prec_P.generate_r1cs_witness(); + + nP.generate_r1cs_witness(nP_val); + compute_prec_nP.generate_r1cs_witness(); + + Q.generate_r1cs_witness(Q_val); + compute_prec_Q.generate_r1cs_witness(); + + ppg.generate_r1cs_witness(); + + libff::affine_ate_G1_precomp > native_prec_P = other_curve::affine_ate_precompute_G1(P_val); + libff::affine_ate_G1_precomp > native_prec_nP = other_curve::affine_ate_precompute_G1(nP_val); + libff::affine_ate_G2_precomp > native_prec_Q = other_curve::affine_ate_precompute_G2(Q_val); + libff::Fqk > native_miller_result_1 = other_curve::affine_ate_miller_loop(native_prec_P, native_prec_Q); + libff::Fqk > native_miller_result_2 = other_curve::affine_ate_miller_loop(native_prec_nP, native_prec_Q); + libff::Fqk > native_miller_product = native_miller_result_1 * native_miller_result_2; + libff::Fqk > native_finexp_result = other_curve::final_exponentiation(native_miller_product); + + assert(pb.is_satisfied()); + assert(ppg.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) { @@ -427,4 +493,7 @@ int main(void) test_hardcoded_verifier("mnt4", "mnt6"); test_hardcoded_verifier("mnt6", "mnt4"); + + test_pairing_product_gadget("mnt4"); + test_pairing_product_gadget("mnt6"); } From 600456b1a794f788509f2596394a37baa219560c Mon Sep 17 00:00:00 2001 From: HarryR Date: Sun, 27 Oct 2019 23:06:50 +0000 Subject: [PATCH 2/3] Work-in-progress Gro16 verifier gadget --- .../gadgets/pairing/pairing_checks.hpp | 6 + .../gadgets/pairing/pairing_checks.tcc | 65 ++- .../gadgetlib1/gadgets/verifiers/gro16.hpp | 460 ++++++++++++++++++ .../test_r1cs_ppzksnark_verifier_gadget.cpp | 72 ++- .../r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp | 9 +- .../r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.tcc | 40 +- 6 files changed, 596 insertions(+), 56 deletions(-) create mode 100644 libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp index 1ed9324..98e09be 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp @@ -119,6 +119,7 @@ class pairing_product_gadget : public gadget > { std::vector > m_miller_results; std::vector > m_miller_loops; + std::vector> m_precomputed_loops; std::vector > m_product_results; std::vector > m_product; std::shared_ptr > m_final_exp; @@ -128,6 +129,11 @@ class pairing_product_gadget : public gadget > { const std::vector> &pairs, const std::string &annotation_prefix); + pairing_product_gadget(protoboard &pb, + const std::vector> &pairs, + const std::vector> &precomputed_loops, + const std::string &annotation_prefix); + void generate_r1cs_constraints(); void generate_r1cs_witness(); diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc index 6d7827d..897a255 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.tcc @@ -95,6 +95,7 @@ template pairing_product_gadget::pairing_product_gadget( protoboard &pb, const std::vector> &pairs, + const std::vector> &precomputed_loops, const std::string &annotation_prefix ) : gadget(pb, annotation_prefix) @@ -105,55 +106,53 @@ pairing_product_gadget::pairing_product_gadget( // XXX: must be reserved, otherwise emplace_back will call destructor on miller loop during move which invalidates shared_ptr m_miller_results.reserve(pairs.size()); m_miller_loops.reserve(pairs.size()); - if( pairs.size() > 1 ) { - m_product_results.reserve(pairs.size() - 1); - m_product.reserve(pairs.size() - 1); + if( pairs.size() > 1 ) + { + const auto x = pairs.size() - 1 + precomputed_loops.size(); + m_product_results.reserve(x); + m_product.reserve(x); } + // Compute miller loops of e(P_i,Q_i), and compute their product int i = 0; for( const auto &p_ref : pairs ) { m_miller_results.emplace_back(pb, FMT(annotation_prefix, ".result_%d", i)); - m_miller_loops.emplace_back( - pb, - p_ref.g1, - p_ref.g2, - m_miller_results[i], - FMT(annotation_prefix, ".miller_loop_%d", i)); + m_miller_loops.emplace_back(pb, p_ref.g1, p_ref.g2, m_miller_results[i], FMT(annotation_prefix, ".miller_loop_%d", i)); if( i > 0 ) { + const auto &last_result = raw_result(); m_product_results.emplace_back(pb, FMT(annotation_prefix, ".product_result_%d", i)); - - if( m_product_results.size() == 1 ) - { - assert( m_miller_results.size() == 2 ); - // pr[0] = result[0] * result[1] - m_product.emplace_back( - pb, - m_miller_results[0], - m_miller_results.back(), - m_product_results.back(), - FMT(annotation_prefix, ".product_%d", i)); - } - else { - // pr[i] = pr[i-1] * result[i] - m_product.emplace_back( - pb, - m_product_results[ m_product_results.size() - 2 ] , // Previous product - m_miller_results.back(), - m_product_results.back(), - FMT(annotation_prefix, ".product_%d", i)); - } + m_product.emplace_back(pb, last_result, m_miller_results.back(), m_product_results.back(), FMT(annotation_prefix, ".product_%d", i)); } i += 1; } + // Include precomputed pairings in the resulting product + for( const auto &x: precomputed_loops ) + { + const auto &last_result = raw_result(); + m_product_results.emplace_back(pb, FMT(annotation_prefix, ".product_result_%d", i)); + m_product.emplace_back(pb, last_result, x, m_product_results.back(), FMT(annotation_prefix, ".product_%d", i)); + i += 1; + } + m_final_exp.reset(new final_exp_gadget(pb, raw_result(), result_is_one, FMT(annotation_prefix, ".check_is_one"))); } +template +pairing_product_gadget::pairing_product_gadget( + protoboard &pb, + const std::vector> &pairs, + const std::string &annotation_prefix +) : + pairing_product_gadget(pb, pairs, {}, annotation_prefix) +{} + + template void pairing_product_gadget::generate_r1cs_constraints() { @@ -190,12 +189,10 @@ Fqk_variable& pairing_product_gadget::result() template Fqk_variable& pairing_product_gadget::raw_result() { - if( m_product_results.size() > 0 ) { + if( m_product_results.size() > 0 ) return m_product_results.back(); - } - // When there is only one pairing, and no product... - return m_miller_results.back(); + return m_miller_results[0]; } diff --git a/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp b/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp new file mode 100644 index 0000000..3f4581e --- /dev/null +++ b/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp @@ -0,0 +1,460 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace libsnark { + + +/** +* Holds a Groth16 proof with its inputs +* The A, B and C points are validated +*/ +template +class gro16_proof_var : public gadget > { +public: + typedef libff::Fr FieldT; + typedef G1_variable G1VarT; + typedef G2_variable G2VarT; + typedef pb_variable FieldVarT; + + G1VarT A; + G2VarT B; + G1VarT C; + + // XXX: do we need a mode where we don't validate the proof points? + G1_checker_gadget m_A_checker; + G2_checker_gadget m_B_checker; + G1_checker_gadget m_C_checker; + + gro16_proof_var( + protoboard &pb, + const G1VarT &_A, + const G2VarT &_B, + const G1VarT &_C, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + A(_A), + B(_B), + C(_C), + m_A_checker(pb, A, FMT(annotation_prefix, ".A_checker")), + m_B_checker(pb, B, FMT(annotation_prefix, ".B_checker")), + m_C_checker(pb, C, FMT(annotation_prefix, ".C_checker")) + { } + + gro16_proof_var( + protoboard &pb, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + A(pb, FMT(this->annotation_prefix, ".A")), + B(pb, FMT(this->annotation_prefix, ".B")), + C(pb, FMT(this->annotation_prefix, ".C")), + m_A_checker(pb, A, FMT(annotation_prefix, ".A_checker")), + m_B_checker(pb, B, FMT(annotation_prefix, ".B_checker")), + m_C_checker(pb, C, FMT(annotation_prefix, ".C_checker")) + { + + } + + void generate_r1cs_constraints() + { + m_A_checker.generate_r1cs_constraints(); + m_B_checker.generate_r1cs_constraints(); + m_C_checker.generate_r1cs_constraints(); + } + + /** Variables are already filled with values */ + void generate_r1cs_witness() + { + m_A_checker.generate_r1cs_witness(); + m_B_checker.generate_r1cs_witness(); + m_C_checker.generate_r1cs_witness(); + } + + /** Fill variables from proof with inputs */ + void generate_r1cs_witness( + const r1cs_gg_ppzksnark_proof> &proof + ) { + A.generate_r1cs_witness(proof.g_A); + B.generate_r1cs_witness(proof.g_B); + C.generate_r1cs_witness(proof.g_C); + generate_r1cs_witness(); + } +}; + + +/** +* Holds a Groth16 verification key as variables +* The points of the verification key are validated +* This will be given to the preprocessor gadget +*/ +template +class gro16_vk_var : public gadget > { +public: + typedef libff::Fr FieldT; + typedef G1_variable G1VarT; + typedef G2_variable G2VarT; + + G1VarT m_alpha; + G2VarT m_beta; + G2VarT m_gamma; + G2VarT m_delta; + + size_t n_inputs; + std::vector m_IC; + + std::vector> m_g1_checkers; + std::vector> m_g2_checkers; + + void _init_checkers() + { + auto pb = this->pb; + const auto annotation_prefix = this->annotation_prefix; + + m_g1_checkers.reserve(n_inputs + 1); + m_g1_checkers.emplace_back(pb, m_alpha, FMT(annotation_prefix, ".alpha_checker")); + + m_g2_checkers.reserve(3); + m_g2_checkers.emplace_back(pb, m_beta, FMT(annotation_prefix, ".beta_checker")); + m_g2_checkers.emplace_back(pb, m_gamma, FMT(annotation_prefix, ".gamma_checker")); + m_g2_checkers.emplace_back(pb, m_delta, FMT(annotation_prefix, ".delta_checker")); + + assert( n_inputs > 0 ); + for( size_t i = 0; i < (n_inputs + 1); i++ ) + { + m_IC.emplace_back(pb, FMT(annotation_prefix, ".input_%d", i)); + m_g1_checkers.emplace_back(pb, m_IC.back(), FMT(annotation_prefix, ".IC_checker_%d", i)); + } + } + + /** + * Construct new variables to hold the verification key + * For when the verification key is a secret input + */ + gro16_vk_var( + protoboard &pb, + size_t _n_inputs, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + m_alpha(pb, FMT(annotation_prefix, ".alpha")), + m_beta(pb, FMT(annotation_prefix, ".beta")), + m_gamma(pb, FMT(annotation_prefix, ".gamma")), + m_delta(pb, FMT(annotation_prefix, ".delta")), + n_inputs(_n_inputs) + { + _init_checkers(); + } + + /** + * Fill verification key from already existing variables + * When the verification key comes from somewhere else within the circuit + */ + gro16_vk_var( + protoboard &pb, + const G1VarT &alpha, + const G2VarT &beta, + const G2VarT &gamma, + const G2VarT &delta, + const std::vector &IC, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + m_alpha(alpha), + m_beta(beta), + m_gamma(gamma), + m_delta(delta), + n_inputs(IC.size()) + { + _init_checkers(); + } + + void generate_r1cs_constraints() + { + for( auto &gadget : m_g1_checkers ) + gadget.generate_r1cs_constraints(); + + for( auto &gadget : m_g2_checkers ) + gadget.generate_r1cs_constraints(); + } + + /** When the VK variable values have already been filled */ + void generate_r1cs_witness() + { + for( auto &gadget : m_g1_checkers ) + gadget.generate_r1cs_constraints(); + + for( auto &gadget : m_g2_checkers ) + gadget.generate_r1cs_constraints(); + } + + /** Fill variable values before witness of point checkers */ + void generate_r1cs_witness( + const libff::G1 &alpha, + const libff::G2 &beta, + const libff::G2 &gamma, + const libff::G2 &delta, + const std::vector> &IC + ) { + m_alpha.generate_r1cs_witness(alpha); + m_beta.generate_r1cs_witness(beta); + m_gamma.generate_r1cs_witness(gamma); + m_delta.generate_r1cs_witness(delta); + + assert( m_IC.size() == IC.size() ); + int i = 0; + for( const auto& x: IC ) + m_IC[i++].generate_r1cs_witness(x); + + generate_r1cs_witness(); + } + + template + void generate_r1cs_witness( + const libsnark::r1cs_gg_ppzksnark_verification_key &vk + ) { + m_alpha.generate_r1cs_witness(vk.alpha_g1); + m_beta.generate_r1cs_witness(vk.beta_g2); + m_gamma.generate_r1cs_witness(vk.gamma_g2); + m_delta.generate_r1cs_witness(vk.delta_g2); + + m_IC[0].generate_r1cs_witness(vk.gamma_ABC_g1.first); + int i = 1; + for( const auto& x: vk.gamma_ABC_g1.rest.indices ) + m_IC[i++].generate_r1cs_witness(vk.gamma_ABC_g1.rest.values[x]); + + generate_r1cs_witness(); + } +}; + + +/** +* Given the verification key variables +* Pre-compute the miller loop coefficients +* And the product of e(alpha,beta) +* This becomes cheaper when used multiple times, to avoid computing coefficients again +*/ +template +class gro16_vk_preprocessor { +public: + typedef libff::Fr FieldT; + typedef pb_variable FieldVarT; + typedef G1_precomputation G1precompT; + typedef G2_precomputation G2precompT; + typedef G1_variable G1VarT; + typedef G2_variable G2VarT; + + G1precompT m_alpha; + precompute_G1_gadget m_alpha_precomp; + + G2precompT m_beta; + precompute_G2_gadget m_beta_precomp; + + G2precompT m_gamma; + precompute_G2_gadget m_gamma_precomp; + + G2precompT m_delta; + precompute_G2_gadget m_delta_precomp; + + Fqk_variable m_alphabeta; + miller_loop_gadget m_alphabeta_loop; + + std::vector m_IC_raw; + + gro16_vk_preprocessor( + protoboard &pb, + const gro16_vk_var &vk, + const std::string &annotation_prefix + ) : + // Precomputation gadget allocates the result variables + m_alpha_precomp(pb, vk.m_alpha, m_alpha, FMT(annotation_prefix, ".alpha_precomp")), + m_beta_precomp(pb, vk.m_beta, m_beta, FMT(annotation_prefix, ".beta_precomp")), + m_gamma_precomp(pb, vk.m_gamma, m_gamma, FMT(annotation_prefix, ".gamma_precomp")), + m_delta_precomp(pb, vk.m_delta, m_delta, FMT(annotation_prefix, ".delta_precomp")), + // Miller loop to precompute e(alpha,beta) + m_alphabeta(pb, FMT(annotation_prefix, ".alphabeta")), + m_alphabeta_loop(pb, m_alpha, m_beta, m_alphabeta, FMT(annotation_prefix, ".alphabeta_miller_loop")), + // Input commitment + m_IC_raw(vk.m_IC) + { + } + + void generate_r1cs_constraints() + { + m_alpha_precomp.generate_r1cs_constraints(); + m_beta_precomp.generate_r1cs_constraints(); + m_gamma_precomp.generate_r1cs_constraints(); + m_delta_precomp.generate_r1cs_constraints(); + + m_alphabeta_loop.generate_r1cs_constraints(); + } + + void generate_r1cs_witness() + { + m_alpha_precomp.generate_r1cs_witness(); + m_beta_precomp.generate_r1cs_witness(); + m_gamma_precomp.generate_r1cs_witness(); + m_delta_precomp.generate_r1cs_witness(); + + m_alphabeta_loop.generate_r1cs_witness(); + } +}; + + +/** +* Holds the input bits +*/ +template +class gro16_inputbits_gadget : public gadget> +{ +public: + typedef libff::Fr FieldT; + + const size_t inputs_count; + pb_variable_array bits; // Contiguous array of bits + + gro16_inputbits_gadget( + protoboard &pb, + const size_t n_inputs, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + inputs_count(n_inputs) + { + assert( inputs_count > 0 ); + const size_t n_input_bits = FieldT::size_in_bits() * inputs_count; + bits.allocate(pb, n_input_bits, FMT(annotation_prefix, ".bits")); + } + + gro16_inputbits_gadget( + protoboard &pb, + pb_variable_array bits, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + inputs_count(bits.size() / FieldT::size_in_bits()), + bits(bits) + { + assert( inputs_count > 0 ); + assert( (bits.size() % FieldT::size_in_bits()) == 0 ); + } + + void generate_r1cs_witness() + { + // ... nothing to do + // ... variables have been passed in via constructor + // ... values are assumed to have been populated elsewhere + } + + /** Fill input bits from field elements */ + template + void generate_r1cs_witness(const std::vector inputs) + { + assert( inputs_count == inputs.size() ); + + int i = 0; + + for (const auto &el : inputs) + { + const auto el_bits = libff::convert_field_element_to_bit_vector(el, T::size_in_bits()); + + for( const auto b : el_bits ) + this->pb.val(bits[i++]) = (b ? 1 : 0); + } + } + + void generate_r1cs_constraints() + { + // ... no constraints needed here + } +}; + + + +template +class gro16_verifier_gadget : public gadget> { +public: + typedef G1_precomputation G1precompT; + typedef G2_precomputation G2precompT; + typedef libff::Fr FieldT; + + G1precompT m_A; + precompute_G1_gadget m_A_precomp; + + G2precompT m_B; + precompute_G2_gadget m_B_precomp; + + G1precompT m_C; + precompute_G1_gadget m_C_precomp; + + const G1_variable m_acc_result; + G1precompT m_acc_result_precomp; + precompute_G1_gadget m_acc_result_precomp_gadget; + + G1_multiscalar_mul_gadget m_acc; + + pairing_product_gadget m_ppg; + // TODO: calculate input commitment... + + gro16_verifier_gadget( + protoboard &pb, + const gro16_proof_var &proof, + const gro16_vk_preprocessor &vkp, + const gro16_inputbits_gadget &bits, + const std::string &annotation_prefix + ) : + gadget(pb, annotation_prefix), + m_A_precomp(pb, proof.A, m_A, FMT(annotation_prefix, ".A_precompute")), + m_B_precomp(pb, proof.B, m_B, FMT(annotation_prefix, ".B_precompute")), + m_C_precomp(pb, proof.C, m_C, FMT(annotation_prefix, ".C_precompute")), + m_acc_result(pb, ".acc"), + m_acc_result_precomp_gadget(pb, m_acc_result, m_acc_result_precomp, FMT(annotation_prefix, ".C_precompute")), + m_acc(pb, + *vkp.m_IC_raw.begin(), + {bits.bits.begin(), bits.bits.end()}, + FieldT::size_in_bits(), + {vkp.m_IC_raw.begin() + 1, vkp.m_IC_raw.end()}, // slice IC[1:] + m_acc_result, + FMT(annotation_prefix, ".acc_gadget")), + m_ppg(pb, + { + pairing_input_pair(m_A, m_B), + pairing_input_pair(m_acc_result_precomp, vkp.m_gamma), + pairing_input_pair(m_C, vkp.m_delta) + }, + {vkp.m_alphabeta}, + FMT(annotation_prefix, ".pairing_product")) + { + // ... nothing more to do here + } + + void generate_r1cs_witness() + { + m_A_precomp.generate_r1cs_witness(); + m_B_precomp.generate_r1cs_witness(); + m_C_precomp.generate_r1cs_witness(); + m_acc.generate_r1cs_witness(); + m_acc_result_precomp_gadget.generate_r1cs_witness(); + m_ppg.generate_r1cs_witness(); + } + + void generate_r1cs_constraints() + { + m_A_precomp.generate_r1cs_constraints(); + m_B_precomp.generate_r1cs_constraints(); + m_C_precomp.generate_r1cs_constraints(); + m_acc.generate_r1cs_constraints(); + m_acc_result_precomp_gadget.generate_r1cs_constraints(); + m_ppg.generate_r1cs_constraints(); + } +}; + + +} // libsnark + diff --git a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp index 51d9e43..36068ab 100644 --- a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp +++ b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp @@ -13,8 +13,10 @@ #include #include #include +#include #include #include +#include using namespace libsnark; @@ -167,6 +169,71 @@ void test_hardcoded_verifier(const std::string &annotation_A, const std::string pb.num_constraints(), annotation_B.c_str(), annotation_A.c_str()); } +template +void test_gro16_verifier(const std::string &annotation_A, const std::string &annotation_B) +{ + typedef libff::Fr FieldT_A; + typedef libff::Fr FieldT_B; + + // Example constaint system + const size_t num_constraints = 50; + const size_t primary_input_size = 3; + auto 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)); + + // Create keypair and proof for constraints + auto keypair = r1cs_gg_ppzksnark_generator(example.constraint_system); + auto pi = r1cs_gg_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input); + bool bit = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, pi); + assert(bit); + + protoboard pb; + // Create gadgets to verify + gro16_inputbits_gadget input_as_bits(pb, primary_input_size, "input_bits"); + gro16_proof_var proof(pb, "proof"); + gro16_vk_var vk(pb, primary_input_size, "vk"); + gro16_vk_preprocessor vkp(pb, vk, "vkp"); + gro16_verifier_gadget verifier(pb, proof, vkp, input_as_bits, "verifier"); + + // Generate constraints, displaying breakdown per function + PROFILE_CONSTRAINTS(pb, "convert 3 input to bits") { + input_as_bits.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "validate proof") { + proof.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "validate verification key") { + vk.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "verification key constraints") { + vkp.generate_r1cs_constraints(); + } + PROFILE_CONSTRAINTS(pb, "proof verifier constraints") { + verifier.generate_r1cs_constraints(); + } + + // Create witness + input_as_bits.generate_r1cs_witness(example.primary_input); + proof.generate_r1cs_witness(pi); + vk.generate_r1cs_witness(keypair.vk); + vkp.generate_r1cs_witness(); + verifier.generate_r1cs_witness(); + + printf("positive test:\n"); + assert(pb.is_satisfied()); + + pb.val(input_as_bits.bits[0]) = FieldT_B::one() - pb.val(input_as_bits.bits[0]); + verifier.generate_r1cs_witness(); + 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) { @@ -445,7 +512,7 @@ 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"); @@ -496,4 +563,7 @@ int main(void) test_pairing_product_gadget("mnt4"); test_pairing_product_gadget("mnt6"); + */ + test_gro16_verifier("mnt4", "mnt6"); + test_gro16_verifier("mnt6", "mnt4"); } diff --git a/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp b/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp index b6bd268..d9925d7 100644 --- a/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp +++ b/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.hpp @@ -167,6 +167,8 @@ std::istream& operator>>(std::istream &in, r1cs_gg_ppzksnark_verification_key class r1cs_gg_ppzksnark_verification_key { public: + libff::G1 alpha_g1; + libff::G2 beta_g2; libff::GT alpha_g1_beta_g2; libff::G2 gamma_g2; libff::G2 delta_g2; @@ -174,11 +176,14 @@ class r1cs_gg_ppzksnark_verification_key { accumulation_vector > gamma_ABC_g1; r1cs_gg_ppzksnark_verification_key() = default; - r1cs_gg_ppzksnark_verification_key(const libff::GT &alpha_g1_beta_g2, + r1cs_gg_ppzksnark_verification_key(const libff::G1 &alpha_g1, + const libff::G2 &beta_g2, const libff::G2 &gamma_g2, const libff::G2 &delta_g2, const accumulation_vector > &gamma_ABC_g1) : - alpha_g1_beta_g2(alpha_g1_beta_g2), + alpha_g1(alpha_g1), + beta_g2(beta_g2), + alpha_g1_beta_g2(ppT::reduced_pairing(alpha_g1, beta_g2)), gamma_g2(gamma_g2), delta_g2(delta_g2), gamma_ABC_g1(gamma_ABC_g1) diff --git a/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.tcc b/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.tcc index 0acb190..e1b19f9 100644 --- a/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.tcc +++ b/libsnark/zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark/r1cs_gg_ppzksnark.tcc @@ -352,8 +352,8 @@ r1cs_gg_ppzksnark_keypair r1cs_gg_ppzksnark_generator(const r1cs_gg_ppzksna libff::leave_block("Generate R1CS proving key"); libff::enter_block("Generate R1CS verification key"); - libff::GT alpha_g1_beta_g2 = ppT::reduced_pairing(alpha_g1, beta_g2); - libff::G2 gamma_g2 = gamma * G2_gen; + //libff::GT alpha_g1_beta_g2 = ppT::reduced_pairing(alpha_g1, beta_g2); + const libff::G2 gamma_g2 = gamma * G2_gen; libff::enter_block("Encode gamma_ABC for R1CS verification key"); libff::G1 gamma_ABC_g1_0 = gamma_ABC_0 * g1_generator; @@ -363,23 +363,25 @@ r1cs_gg_ppzksnark_keypair r1cs_gg_ppzksnark_generator(const r1cs_gg_ppzksna libff::leave_block("Call to r1cs_gg_ppzksnark_generator"); - accumulation_vector > gamma_ABC_g1(std::move(gamma_ABC_g1_0), std::move(gamma_ABC_g1_values)); - - r1cs_gg_ppzksnark_verification_key vk = r1cs_gg_ppzksnark_verification_key(alpha_g1_beta_g2, - gamma_g2, - delta_g2, - gamma_ABC_g1); - - r1cs_gg_ppzksnark_proving_key pk = r1cs_gg_ppzksnark_proving_key(std::move(alpha_g1), - std::move(beta_g1), - std::move(beta_g2), - std::move(delta_g1), - std::move(delta_g2), - std::move(A_query), - std::move(B_query), - std::move(H_query), - std::move(L_query), - std::move(r1cs_copy)); + const accumulation_vector > gamma_ABC_g1(std::move(gamma_ABC_g1_0), std::move(gamma_ABC_g1_values)); + + auto vk = r1cs_gg_ppzksnark_verification_key( + alpha_g1, + beta_g2, + gamma_g2, + delta_g2, + gamma_ABC_g1); + + auto pk = r1cs_gg_ppzksnark_proving_key(std::move(alpha_g1), + std::move(beta_g1), + std::move(beta_g2), + std::move(delta_g1), + std::move(delta_g2), + std::move(A_query), + std::move(B_query), + std::move(H_query), + std::move(L_query), + std::move(r1cs_copy)); pk.print_size(); vk.print_size(); From 27a472a0355ee22ffcaeb864c11a22e05817fe05 Mon Sep 17 00:00:00 2001 From: HarryR Date: Tue, 5 Nov 2019 16:42:28 +0000 Subject: [PATCH 3/3] Fixed Groth16 verifier --- .../gadgets/pairing/pairing_checks.hpp | 1 - .../gadgetlib1/gadgets/verifiers/gro16.hpp | 166 +++++++++++------- .../test_r1cs_ppzksnark_verifier_gadget.cpp | 4 +- 3 files changed, 103 insertions(+), 68 deletions(-) diff --git a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp index 98e09be..3757581 100644 --- a/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp +++ b/libsnark/gadgetlib1/gadgets/pairing/pairing_checks.hpp @@ -119,7 +119,6 @@ class pairing_product_gadget : public gadget > { std::vector > m_miller_results; std::vector > m_miller_loops; - std::vector> m_precomputed_loops; std::vector > m_product_results; std::vector > m_product; std::shared_ptr > m_final_exp; diff --git a/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp b/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp index 3f4581e..17234af 100644 --- a/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp +++ b/libsnark/gadgetlib1/gadgets/verifiers/gro16.hpp @@ -52,9 +52,9 @@ class gro16_proof_var : public gadget > { const std::string &annotation_prefix ) : gadget(pb, annotation_prefix), - A(pb, FMT(this->annotation_prefix, ".A")), - B(pb, FMT(this->annotation_prefix, ".B")), - C(pb, FMT(this->annotation_prefix, ".C")), + A(pb, FMT(annotation_prefix, ".A")), + B(pb, FMT(annotation_prefix, ".B")), + C(pb, FMT(annotation_prefix, ".C")), m_A_checker(pb, A, FMT(annotation_prefix, ".A_checker")), m_B_checker(pb, B, FMT(annotation_prefix, ".B_checker")), m_C_checker(pb, C, FMT(annotation_prefix, ".C_checker")) @@ -69,22 +69,30 @@ class gro16_proof_var : public gadget > { m_C_checker.generate_r1cs_constraints(); } - /** Variables are already filled with values */ - void generate_r1cs_witness() - { - m_A_checker.generate_r1cs_witness(); - m_B_checker.generate_r1cs_witness(); - m_C_checker.generate_r1cs_witness(); - } - /** Fill variables from proof with inputs */ void generate_r1cs_witness( const r1cs_gg_ppzksnark_proof> &proof ) { + // A and C need to be negated, so the first term in each pairing is negative + // e(A*B) * e(-IC*gamma) * e(-C*delta) * (-alpha*gamma) == 1 A.generate_r1cs_witness(proof.g_A); + m_A_checker.generate_r1cs_witness(); + B.generate_r1cs_witness(proof.g_B); - C.generate_r1cs_witness(proof.g_C); - generate_r1cs_witness(); + m_B_checker.generate_r1cs_witness(); + + C.generate_r1cs_witness(-proof.g_C); + m_C_checker.generate_r1cs_witness(); + } + + void print(const char *prefix="") + { + std::cout << prefix << ".A.X = "; this->pb.lc_val(A.X).print(); + std::cout << prefix << ".A.Y = "; this->pb.lc_val(A.Y).print(); + std::cout << prefix << ".B.X = "; this->B.X->get_element().print(); + std::cout << prefix << ".B.Y = "; this->B.Y->get_element().print(); + std::cout << prefix << ".C.X = "; this->pb.lc_val(C.X).print(); + std::cout << prefix << ".C.Y = "; this->pb.lc_val(C.Y).print(); } }; @@ -106,7 +114,9 @@ class gro16_vk_var : public gadget > { G2VarT m_gamma; G2VarT m_delta; - size_t n_inputs; + G1VarT m_IC_base; + + const size_t n_inputs; std::vector m_IC; std::vector> m_g1_checkers; @@ -114,23 +124,23 @@ class gro16_vk_var : public gadget > { void _init_checkers() { - auto pb = this->pb; const auto annotation_prefix = this->annotation_prefix; - m_g1_checkers.reserve(n_inputs + 1); - m_g1_checkers.emplace_back(pb, m_alpha, FMT(annotation_prefix, ".alpha_checker")); - - m_g2_checkers.reserve(3); - m_g2_checkers.emplace_back(pb, m_beta, FMT(annotation_prefix, ".beta_checker")); - m_g2_checkers.emplace_back(pb, m_gamma, FMT(annotation_prefix, ".gamma_checker")); - m_g2_checkers.emplace_back(pb, m_delta, FMT(annotation_prefix, ".delta_checker")); + m_g1_checkers.reserve(n_inputs + 2); + m_g1_checkers.emplace_back(this->pb, m_alpha, FMT(annotation_prefix, ".alpha_checker")); + m_g1_checkers.emplace_back(this->pb, m_IC_base, FMT(annotation_prefix, ".IC_base")); assert( n_inputs > 0 ); - for( size_t i = 0; i < (n_inputs + 1); i++ ) + for( size_t i = 0; i < n_inputs; i++ ) { - m_IC.emplace_back(pb, FMT(annotation_prefix, ".input_%d", i)); - m_g1_checkers.emplace_back(pb, m_IC.back(), FMT(annotation_prefix, ".IC_checker_%d", i)); + m_IC.emplace_back(this->pb, FMT(annotation_prefix, ".input_%d", i)); + m_g1_checkers.emplace_back(this->pb, m_IC.back(), FMT(annotation_prefix, ".IC_checker_%d", i)); } + + m_g2_checkers.reserve(3); + m_g2_checkers.emplace_back(this->pb, m_beta, FMT(annotation_prefix, ".beta_checker")); + m_g2_checkers.emplace_back(this->pb, m_gamma, FMT(annotation_prefix, ".gamma_checker")); + m_g2_checkers.emplace_back(this->pb, m_delta, FMT(annotation_prefix, ".delta_checker")); } /** @@ -147,6 +157,7 @@ class gro16_vk_var : public gadget > { m_beta(pb, FMT(annotation_prefix, ".beta")), m_gamma(pb, FMT(annotation_prefix, ".gamma")), m_delta(pb, FMT(annotation_prefix, ".delta")), + m_IC_base(pb, FMT(annotation_prefix, ".IC_base")), n_inputs(_n_inputs) { _init_checkers(); @@ -162,6 +173,7 @@ class gro16_vk_var : public gadget > { const G2VarT &beta, const G2VarT &gamma, const G2VarT &delta, + const G1VarT &IC_base, const std::vector &IC, const std::string &annotation_prefix ) : @@ -170,7 +182,9 @@ class gro16_vk_var : public gadget > { m_beta(beta), m_gamma(gamma), m_delta(delta), - n_inputs(IC.size()) + m_IC_base(IC_base), + n_inputs(IC.size()), + m_IC(IC) { _init_checkers(); } @@ -184,53 +198,39 @@ class gro16_vk_var : public gadget > { gadget.generate_r1cs_constraints(); } - /** When the VK variable values have already been filled */ void generate_r1cs_witness() { for( auto &gadget : m_g1_checkers ) - gadget.generate_r1cs_constraints(); + gadget.generate_r1cs_witness(); for( auto &gadget : m_g2_checkers ) - gadget.generate_r1cs_constraints(); - } - - /** Fill variable values before witness of point checkers */ - void generate_r1cs_witness( - const libff::G1 &alpha, - const libff::G2 &beta, - const libff::G2 &gamma, - const libff::G2 &delta, - const std::vector> &IC - ) { - m_alpha.generate_r1cs_witness(alpha); - m_beta.generate_r1cs_witness(beta); - m_gamma.generate_r1cs_witness(gamma); - m_delta.generate_r1cs_witness(delta); - - assert( m_IC.size() == IC.size() ); - int i = 0; - for( const auto& x: IC ) - m_IC[i++].generate_r1cs_witness(x); - - generate_r1cs_witness(); + gadget.generate_r1cs_witness(); } template void generate_r1cs_witness( - const libsnark::r1cs_gg_ppzksnark_verification_key &vk + const r1cs_gg_ppzksnark_verification_key &vk ) { - m_alpha.generate_r1cs_witness(vk.alpha_g1); + m_alpha.generate_r1cs_witness(-vk.alpha_g1); m_beta.generate_r1cs_witness(vk.beta_g2); m_gamma.generate_r1cs_witness(vk.gamma_g2); m_delta.generate_r1cs_witness(vk.delta_g2); - m_IC[0].generate_r1cs_witness(vk.gamma_ABC_g1.first); - int i = 1; - for( const auto& x: vk.gamma_ABC_g1.rest.indices ) - m_IC[i++].generate_r1cs_witness(vk.gamma_ABC_g1.rest.values[x]); + // IC needs to be negated for the sum + m_IC_base.generate_r1cs_witness(-(vk.gamma_ABC_g1.first)); + assert( vk.gamma_ABC_g1.rest.size() == n_inputs ); + int i = 0; + for( const auto& x: vk.gamma_ABC_g1.rest.values ) { + m_IC[i++].generate_r1cs_witness(-x); + } generate_r1cs_witness(); } + + void print(const char *prefix="") + { + std::cout << prefix << ".alpha = "; this->pb.lc_val(m_alpha.X).print(); + } }; @@ -265,7 +265,8 @@ class gro16_vk_preprocessor { Fqk_variable m_alphabeta; miller_loop_gadget m_alphabeta_loop; - std::vector m_IC_raw; + G1VarT m_IC_base; + std::vector m_IC; gro16_vk_preprocessor( protoboard &pb, @@ -281,7 +282,28 @@ class gro16_vk_preprocessor { m_alphabeta(pb, FMT(annotation_prefix, ".alphabeta")), m_alphabeta_loop(pb, m_alpha, m_beta, m_alphabeta, FMT(annotation_prefix, ".alphabeta_miller_loop")), // Input commitment - m_IC_raw(vk.m_IC) + m_IC_base(vk.m_IC_base), + m_IC(vk.m_IC) + { + } + + /* Verification key will be constant, no checker gadgets are necessary, only precomputation */ + gro16_vk_preprocessor( + protoboard &pb, + const r1cs_gg_ppzksnark_verification_key &vk, + const std::string &annotation_prefix + ) : + // Precomputation gadget allocates the result variables + m_alpha_precomp(pb, vk.alpha_g1, m_alpha, FMT(annotation_prefix, ".alpha_precomp")), + m_beta_precomp(pb, vk.beta_g2, m_beta, FMT(annotation_prefix, ".beta_precomp")), + m_gamma_precomp(pb, vk.gamma_g2, m_gamma, FMT(annotation_prefix, ".gamma_precomp")), + m_delta_precomp(pb, vk.delta_g2, m_delta, FMT(annotation_prefix, ".delta_precomp")), + // Miller loop to precompute e(alpha,beta) + m_alphabeta(pb, FMT(annotation_prefix, ".alphabeta")), + m_alphabeta_loop(pb, m_alpha, m_beta, m_alphabeta, FMT(annotation_prefix, ".alphabeta_miller_loop")), + // Input commitment + m_IC_base(vk.gamma_ABC_g1.first), + m_IC(vk.gamma_ABC_g1.rest) { } @@ -304,6 +326,10 @@ class gro16_vk_preprocessor { m_alphabeta_loop.generate_r1cs_witness(); } + + void print(const char *prefix="") { + + } }; @@ -334,7 +360,7 @@ class gro16_inputbits_gadget : public gadget> gro16_inputbits_gadget( protoboard &pb, - pb_variable_array bits, + const pb_variable_array bits, const std::string &annotation_prefix ) : gadget(pb, annotation_prefix), @@ -363,9 +389,9 @@ class gro16_inputbits_gadget : public gadget> for (const auto &el : inputs) { const auto el_bits = libff::convert_field_element_to_bit_vector(el, T::size_in_bits()); - - for( const auto b : el_bits ) + for( const auto b : el_bits ) { this->pb.val(bits[i++]) = (b ? 1 : 0); + } } } @@ -373,6 +399,9 @@ class gro16_inputbits_gadget : public gadget> { // ... no constraints needed here } + + void print(const char *prefix="") { + } }; @@ -416,17 +445,17 @@ class gro16_verifier_gadget : public gadget> { m_acc_result(pb, ".acc"), m_acc_result_precomp_gadget(pb, m_acc_result, m_acc_result_precomp, FMT(annotation_prefix, ".C_precompute")), m_acc(pb, - *vkp.m_IC_raw.begin(), + vkp.m_IC_base, {bits.bits.begin(), bits.bits.end()}, FieldT::size_in_bits(), - {vkp.m_IC_raw.begin() + 1, vkp.m_IC_raw.end()}, // slice IC[1:] + vkp.m_IC, m_acc_result, FMT(annotation_prefix, ".acc_gadget")), m_ppg(pb, { - pairing_input_pair(m_A, m_B), pairing_input_pair(m_acc_result_precomp, vkp.m_gamma), - pairing_input_pair(m_C, vkp.m_delta) + pairing_input_pair(m_C, vkp.m_delta), + pairing_input_pair(m_A, m_B) }, {vkp.m_alphabeta}, FMT(annotation_prefix, ".pairing_product")) @@ -452,6 +481,13 @@ class gro16_verifier_gadget : public gadget> { m_acc.generate_r1cs_constraints(); m_acc_result_precomp_gadget.generate_r1cs_constraints(); m_ppg.generate_r1cs_constraints(); + + this->pb.add_r1cs_constraint(r1cs_constraint(m_ppg.result_is_one, 1, 1), FMT(this->annotation_prefix, ".result must be 1")); + } + + void print(const char *prefix="") { + std::cout << prefix << ".m_acc_result.X = "; this->pb.lc_val(m_acc_result.X).print(); + std::cout << prefix << ".m_acc_result.Y = "; this->pb.lc_val(m_acc_result.Y).print(); } }; diff --git a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp index 36068ab..705f30a 100644 --- a/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp +++ b/libsnark/gadgetlib1/gadgets/verifiers/tests/test_r1cs_ppzksnark_verifier_gadget.cpp @@ -512,7 +512,7 @@ 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"); @@ -563,7 +563,7 @@ int main(void) test_pairing_product_gadget("mnt4"); test_pairing_product_gadget("mnt6"); - */ + test_gro16_verifier("mnt4", "mnt6"); test_gro16_verifier("mnt6", "mnt4"); }