From 99dff178752bc1c8d14f1b7bd76ade2a7de70256 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Sat, 26 Nov 2016 02:50:03 +0100 Subject: [PATCH 01/14] added multiple_miller_loop to alt_bn128 and configured double_miller_loop to use it --- .../curves/alt_bn128/alt_bn128_pairing.cpp | 75 +++++++++++-------- .../curves/alt_bn128/alt_bn128_pairing.hpp | 6 ++ src/algebra/curves/alt_bn128/alt_bn128_pp.cpp | 9 +++ src/algebra/curves/alt_bn128/alt_bn128_pp.hpp | 6 ++ src/algebra/curves/tests/test_bilinearity.cpp | 31 +++++++- 5 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/algebra/curves/alt_bn128/alt_bn128_pairing.cpp b/src/algebra/curves/alt_bn128/alt_bn128_pairing.cpp index 3eb5aa11..91ce0df0 100644 --- a/src/algebra/curves/alt_bn128/alt_bn128_pairing.cpp +++ b/src/algebra/curves/alt_bn128/alt_bn128_pairing.cpp @@ -418,18 +418,19 @@ alt_bn128_Fq12 alt_bn128_ate_miller_loop(const alt_bn128_ate_G1_precomp &prec_P, return f; } -alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp &prec_P1, - const alt_bn128_ate_G2_precomp &prec_Q1, - const alt_bn128_ate_G1_precomp &prec_P2, - const alt_bn128_ate_G2_precomp &prec_Q2) -{ - enter_block("Call to alt_bn128_ate_double_miller_loop"); - alt_bn128_Fq12 f = alt_bn128_Fq12::one(); +alt_bn128_Fq12 alt_bn128_ate_multiple_miller_loop( + const std::initializer_list >& v +) +{ + enter_block("Call to alt_bn128_ate_multiple_miller_loop"); + auto f = alt_bn128_Fq12::one(); bool found_one = false; size_t idx = 0; - const bigint &loop_count = alt_bn128_ate_loop_count; for (long i = loop_count.max_bits(); i >= 0; --i) { @@ -445,48 +446,62 @@ alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp & alt_bn128_param_p (skipping leading zeros) in MSB to LSB order */ - alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx]; - alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx]; - ++idx; - f = f.squared(); - - f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); - f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + for(auto& p:v){ + auto c = p.second.coeffs[idx]; + f = f.mul_by_024(c.ell_0, p.first.PY * c.ell_VW, p.first.PX * c.ell_VV); + } + ++idx; if (bit) { - alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx]; - alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx]; + for(auto& p:v){ + auto c = p.second.coeffs[idx]; + f = f.mul_by_024(c.ell_0, p.first.PY * c.ell_VW, p.first.PX * c.ell_VV); + } ++idx; - f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); - f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); } } if (alt_bn128_ate_is_loop_count_neg) { - f = f.inverse(); + f = f.inverse(); } - alt_bn128_ate_ell_coeffs c1 = prec_Q1.coeffs[idx]; - alt_bn128_ate_ell_coeffs c2 = prec_Q2.coeffs[idx]; - ++idx; - f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); - f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + for(auto& p:v){ + auto c = p.second.coeffs[idx]; + f = f.mul_by_024(c.ell_0, p.first.PY * c.ell_VW, p.first.PX * c.ell_VV); + } + ++idx; - c1 = prec_Q1.coeffs[idx]; - c2 = prec_Q2.coeffs[idx]; - ++idx; - f = f.mul_by_024(c1.ell_0, prec_P1.PY * c1.ell_VW, prec_P1.PX * c1.ell_VV); - f = f.mul_by_024(c2.ell_0, prec_P2.PY * c2.ell_VW, prec_P2.PX * c2.ell_VV); + for(auto& p:v){ + auto c = p.second.coeffs[idx]; + f = f.mul_by_024(c.ell_0, p.first.PY * c.ell_VW, p.first.PX * c.ell_VV); + } + ++idx; + leave_block("Call to alt_bn128_ate_multiple_miller_loop"); + return f; +} + +alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp &prec_P1, + const alt_bn128_ate_G2_precomp &prec_Q1, + const alt_bn128_ate_G1_precomp &prec_P2, + const alt_bn128_ate_G2_precomp &prec_Q2) +{ + enter_block("Call to alt_bn128_ate_double_miller_loop"); + + auto f = alt_bn128_ate_multiple_miller_loop({ + std::make_pair(prec_P1,prec_Q1), + std::make_pair(prec_P2,prec_Q2) + }); leave_block("Call to alt_bn128_ate_double_miller_loop"); return f; } + alt_bn128_Fq12 alt_bn128_ate_pairing(const alt_bn128_G1& P, const alt_bn128_G2 &Q) { enter_block("Call to alt_bn128_ate_pairing"); diff --git a/src/algebra/curves/alt_bn128/alt_bn128_pairing.hpp b/src/algebra/curves/alt_bn128/alt_bn128_pairing.hpp index 15d32548..8744faa7 100644 --- a/src/algebra/curves/alt_bn128/alt_bn128_pairing.hpp +++ b/src/algebra/curves/alt_bn128/alt_bn128_pairing.hpp @@ -56,6 +56,12 @@ alt_bn128_Fq12 alt_bn128_ate_double_miller_loop(const alt_bn128_ate_G1_precomp & const alt_bn128_ate_G2_precomp &prec_Q1, const alt_bn128_ate_G1_precomp &prec_P2, const alt_bn128_ate_G2_precomp &prec_Q2); +alt_bn128_Fq12 alt_bn128_ate_multiple_miller_loop( + const std::initializer_list >& v +); alt_bn128_Fq12 alt_bn128_ate_pairing(const alt_bn128_G1& P, const alt_bn128_G2 &Q); diff --git a/src/algebra/curves/alt_bn128/alt_bn128_pp.cpp b/src/algebra/curves/alt_bn128/alt_bn128_pp.cpp index 25ea924d..f23af110 100644 --- a/src/algebra/curves/alt_bn128/alt_bn128_pp.cpp +++ b/src/algebra/curves/alt_bn128/alt_bn128_pp.cpp @@ -43,6 +43,15 @@ alt_bn128_Fq12 alt_bn128_pp::double_miller_loop(const alt_bn128_G1_precomp &prec return alt_bn128_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); } +alt_bn128_Fq12 alt_bn128_pp::multiple_miller_loop( + const std::initializer_list >& v +) +{ + return alt_bn128_ate_multiple_miller_loop(v); +} alt_bn128_Fq12 alt_bn128_pp::pairing(const alt_bn128_G1 &P, const alt_bn128_G2 &Q) { diff --git a/src/algebra/curves/alt_bn128/alt_bn128_pp.hpp b/src/algebra/curves/alt_bn128/alt_bn128_pp.hpp index ec8059dc..32b8cc17 100644 --- a/src/algebra/curves/alt_bn128/alt_bn128_pp.hpp +++ b/src/algebra/curves/alt_bn128/alt_bn128_pp.hpp @@ -39,6 +39,12 @@ class alt_bn128_pp { const alt_bn128_G2_precomp &prec_Q1, const alt_bn128_G1_precomp &prec_P2, const alt_bn128_G2_precomp &prec_Q2); + static alt_bn128_Fq12 multiple_miller_loop( + const std::initializer_list >& v + ); static alt_bn128_Fq12 pairing(const alt_bn128_G1 &P, const alt_bn128_G2 &Q); static alt_bn128_Fq12 reduced_pairing(const alt_bn128_G1 &P, diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index 29574528..0cb083dd 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -73,6 +73,35 @@ void double_miller_loop_test() assert(ans_1 * ans_2 == ans_12); } +template +void multiple_miller_loop_test() +{ + const G1 P1 = (Fr::random_element()) * G1::one(); + const G1 P2 = (Fr::random_element()) * G1::one(); + const G1 P3 = (Fr::random_element()) * G1::one(); + + const G2 Q1 = (Fr::random_element()) * G2::one(); + const G2 Q2 = (Fr::random_element()) * G2::one(); + const G2 Q3 = (Fr::random_element()) * G2::one(); + + + const G1_precomp prec_P1 = ppT::precompute_G1(P1); + const G1_precomp prec_P2 = ppT::precompute_G1(P2); + const G1_precomp prec_P3 = ppT::precompute_G1(P3); + const G2_precomp prec_Q1 = ppT::precompute_G2(Q1); + const G2_precomp prec_Q2 = ppT::precompute_G2(Q2); + const G2_precomp prec_Q3 = ppT::precompute_G2(Q3); + const Fqk ans_1 = ppT::miller_loop(prec_P1, prec_Q1); + const Fqk ans_2 = ppT::miller_loop(prec_P2, prec_Q2); + const Fqk ans_3 = ppT::miller_loop(prec_P3, prec_Q3); + const Fqk ans_123 = ppT::multiple_miller_loop({ + std::make_pair(prec_P1, prec_Q1), + std::make_pair(prec_P2, prec_Q2), + std::make_pair(prec_P3, prec_Q3) + }); + assert(ans_1 * ans_2 * ans_3 == ans_123); +} + template void affine_pairing_test() { @@ -127,7 +156,7 @@ int main(void) alt_bn128_pp::init_public_params(); pairing_test(); double_miller_loop_test(); - + multiple_miller_loop_test(); #ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled bn128_pp::init_public_params(); pairing_test(); From 72ab4bbaf46f8faaf796745c1a4cb1775b7889b7 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Sun, 27 Nov 2016 01:17:14 +0100 Subject: [PATCH 02/14] r1cs_ppzksnark_probabilistic_verifier compiles --- src/algebra/curves/tests/test_bilinearity.cpp | 19 ++++- .../r1cs_ppzksnark/r1cs_ppzksnark.tcc | 73 ++++++++++++++++++- 2 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index 0cb083dd..c162f631 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -79,27 +79,40 @@ void multiple_miller_loop_test() const G1 P1 = (Fr::random_element()) * G1::one(); const G1 P2 = (Fr::random_element()) * G1::one(); const G1 P3 = (Fr::random_element()) * G1::one(); + const G1 P4 = (Fr::random_element()) * G1::one(); + const G1 P5 = (Fr::random_element()) * G1::one(); const G2 Q1 = (Fr::random_element()) * G2::one(); const G2 Q2 = (Fr::random_element()) * G2::one(); const G2 Q3 = (Fr::random_element()) * G2::one(); + const G2 Q4 = (Fr::random_element()) * G2::one(); + const G2 Q5 = (Fr::random_element()) * G2::one(); const G1_precomp prec_P1 = ppT::precompute_G1(P1); const G1_precomp prec_P2 = ppT::precompute_G1(P2); const G1_precomp prec_P3 = ppT::precompute_G1(P3); + const G1_precomp prec_P4 = ppT::precompute_G1(P4); + const G1_precomp prec_P5 = ppT::precompute_G1(P5); const G2_precomp prec_Q1 = ppT::precompute_G2(Q1); const G2_precomp prec_Q2 = ppT::precompute_G2(Q2); const G2_precomp prec_Q3 = ppT::precompute_G2(Q3); + const G2_precomp prec_Q4 = ppT::precompute_G2(Q4); + const G2_precomp prec_Q5 = ppT::precompute_G2(Q5); const Fqk ans_1 = ppT::miller_loop(prec_P1, prec_Q1); const Fqk ans_2 = ppT::miller_loop(prec_P2, prec_Q2); const Fqk ans_3 = ppT::miller_loop(prec_P3, prec_Q3); - const Fqk ans_123 = ppT::multiple_miller_loop({ + const Fqk ans_4 = ppT::miller_loop(prec_P4, prec_Q4); + const Fqk ans_5 = ppT::miller_loop(prec_P5, prec_Q5); + + const Fqk ans_12345 = ppT::multiple_miller_loop({ std::make_pair(prec_P1, prec_Q1), std::make_pair(prec_P2, prec_Q2), - std::make_pair(prec_P3, prec_Q3) + std::make_pair(prec_P3, prec_Q3), + std::make_pair(prec_P4, prec_Q4), + std::make_pair(prec_P5, prec_Q5) }); - assert(ans_1 * ans_2 * ans_3 == ans_123); + assert(ans_1 * ans_2 * ans_3 * ans_4 * ans_5 == ans_12345); } template diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc index 88145da6..b31bc70e 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc @@ -34,7 +34,7 @@ bool r1cs_ppzksnark_proving_key::operator==(const r1cs_ppzksnark_proving_ke return (this->A_query == other.A_query && this->B_query == other.B_query && this->C_query == other.C_query && - this->H_query == other.H_query && + this->H_query == other.H_query && this->K_query == other.K_query && this->constraint_system == other.constraint_system); } @@ -788,5 +788,76 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke return result; } + +/*A verifier with that batches all pairing checks into one using random coefficients + requires only one FE (final exponentiation). + Also requires only one call to multiple_miller_loop, as only needs to compute one product of + Miller loops (MLs). + Also uses rule e(a,c)*e(b,c)=e(a+b,c) to reduce number of factors in the ML product + If proof is wrong has at most 1/|F| probability of accepting it + Always accepts correct proofs +*/ +template +bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + const accumulation_vector > accumulated_IC = vk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); + const G1 &acc = accumulated_IC.first; + //computing the random coefficients that will be used + auto r_1 = Fr::random_element(); + auto r_2 = Fr::random_element(); + auto r_3 = Fr::random_element(); + auto r_4 = Fr::random_element(); + auto r_5 = Fr::random_element(); + + //computing the inputs for the first ML factor + // r1Pi_a and vk_A + auto left_1 = ppT::precompute_G1(r_1*proof.g_A.g); + auto right_1 = ppT::precompute_G2(vk.alphaA_g2); + auto pair_1 = std::make_pair(left_1,right_1); + //computing the inputs for the second ML factor + // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 + auto left_2 = ppT::precompute_G1(r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g); + auto right_2 = ppT::precompute_G2(-G2::one()); + auto pair_2 = std::make_pair(left_2,right_2); + //computing the inputs for the third ML factor + // r3Pi_c and vk_C + auto left_3 = ppT::precompute_G1(r_3*proof.g_C.g); + auto right_3 = ppT::precompute_G2(ppT::precompute_G2(vk.alphaC_g2)); + auto pair_3 = std::make_pair(left_3,right_3); + + //computing the inputs for the fourth ML factor + // r4Pi_K and vk_gamma + auto left_4 = ppT::precompute_G1(r_4*proof.g_K); + auto right_4 = ppT::precompute_G2(vk.gamma_g2); + auto pair_4 = std::make_pair(left_4,right_4); + + //computing the inputs for the fifth ML factor + //−r 4(vk x + πA + πC) and vk^2_betagamma + auto left_5 = ppT::precompute_G1(-r_4*(acc + proof.g_A.g + proof.g_C.g)); + auto right_5 = ppT::precompute_G2(vk.gamma_beta_g2); + auto pair_5 = std::make_pair(left_5,right_5); + + //computing the inputs for the six ML factor + //r5Pi_H and -vk_Z + auto left_6 = ppT::precompute_G1(r_5*proof.g_H); + auto right_6 = ppT::precompute_G2(vk.rC_Z_g2); + auto pair_6 = std::make_pair(left_6,right_6); + + //computing the inputs for the seventh ML factor + //r_2 vk_B-r_4 vk^1_{\beta\gamma}+r_5(vk_x + \pi_A) and pi_B + auto left_7 = ppT::precompute_G1(r_2*vk.alphaB_g1-r_4*vk.gamma_beta_g1+r_5*(acc+proof.g_A.g)); + auto right_7 = ppT::precompute_G2(proof.g_B.g); + auto pair_7 = std::make_pair(left_7,right_7); + + auto ML = ppT::multiple_miller_loop({ + pair_1,pair_2,pair_3,pair_4,pair_5,pair_6,pair_7 + }); + auto FE= ppT::final_exponentiation(ML); + + return (FE==1); +} + } // libsnark #endif // R1CS_PPZKSNARK_TCC_ From b473e5373585643a53ee1e7baad43695c0be0f6f Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Sun, 27 Nov 2016 22:54:58 +0100 Subject: [PATCH 03/14] testing speed of probabilistic verifier..seems 30% faster --- .../r1cs_ppzksnark/r1cs_ppzksnark.hpp | 4 +++ .../r1cs_ppzksnark/r1cs_ppzksnark.tcc | 26 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index bbccf563..737f9f26 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -462,6 +462,10 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke const r1cs_ppzksnark_primary_input &primary_input, const r1cs_ppzksnark_proof &proof); +template +bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); } // libsnark diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc index b31bc70e..4984a53b 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc @@ -25,7 +25,7 @@ See r1cs_ppzksnark.hpp . #include "algebra/scalar_multiplication/multiexp.hpp" #include "algebra/scalar_multiplication/kc_multiexp.hpp" #include "reductions/r1cs_to_qap/r1cs_to_qap.hpp" - +#include "algebra/curves/alt_bn128/alt_bn128_pp.hpp" namespace libsnark { template @@ -694,6 +694,7 @@ bool r1cs_ppzksnark_verifier_strong_IC(const r1cs_ppzksnark_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(vk); bool result = r1cs_ppzksnark_online_verifier_strong_IC(pvk, primary_input, proof); + //bool result = r1cs_ppzksnark_probabilistic_verifier(vk,primary_input,proof); leave_block("Call to r1cs_ppzksnark_verifier_strong_IC"); return result; } @@ -802,6 +803,7 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key const r1cs_ppzksnark_primary_input &primary_input, const r1cs_ppzksnark_proof &proof) { + enter_block("Call to r1cs_ppzksnark_probabilistic_verifier"); const accumulation_vector > accumulated_IC = vk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); const G1 &acc = accumulated_IC.first; //computing the random coefficients that will be used @@ -811,11 +813,15 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key auto r_4 = Fr::random_element(); auto r_5 = Fr::random_element(); + enter_block("Preparing first ML factor"); + //computing the inputs for the first ML factor // r1Pi_a and vk_A auto left_1 = ppT::precompute_G1(r_1*proof.g_A.g); auto right_1 = ppT::precompute_G2(vk.alphaA_g2); auto pair_1 = std::make_pair(left_1,right_1); + leave_block("Preparing first ML factor"); + //computing the inputs for the second ML factor // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 auto left_2 = ppT::precompute_G1(r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g); @@ -824,7 +830,7 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key //computing the inputs for the third ML factor // r3Pi_c and vk_C auto left_3 = ppT::precompute_G1(r_3*proof.g_C.g); - auto right_3 = ppT::precompute_G2(ppT::precompute_G2(vk.alphaC_g2)); + auto right_3 = ppT::precompute_G2(vk.alphaC_g2) ; auto pair_3 = std::make_pair(left_3,right_3); //computing the inputs for the fourth ML factor @@ -851,12 +857,16 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key auto right_7 = ppT::precompute_G2(proof.g_B.g); auto pair_7 = std::make_pair(left_7,right_7); - auto ML = ppT::multiple_miller_loop({ - pair_1,pair_2,pair_3,pair_4,pair_5,pair_6,pair_7 - }); - auto FE= ppT::final_exponentiation(ML); - - return (FE==1); + auto ML1 = ppT::double_miller_loop(left_1,right_1,left_2,right_2); + auto ML2 = ppT::double_miller_loop(left_3,right_3,left_4,right_4); + auto ML3 = ppT::double_miller_loop(left_5,right_5,left_6,right_6); + auto ML4 = ppT::miller_loop(left_7,right_7); + //auto ML = alt_bn128_pp::multiple_miller_loop({ + // pair_1,pair_2,pair_3,pair_4,pair_5,pair_6,pair_7 + //}); + auto FE= ppT::final_exponentiation(ML1*ML2*ML3*ML4); + leave_block("Call to r1cs_ppzksnark_probabilistic_verifier"); + return (FE==GT::one()); } } // libsnark From 989adcaf40bd173e263378a947c24d00644590fc Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Mon, 5 Dec 2016 14:35:38 +0100 Subject: [PATCH 04/14] compiling after adding batch verifier --- .../r1cs_ppzksnark/r1cs_ppzksnark.hpp | 60 ++++ .../r1cs_ppzksnark/r1cs_ppzksnark.tcc | 320 +++++++++++++----- 2 files changed, 299 insertions(+), 81 deletions(-) diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index 737f9f26..bfe5414d 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -252,6 +252,48 @@ class r1cs_ppzksnark_processed_verification_key { friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); }; +/** + * The purpose of this class is to enable one time precomputation of elements that are + * functions of the verification key + * and are used in each invocation of r1cs_ppzksnark_probabilistic_verifier + * and also r1cs_ppzksnark_batcher + */ +template +class r1cs_ppzksnark_processed_batch_verification_key { +public: + G2_precomp pair1; + G2_precomp pair2; + G2_precomp pair3; + G2_precomp pair4; + G2_precomp pair5; + G2_precomp pair6; + + // accumulation_vector > encoded_IC_query; + + // bool operator==(const r1cs_ppzksnark_processed_verification_key &other) const; + // friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_processed_verification_key &pvk); + // friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); +}; + +template +class batch_verification_accumulator{ +public: + G1 pair1; + G1 pair2; + G1 pair3; + G1 pair4; + G1 pair5; + G1 pair6; + Fqk pair7; + +batch_verification_accumulator() = default; + +// batch_verification_accumulator():pair1(G1::zero()),pair2(G1::zero()),pair3(G1::zero()),pair4(G1::zero()), +// pair5(G1::zero()),pair6(G1::zero()),pair7(Fqk::one()) +// { + +// }; +}; /********************************** Key pair *********************************/ @@ -462,10 +504,28 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke const r1cs_ppzksnark_primary_input &primary_input, const r1cs_ppzksnark_proof &proof); +/****Ariel stuff ***/ + +/* + * accumulate another proof inside acc for the final batch check + */ +template +void r1cs_ppzksnark_batcher(const r1cs_ppzksnark_verification_key &vk, + batch_verification_accumulator &acc, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); + template bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key &vk, const r1cs_ppzksnark_primary_input &primary_input, const r1cs_ppzksnark_proof &proof); +template +bool r1cs_ppzksnark_batch_verifier(const r1cs_ppzksnark_processed_batch_verification_key &pvk, + const batch_verification_accumulator &acc, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); + + } // libsnark diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc index 4984a53b..60d5ff1d 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc @@ -214,19 +214,19 @@ template r1cs_ppzksnark_verification_key r1cs_ppzksnark_verification_key::dummy_verification_key(const size_t input_size) { r1cs_ppzksnark_verification_key result; - result.alphaA_g2 = Fr::random_element() * G2::one(); - result.alphaB_g1 = Fr::random_element() * G1::one(); - result.alphaC_g2 = Fr::random_element() * G2::one(); - result.gamma_g2 = Fr::random_element() * G2::one(); - result.gamma_beta_g1 = Fr::random_element() * G1::one(); - result.gamma_beta_g2 = Fr::random_element() * G2::one(); - result.rC_Z_g2 = Fr::random_element() * G2::one(); - - G1 base = Fr::random_element() * G1::one(); + result.alphaA_g2 = Fr::random_element() * G2::zero(); + result.alphaB_g1 = Fr::random_element() * G1::zero(); + result.alphaC_g2 = Fr::random_element() * G2::zero(); + result.gamma_g2 = Fr::random_element() * G2::zero(); + result.gamma_beta_g1 = Fr::random_element() * G1::zero(); + result.gamma_beta_g2 = Fr::random_element() * G2::zero(); + result.rC_Z_g2 = Fr::random_element() * G2::zero(); + + G1 base = Fr::random_element() * G1::zero(); G1_vector v; for (size_t i = 0; i < input_size; ++i) { - v.emplace_back(Fr::random_element() * G1::one()); + v.emplace_back(Fr::random_element() * G1::zero()); } result.encoded_IC_query = accumulation_vector >(std::move(base), std::move(v)); @@ -334,11 +334,11 @@ r1cs_ppzksnark_keypair r1cs_ppzksnark_generator(const r1cs_ppzksnark_constr #endif enter_block("Generating G1 multiexp table"); - window_table > g1_table = get_window_table(Fr::size_in_bits(), g1_window, G1::one()); + window_table > g1_table = get_window_table(Fr::size_in_bits(), g1_window, G1::zero()); leave_block("Generating G1 multiexp table"); enter_block("Generating G2 multiexp table"); - window_table > g2_table = get_window_table(Fr::size_in_bits(), g2_window, G2::one()); + window_table > g2_table = get_window_table(Fr::size_in_bits(), g2_window, G2::zero()); leave_block("Generating G2 multiexp table"); enter_block("Generate R1CS proving key"); @@ -372,16 +372,16 @@ r1cs_ppzksnark_keypair r1cs_ppzksnark_generator(const r1cs_ppzksnark_constr leave_block("Generate R1CS proving key"); enter_block("Generate R1CS verification key"); - G2 alphaA_g2 = alphaA * G2::one(); - G1 alphaB_g1 = alphaB * G1::one(); - G2 alphaC_g2 = alphaC * G2::one(); - G2 gamma_g2 = gamma * G2::one(); - G1 gamma_beta_g1 = (gamma * beta) * G1::one(); - G2 gamma_beta_g2 = (gamma * beta) * G2::one(); - G2 rC_Z_g2 = (rC * qap_inst.Zt) * G2::one(); + G2 alphaA_g2 = alphaA * G2::zero(); + G1 alphaB_g1 = alphaB * G1::zero(); + G2 alphaC_g2 = alphaC * G2::zero(); + G2 gamma_g2 = gamma * G2::zero(); + G1 gamma_beta_g1 = (gamma * beta) * G1::zero(); + G2 gamma_beta_g2 = (gamma * beta) * G2::zero(); + G2 rC_Z_g2 = (rC * qap_inst.Zt) * G2::zero(); enter_block("Encode IC query for R1CS verification key"); - G1 encoded_IC_base = (rA * IC_coefficients[0]) * G1::one(); + G1 encoded_IC_base = (rA * IC_coefficients[0]) * G1::zero(); Fr_vector multiplied_IC_coefficients; multiplied_IC_coefficients.reserve(qap_inst.num_inputs()); for (size_t i = 1; i < qap_inst.num_inputs() + 1; ++i) @@ -558,7 +558,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif { if (!inhibit_profiling_info) { - print_indent(); printf("At least one of the proof elements does not lie on the curve.\n"); + print_indent(); printf("At least zero of the proof elements does not lie on the curve.\n"); } result = false; } @@ -571,7 +571,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk kc_A_1 = ppT::miller_loop(proof_g_A_g_precomp, pvk.vk_alphaA_g2_precomp); Fqk kc_A_2 = ppT::miller_loop(proof_g_A_h_precomp, pvk.pp_G2_one_precomp); GT kc_A = ppT::final_exponentiation(kc_A_1 * kc_A_2.unitary_inverse()); - if (kc_A != GT::one()) + if (kc_A != GT::zero()) { if (!inhibit_profiling_info) { @@ -587,7 +587,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk kc_B_1 = ppT::miller_loop(pvk.vk_alphaB_g1_precomp, proof_g_B_g_precomp); Fqk kc_B_2 = ppT::miller_loop(proof_g_B_h_precomp, pvk.pp_G2_one_precomp); GT kc_B = ppT::final_exponentiation(kc_B_1 * kc_B_2.unitary_inverse()); - if (kc_B != GT::one()) + if (kc_B != GT::zero()) { if (!inhibit_profiling_info) { @@ -603,7 +603,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk kc_C_1 = ppT::miller_loop(proof_g_C_g_precomp, pvk.vk_alphaC_g2_precomp); Fqk kc_C_2 = ppT::miller_loop(proof_g_C_h_precomp, pvk.pp_G2_one_precomp); GT kc_C = ppT::final_exponentiation(kc_C_1 * kc_C_2.unitary_inverse()); - if (kc_C != GT::one()) + if (kc_C != GT::zero()) { if (!inhibit_profiling_info) { @@ -621,7 +621,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk QAP_1 = ppT::miller_loop(proof_g_A_g_acc_precomp, proof_g_B_g_precomp); Fqk QAP_23 = ppT::double_miller_loop(proof_g_H_precomp, pvk.vk_rC_Z_g2_precomp, proof_g_C_g_precomp, pvk.pp_G2_one_precomp); GT QAP = ppT::final_exponentiation(QAP_1 * QAP_23.unitary_inverse()); - if (QAP != GT::one()) + if (QAP != GT::zero()) { if (!inhibit_profiling_info) { @@ -637,7 +637,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk K_1 = ppT::miller_loop(proof_g_K_precomp, pvk.vk_gamma_g2_precomp); Fqk K_23 = ppT::double_miller_loop(proof_g_A_g_acc_C_precomp, pvk.vk_gamma_beta_g2_precomp, pvk.vk_gamma_beta_g1_precomp, proof_g_B_g_precomp); GT K = ppT::final_exponentiation(K_1 * K_23.unitary_inverse()); - if (K != GT::one()) + if (K != GT::zero()) { if (!inhibit_profiling_info) { @@ -692,9 +692,9 @@ bool r1cs_ppzksnark_verifier_strong_IC(const r1cs_ppzksnark_verification_key &proof) { enter_block("Call to r1cs_ppzksnark_verifier_strong_IC"); - r1cs_ppzksnark_processed_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(vk); - bool result = r1cs_ppzksnark_online_verifier_strong_IC(pvk, primary_input, proof); - //bool result = r1cs_ppzksnark_probabilistic_verifier(vk,primary_input,proof); + //r1cs_ppzksnark_processed_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(vk); + //bool result = r1cs_ppzksnark_online_verifier_strong_IC(pvk, primary_input, proof); + bool result = r1cs_ppzksnark_probabilistic_verifier(vk,primary_input,proof); leave_block("Call to r1cs_ppzksnark_verifier_strong_IC"); return result; } @@ -707,7 +707,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke enter_block("Call to r1cs_ppzksnark_affine_verifier_weak_IC"); assert(vk.encoded_IC_query.domain_size() >= primary_input.size()); - affine_ate_G2_precomp pvk_pp_G2_one_precomp = ppT::affine_ate_precompute_G2(G2::one()); + affine_ate_G2_precomp pvk_pp_G2_one_precomp = ppT::affine_ate_precompute_G2(G2::zero()); affine_ate_G2_precomp pvk_vk_alphaA_g2_precomp = ppT::affine_ate_precompute_G2(vk.alphaA_g2); affine_ate_G1_precomp pvk_vk_alphaB_g1_precomp = ppT::affine_ate_precompute_G1(vk.alphaB_g1); affine_ate_G2_precomp pvk_vk_alphaC_g2_precomp = ppT::affine_ate_precompute_G2(vk.alphaC_g2); @@ -729,7 +729,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke Fqk kc_A_miller = ppT::affine_ate_e_over_e_miller_loop(proof_g_A_g_precomp, pvk_vk_alphaA_g2_precomp, proof_g_A_h_precomp, pvk_pp_G2_one_precomp); GT kc_A = ppT::final_exponentiation(kc_A_miller); - if (kc_A != GT::one()) + if (kc_A != GT::zero()) { print_indent(); printf("Knowledge commitment for A query incorrect.\n"); result = false; @@ -741,7 +741,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_B_h_precomp = ppT::affine_ate_precompute_G1(proof.g_B.h); Fqk kc_B_miller = ppT::affine_ate_e_over_e_miller_loop(pvk_vk_alphaB_g1_precomp, proof_g_B_g_precomp, proof_g_B_h_precomp, pvk_pp_G2_one_precomp); GT kc_B = ppT::final_exponentiation(kc_B_miller); - if (kc_B != GT::one()) + if (kc_B != GT::zero()) { print_indent(); printf("Knowledge commitment for B query incorrect.\n"); result = false; @@ -753,7 +753,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_C_h_precomp = ppT::affine_ate_precompute_G1(proof.g_C.h); Fqk kc_C_miller = ppT::affine_ate_e_over_e_miller_loop(proof_g_C_g_precomp, pvk_vk_alphaC_g2_precomp, proof_g_C_h_precomp, pvk_pp_G2_one_precomp); GT kc_C = ppT::final_exponentiation(kc_C_miller); - if (kc_C != GT::one()) + if (kc_C != GT::zero()) { print_indent(); printf("Knowledge commitment for C query incorrect.\n"); result = false; @@ -765,7 +765,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_H_precomp = ppT::affine_ate_precompute_G1(proof.g_H); Fqk QAP_miller = ppT::affine_ate_e_times_e_over_e_miller_loop(proof_g_H_precomp, pvk_vk_rC_Z_g2_precomp, proof_g_C_g_precomp, pvk_pp_G2_one_precomp, proof_g_A_g_acc_precomp, proof_g_B_g_precomp); GT QAP = ppT::final_exponentiation(QAP_miller); - if (QAP != GT::one()) + if (QAP != GT::zero()) { print_indent(); printf("QAP divisibility check failed.\n"); result = false; @@ -777,7 +777,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_A_g_acc_C_precomp = ppT::affine_ate_precompute_G1((proof.g_A.g + acc) + proof.g_C.g); Fqk K_miller = ppT::affine_ate_e_times_e_over_e_miller_loop(proof_g_A_g_acc_C_precomp, pvk_vk_gamma_beta_g2_precomp, pvk_vk_gamma_beta_g1_precomp, proof_g_B_g_precomp, proof_g_K_precomp, pvk_vk_gamma_g2_precomp); GT K = ppT::final_exponentiation(K_miller); - if (K != GT::one()) + if (K != GT::zero()) { print_indent(); printf("Same-coefficient check failed.\n"); result = false; @@ -790,22 +790,58 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke } -/*A verifier with that batches all pairing checks into one using random coefficients - requires only one FE (final exponentiation). - Also requires only one call to multiple_miller_loop, as only needs to compute one product of - Miller loops (MLs). - Also uses rule e(a,c)*e(b,c)=e(a+b,c) to reduce number of factors in the ML product - If proof is wrong has at most 1/|F| probability of accepting it - Always accepts correct proofs -*/ + + + +/*****Ariel stuff from here ****/ + template -bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key &vk, - const r1cs_ppzksnark_primary_input &primary_input, - const r1cs_ppzksnark_proof &proof) +r1cs_ppzksnark_processed_batch_verification_key r1cs_ppzksnark_batch_verifier_process_vk(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) { - enter_block("Call to r1cs_ppzksnark_probabilistic_verifier"); + enter_block("Call to r1cs_ppzksnark_batch_verifier_process_vk"); + r1cs_ppzksnark_processed_verification_key pvk; + pvk.pair1 = ppT::precompute_G2(vk.alphaA_g2); + + //computing the second input for the second ML factor + // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 + pvk.pair2 = alt_bn128_pp::precompute_G2(-G2::zero()); + //computing the second input for the third ML factor + // r3Pi_c and vk_C + pvk.pair3 = alt_bn128_pp::precompute_G2(vk.alphaC_g2) ; + + //computing the second input for the fourth ML factor + // r4Pi_K and vk_gamma + pvk.pair4 = alt_bn128_pp::precompute_G2(vk.gamma_g2); + + //computing the second input for the fifth ML factor + //−r 4(vk x + πA + πC) and vk^2_betagamma + pvk.pair5 = alt_bn128_pp::precompute_G2(vk.gamma_beta_g2); + + //computing the second input for the six ML factor + //r5Pi_H and -vk_Z + pvk.pair6 = alt_bn128_pp::precompute_G2(-vk.rC_Z_g2); + + leave_block("Call to r1cs_ppzksnark_batch_verifier_process_vk"); + return pvk; +} + + + + +/* + * accumulate another proof inside acc for the final batch check + */ +template +void r1cs_ppzksnark_batcher(const r1cs_ppzksnark_verification_key &vk, + batch_verification_accumulator &acc, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + enter_block("Call to r1cs_ppzksnark_batcher"); const accumulation_vector > accumulated_IC = vk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); - const G1 &acc = accumulated_IC.first; + const G1 &accu = accumulated_IC.first; //computing the random coefficients that will be used auto r_1 = Fr::random_element(); auto r_2 = Fr::random_element(); @@ -813,61 +849,183 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key auto r_4 = Fr::random_element(); auto r_5 = Fr::random_element(); - enter_block("Preparing first ML factor"); - //computing the inputs for the first ML factor + //computing left input for the first ML factor // r1Pi_a and vk_A - auto left_1 = ppT::precompute_G1(r_1*proof.g_A.g); - auto right_1 = ppT::precompute_G2(vk.alphaA_g2); - auto pair_1 = std::make_pair(left_1,right_1); - leave_block("Preparing first ML factor"); + acc.pair1 += r_1*proof.g_A.g; + + //computing left input for the second ML factor + // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 + acc.pair2 += r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g; + //computing left input for the third ML factor + // r3Pi_c and vk_C + acc.pair3 = r_3*proof.g_C.g; - //computing the inputs for the second ML factor + //computing left input for the fourth ML factor + // r4Pi_K and vk_gamma + acc.pair4 = r_4*proof.g_K; + //computing left input for the fifth ML factor + //−r 4(vk x + πA + πC) and vk^2_betagamma + acc.pair5 = vk.gamma_beta_g2; + + //computing left input for the six ML factor + //r5Pi_H and -vk_Z + acc.pair6 = r_5*proof.g_H; + + //computing left input for the seventh ML factor + //r_2 vk_B-r_4 vk^1_{\beta\gamma}+r_5(vk_x + \pi_A) and pi_B + auto left_7 = ppT::precompute_G1(r_2*vk.alphaB_g1-r_4*vk.gamma_beta_g1+r_5*(accu+proof.g_A.g)); + auto right_7 = ppT::precompute_G2(proof.g_B.g); + acc.pair7 *= ppT::miller_loop(left_7,right_7); + + leave_block("Call to r1cs_ppzksnark_batcher"); +} + + + + +/*A verifier that batches all pairing checks into zero using random coefficients + requires only zero FE (final expzerontiation). + Also requires only zero call to multiple_miller_loop, as only needs to compute zero product of + Miller loops (MLs). + Also uses rule e(a,c)*e(b,c)=e(a+b,c) to reduce number of factors in the ML product + If proof is wrong has at most 1/|F| probability of accepting it + Always accepts correct proofs +*/ +template<> +bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + enter_block("Call to r1cs_ppzksnark_probabilistic_verifier"); + const accumulation_vector > accumulated_IC = vk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); + const G1 &acc = accumulated_IC.first; + //computing the random coefficients that will be used + auto r_1 = Fr::random_element(); + auto r_2 = Fr::random_element(); + auto r_3 = Fr::random_element(); + auto r_4 = Fr::random_element(); + auto r_5 = Fr::random_element(); + + enter_block("Preparing 7 ML factors"); + + //computing left input for the first ML factor + // r1Pi_a and vk_A + auto left_1 = alt_bn128_pp::precompute_G1(r_1*proof.g_A.g); + auto right_1 = alt_bn128_pp::precompute_G2(vk.alphaA_g2); + auto pair_1 = std::make_pair(left_1,right_1); + + //computing left input for the second ML factor // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 - auto left_2 = ppT::precompute_G1(r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g); - auto right_2 = ppT::precompute_G2(-G2::one()); + auto left_2 = alt_bn128_pp::precompute_G1(r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g); + auto right_2 = alt_bn128_pp::precompute_G2(-G2::zero()); auto pair_2 = std::make_pair(left_2,right_2); - //computing the inputs for the third ML factor + //computing left input for the third ML factor // r3Pi_c and vk_C - auto left_3 = ppT::precompute_G1(r_3*proof.g_C.g); - auto right_3 = ppT::precompute_G2(vk.alphaC_g2) ; + auto left_3 = alt_bn128_pp::precompute_G1(r_3*proof.g_C.g); + auto right_3 = alt_bn128_pp::precompute_G2(vk.alphaC_g2) ; auto pair_3 = std::make_pair(left_3,right_3); - //computing the inputs for the fourth ML factor + //computing left input for the fourth ML factor // r4Pi_K and vk_gamma - auto left_4 = ppT::precompute_G1(r_4*proof.g_K); - auto right_4 = ppT::precompute_G2(vk.gamma_g2); + auto left_4 = alt_bn128_pp::precompute_G1(r_4*proof.g_K); + auto right_4 = alt_bn128_pp::precompute_G2(vk.gamma_g2); auto pair_4 = std::make_pair(left_4,right_4); - //computing the inputs for the fifth ML factor + //computing left input for the fifth ML factor //−r 4(vk x + πA + πC) and vk^2_betagamma - auto left_5 = ppT::precompute_G1(-r_4*(acc + proof.g_A.g + proof.g_C.g)); - auto right_5 = ppT::precompute_G2(vk.gamma_beta_g2); + auto left_5 = alt_bn128_pp::precompute_G1(-r_4*(acc + proof.g_A.g + proof.g_C.g)); + auto right_5 = alt_bn128_pp::precompute_G2(vk.gamma_beta_g2); auto pair_5 = std::make_pair(left_5,right_5); - //computing the inputs for the six ML factor + //computing left input for the six ML factor //r5Pi_H and -vk_Z - auto left_6 = ppT::precompute_G1(r_5*proof.g_H); - auto right_6 = ppT::precompute_G2(vk.rC_Z_g2); + auto left_6 = alt_bn128_pp::precompute_G1(r_5*proof.g_H); + auto right_6 = alt_bn128_pp::precompute_G2(-vk.rC_Z_g2); auto pair_6 = std::make_pair(left_6,right_6); - //computing the inputs for the seventh ML factor + //computing left input for the seventh ML factor //r_2 vk_B-r_4 vk^1_{\beta\gamma}+r_5(vk_x + \pi_A) and pi_B - auto left_7 = ppT::precompute_G1(r_2*vk.alphaB_g1-r_4*vk.gamma_beta_g1+r_5*(acc+proof.g_A.g)); - auto right_7 = ppT::precompute_G2(proof.g_B.g); + auto left_7 = alt_bn128_pp::precompute_G1(r_2*vk.alphaB_g1-r_4*vk.gamma_beta_g1+r_5*(acc+proof.g_A.g)); + auto right_7 = alt_bn128_pp::precompute_G2(proof.g_B.g); auto pair_7 = std::make_pair(left_7,right_7); + leave_block("Preparing 7 ML factors"); + + /* auto ML1 = alt_bn128_pp::double_miller_loop(left_1,right_1,left_2,right_2); + auto ML2 = alt_bn128_pp::double_miller_loop(left_3,right_3,left_4,right_4); + auto ML3 = alt_bn128_pp::double_miller_loop(left_5,right_5,left_6,right_6); + auto ML4 = alt_bn128_pp::miller_loop(left_7,right_7);*/ + alt_bn128_Fq12 ML = alt_bn128_pp::multiple_miller_loop({ + pair_1,pair_2,pair_3,pair_4,pair_5,pair_6,pair_7 + }); + auto FE= alt_bn128_pp::final_exponentiation(ML); + leave_block("Call to r1cs_ppzksnark_probabilistic_verifier"); + return (FE==GT::zero()); +} + +template +bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + r1cs_ppzksnark_processed_verification_key pvk = r1cs_ppzksnark_verifier_process_vk(vk); + bool result = r1cs_ppzksnark_online_verifier_strong_IC(pvk, primary_input, proof); + return result; +} + +template<> +bool r1cs_ppzksnark_batch_verifier(const r1cs_ppzksnark_processed_batch_verification_key &pvk, + const batch_verification_accumulator &acc, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + enter_block("Call to r1cs_ppzksnark_batch_verifier"); + + enter_block("Preparing 7 ML factors"); + + //computing left input for the first ML factor + // r3Pi_a and vk_A + auto pair_1 = std::make_pair(alt_bn128_pp::precompute_G1(acc.pair1),pvk.pair1); - auto ML1 = ppT::double_miller_loop(left_1,right_1,left_2,right_2); - auto ML2 = ppT::double_miller_loop(left_3,right_3,left_4,right_4); - auto ML3 = ppT::double_miller_loop(left_5,right_5,left_6,right_6); - auto ML4 = ppT::miller_loop(left_7,right_7); - //auto ML = alt_bn128_pp::multiple_miller_loop({ - // pair_1,pair_2,pair_3,pair_4,pair_5,pair_6,pair_7 - //}); - auto FE= ppT::final_exponentiation(ML1*ML2*ML3*ML4); + //computing left input for the second ML factor + // r3Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 + auto pair_2 = std::make_pair(alt_bn128_pp::precompute_G1(acc.pair2),pvk.pair2); + //computing left input for the third ML factor + // r3Pi_c and vk_C + auto pair_3 = std::make_pair(alt_bn128_pp::precompute_G1(acc.pair3),pvk.pair3); + + //computing left input for the fourth ML factor + // r4Pi_K and vk_gamma + auto pair_4 = std::make_pair(alt_bn128_pp::precompute_G1(acc.pair4),pvk.pair4); + + //computing left input for the fifth ML factor + //−r 4(vk x + πA + πC) and vk^2_betagamma + auto pair_5 = std::make_pair(alt_bn128_pp::precompute_G1(acc.pair5),pvk.pair5); + + //computing left input for the six ML factor + //r5Pi_H and -vk_Z + auto pair_6 = std::make_pair(alt_bn128_pp::precompute_G1(acc.pair6),pvk.pair6); + + //computing left input for the seventh ML factor + //r_2 vk_B-r_4 vk^3_{\beta\gamma}+r_5(vk_x + \pi_A) and pi_B + leave_block("Preparing 7 ML factors"); + + alt_bn128_Fq12 ML = alt_bn128_pp::multiple_miller_loop({ + pair_1,pair_2,pair_3,pair_4,pair_5,pair_6 + })*acc.pair7; + auto FE= alt_bn128_pp::final_exponentiation(ML); leave_block("Call to r1cs_ppzksnark_probabilistic_verifier"); - return (FE==GT::one()); + return (FE==GT::zero()); } +template +bool r1cs_ppzksnark_batch_verifier(const r1cs_ppzksnark_processed_batch_verification_key &pvk, + const batch_verification_accumulator &acc, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) +{ + assert(false); + return false; +} } // libsnark #endif // R1CS_PPZKSNARK_TCC_ From f154e525dbc22aed8fe34314264feb1cbab6c5c3 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Tue, 6 Dec 2016 01:50:35 +0100 Subject: [PATCH 05/14] batcher test working --- .../examples/run_r1cs_ppzksnark.tcc | 41 +++++++ .../r1cs_ppzksnark/r1cs_ppzksnark.hpp | 17 +-- .../r1cs_ppzksnark/r1cs_ppzksnark.tcc | 102 +++++++++--------- .../tests/test_r1cs_ppzksnark.cpp | 39 ++++++- 4 files changed, 140 insertions(+), 59 deletions(-) diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc index 9bc87586..6cee048f 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/examples/run_r1cs_ppzksnark.tcc @@ -109,6 +109,47 @@ bool run_r1cs_ppzksnark(const r1cs_example > &example, return ans; } + + +///***Ariel version for testing batch verifier****// + + +/** + * The code below provides an example of all stages of running a R1CS ppzkSNARK. + * + * Of course, in a real-life scenario, we would have three distinct entities, + * mangled into one in the demonstration below. The three entities are as follows. + * (1) The "generator", which runs the ppzkSNARK generator on input a given + * constraint system CS to create a proving and a verification key for CS. + * (2) The "prover", which runs the ppzkSNARK prover on input the proving key, + * a primary input for CS, and an auxiliary input for CS. + * (3) The "verifier", which runs the ppzkSNARK verifier on input the verification key, + * a primary input for CS, and a proof. + */ +template +void add_proof_in_batch_verifier_test(batch_verification_accumulator &acc, + const r1cs_ppzksnark_proof &proof, + const r1cs_ppzksnark_verification_key &vk, + const r1cs_primary_input > &primary_input) +{ + enter_block("Call to add_proof_in_batch_verifier_test"); + + //r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(example.constraint_system); + + + + print_header("R1CS ppzkSNARK Prover"); + //r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input); + + r1cs_ppzksnark_batcher(vk, acc, primary_input, proof); + + + leave_block("Call to add_proof_in_batch_verifier_test"); + + +} + + } // libsnark #endif // RUN_R1CS_PPZKSNARK_TCC_ diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index bfe5414d..b9b6d7dc 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -275,6 +275,7 @@ class r1cs_ppzksnark_processed_batch_verification_key { // friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); }; +//a data structure to store intermediate results from various proofs before the final batch verification template class batch_verification_accumulator{ public: @@ -286,13 +287,13 @@ class batch_verification_accumulator{ G1 pair6; Fqk pair7; -batch_verification_accumulator() = default; +//batch_verification_accumulator() = default; -// batch_verification_accumulator():pair1(G1::zero()),pair2(G1::zero()),pair3(G1::zero()),pair4(G1::zero()), -// pair5(G1::zero()),pair6(G1::zero()),pair7(Fqk::one()) -// { +batch_verification_accumulator():pair1(G1::zero()),pair2(G1::zero()),pair3(G1::zero()),pair4(G1::zero()), +pair5(G1::zero()),pair6(G1::zero()),pair7(Fqk::one()) +{ -// }; +}; }; /********************************** Key pair *********************************/ @@ -524,7 +525,11 @@ bool r1cs_ppzksnark_batch_verifier(const r1cs_ppzksnark_processed_batch_verifica const batch_verification_accumulator &acc, const r1cs_ppzksnark_primary_input &primary_input, const r1cs_ppzksnark_proof &proof); - +template +r1cs_ppzksnark_processed_batch_verification_key r1cs_ppzksnark_batch_verifier_process_vk( + const r1cs_ppzksnark_verification_key &vk, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof); } // libsnark diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc index 60d5ff1d..8ea80acc 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.tcc @@ -214,19 +214,19 @@ template r1cs_ppzksnark_verification_key r1cs_ppzksnark_verification_key::dummy_verification_key(const size_t input_size) { r1cs_ppzksnark_verification_key result; - result.alphaA_g2 = Fr::random_element() * G2::zero(); - result.alphaB_g1 = Fr::random_element() * G1::zero(); - result.alphaC_g2 = Fr::random_element() * G2::zero(); - result.gamma_g2 = Fr::random_element() * G2::zero(); - result.gamma_beta_g1 = Fr::random_element() * G1::zero(); - result.gamma_beta_g2 = Fr::random_element() * G2::zero(); - result.rC_Z_g2 = Fr::random_element() * G2::zero(); - - G1 base = Fr::random_element() * G1::zero(); + result.alphaA_g2 = Fr::random_element() * G2::one(); + result.alphaB_g1 = Fr::random_element() * G1::one(); + result.alphaC_g2 = Fr::random_element() * G2::one(); + result.gamma_g2 = Fr::random_element() * G2::one(); + result.gamma_beta_g1 = Fr::random_element() * G1::one(); + result.gamma_beta_g2 = Fr::random_element() * G2::one(); + result.rC_Z_g2 = Fr::random_element() * G2::one(); + + G1 base = Fr::random_element() * G1::one(); G1_vector v; for (size_t i = 0; i < input_size; ++i) { - v.emplace_back(Fr::random_element() * G1::zero()); + v.emplace_back(Fr::random_element() * G1::one()); } result.encoded_IC_query = accumulation_vector >(std::move(base), std::move(v)); @@ -334,11 +334,11 @@ r1cs_ppzksnark_keypair r1cs_ppzksnark_generator(const r1cs_ppzksnark_constr #endif enter_block("Generating G1 multiexp table"); - window_table > g1_table = get_window_table(Fr::size_in_bits(), g1_window, G1::zero()); + window_table > g1_table = get_window_table(Fr::size_in_bits(), g1_window, G1::one()); leave_block("Generating G1 multiexp table"); enter_block("Generating G2 multiexp table"); - window_table > g2_table = get_window_table(Fr::size_in_bits(), g2_window, G2::zero()); + window_table > g2_table = get_window_table(Fr::size_in_bits(), g2_window, G2::one()); leave_block("Generating G2 multiexp table"); enter_block("Generate R1CS proving key"); @@ -372,16 +372,16 @@ r1cs_ppzksnark_keypair r1cs_ppzksnark_generator(const r1cs_ppzksnark_constr leave_block("Generate R1CS proving key"); enter_block("Generate R1CS verification key"); - G2 alphaA_g2 = alphaA * G2::zero(); - G1 alphaB_g1 = alphaB * G1::zero(); - G2 alphaC_g2 = alphaC * G2::zero(); - G2 gamma_g2 = gamma * G2::zero(); - G1 gamma_beta_g1 = (gamma * beta) * G1::zero(); - G2 gamma_beta_g2 = (gamma * beta) * G2::zero(); - G2 rC_Z_g2 = (rC * qap_inst.Zt) * G2::zero(); + G2 alphaA_g2 = alphaA * G2::one(); + G1 alphaB_g1 = alphaB * G1::one(); + G2 alphaC_g2 = alphaC * G2::one(); + G2 gamma_g2 = gamma * G2::one(); + G1 gamma_beta_g1 = (gamma * beta) * G1::one(); + G2 gamma_beta_g2 = (gamma * beta) * G2::one(); + G2 rC_Z_g2 = (rC * qap_inst.Zt) * G2::one(); enter_block("Encode IC query for R1CS verification key"); - G1 encoded_IC_base = (rA * IC_coefficients[0]) * G1::zero(); + G1 encoded_IC_base = (rA * IC_coefficients[0]) * G1::one(); Fr_vector multiplied_IC_coefficients; multiplied_IC_coefficients.reserve(qap_inst.num_inputs()); for (size_t i = 1; i < qap_inst.num_inputs() + 1; ++i) @@ -558,7 +558,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif { if (!inhibit_profiling_info) { - print_indent(); printf("At least zero of the proof elements does not lie on the curve.\n"); + print_indent(); printf("At least one of the proof elements does not lie on the curve.\n"); } result = false; } @@ -571,7 +571,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk kc_A_1 = ppT::miller_loop(proof_g_A_g_precomp, pvk.vk_alphaA_g2_precomp); Fqk kc_A_2 = ppT::miller_loop(proof_g_A_h_precomp, pvk.pp_G2_one_precomp); GT kc_A = ppT::final_exponentiation(kc_A_1 * kc_A_2.unitary_inverse()); - if (kc_A != GT::zero()) + if (kc_A != GT::one()) { if (!inhibit_profiling_info) { @@ -587,7 +587,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk kc_B_1 = ppT::miller_loop(pvk.vk_alphaB_g1_precomp, proof_g_B_g_precomp); Fqk kc_B_2 = ppT::miller_loop(proof_g_B_h_precomp, pvk.pp_G2_one_precomp); GT kc_B = ppT::final_exponentiation(kc_B_1 * kc_B_2.unitary_inverse()); - if (kc_B != GT::zero()) + if (kc_B != GT::one()) { if (!inhibit_profiling_info) { @@ -603,7 +603,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk kc_C_1 = ppT::miller_loop(proof_g_C_g_precomp, pvk.vk_alphaC_g2_precomp); Fqk kc_C_2 = ppT::miller_loop(proof_g_C_h_precomp, pvk.pp_G2_one_precomp); GT kc_C = ppT::final_exponentiation(kc_C_1 * kc_C_2.unitary_inverse()); - if (kc_C != GT::zero()) + if (kc_C != GT::one()) { if (!inhibit_profiling_info) { @@ -621,7 +621,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk QAP_1 = ppT::miller_loop(proof_g_A_g_acc_precomp, proof_g_B_g_precomp); Fqk QAP_23 = ppT::double_miller_loop(proof_g_H_precomp, pvk.vk_rC_Z_g2_precomp, proof_g_C_g_precomp, pvk.pp_G2_one_precomp); GT QAP = ppT::final_exponentiation(QAP_1 * QAP_23.unitary_inverse()); - if (QAP != GT::zero()) + if (QAP != GT::one()) { if (!inhibit_profiling_info) { @@ -637,7 +637,7 @@ bool r1cs_ppzksnark_online_verifier_weak_IC(const r1cs_ppzksnark_processed_verif Fqk K_1 = ppT::miller_loop(proof_g_K_precomp, pvk.vk_gamma_g2_precomp); Fqk K_23 = ppT::double_miller_loop(proof_g_A_g_acc_C_precomp, pvk.vk_gamma_beta_g2_precomp, pvk.vk_gamma_beta_g1_precomp, proof_g_B_g_precomp); GT K = ppT::final_exponentiation(K_1 * K_23.unitary_inverse()); - if (K != GT::zero()) + if (K != GT::one()) { if (!inhibit_profiling_info) { @@ -707,7 +707,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke enter_block("Call to r1cs_ppzksnark_affine_verifier_weak_IC"); assert(vk.encoded_IC_query.domain_size() >= primary_input.size()); - affine_ate_G2_precomp pvk_pp_G2_one_precomp = ppT::affine_ate_precompute_G2(G2::zero()); + affine_ate_G2_precomp pvk_pp_G2_one_precomp = ppT::affine_ate_precompute_G2(G2::one()); affine_ate_G2_precomp pvk_vk_alphaA_g2_precomp = ppT::affine_ate_precompute_G2(vk.alphaA_g2); affine_ate_G1_precomp pvk_vk_alphaB_g1_precomp = ppT::affine_ate_precompute_G1(vk.alphaB_g1); affine_ate_G2_precomp pvk_vk_alphaC_g2_precomp = ppT::affine_ate_precompute_G2(vk.alphaC_g2); @@ -729,7 +729,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke Fqk kc_A_miller = ppT::affine_ate_e_over_e_miller_loop(proof_g_A_g_precomp, pvk_vk_alphaA_g2_precomp, proof_g_A_h_precomp, pvk_pp_G2_one_precomp); GT kc_A = ppT::final_exponentiation(kc_A_miller); - if (kc_A != GT::zero()) + if (kc_A != GT::one()) { print_indent(); printf("Knowledge commitment for A query incorrect.\n"); result = false; @@ -741,7 +741,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_B_h_precomp = ppT::affine_ate_precompute_G1(proof.g_B.h); Fqk kc_B_miller = ppT::affine_ate_e_over_e_miller_loop(pvk_vk_alphaB_g1_precomp, proof_g_B_g_precomp, proof_g_B_h_precomp, pvk_pp_G2_one_precomp); GT kc_B = ppT::final_exponentiation(kc_B_miller); - if (kc_B != GT::zero()) + if (kc_B != GT::one()) { print_indent(); printf("Knowledge commitment for B query incorrect.\n"); result = false; @@ -753,7 +753,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_C_h_precomp = ppT::affine_ate_precompute_G1(proof.g_C.h); Fqk kc_C_miller = ppT::affine_ate_e_over_e_miller_loop(proof_g_C_g_precomp, pvk_vk_alphaC_g2_precomp, proof_g_C_h_precomp, pvk_pp_G2_one_precomp); GT kc_C = ppT::final_exponentiation(kc_C_miller); - if (kc_C != GT::zero()) + if (kc_C != GT::one()) { print_indent(); printf("Knowledge commitment for C query incorrect.\n"); result = false; @@ -765,7 +765,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_H_precomp = ppT::affine_ate_precompute_G1(proof.g_H); Fqk QAP_miller = ppT::affine_ate_e_times_e_over_e_miller_loop(proof_g_H_precomp, pvk_vk_rC_Z_g2_precomp, proof_g_C_g_precomp, pvk_pp_G2_one_precomp, proof_g_A_g_acc_precomp, proof_g_B_g_precomp); GT QAP = ppT::final_exponentiation(QAP_miller); - if (QAP != GT::zero()) + if (QAP != GT::one()) { print_indent(); printf("QAP divisibility check failed.\n"); result = false; @@ -777,7 +777,7 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke affine_ate_G1_precomp proof_g_A_g_acc_C_precomp = ppT::affine_ate_precompute_G1((proof.g_A.g + acc) + proof.g_C.g); Fqk K_miller = ppT::affine_ate_e_times_e_over_e_miller_loop(proof_g_A_g_acc_C_precomp, pvk_vk_gamma_beta_g2_precomp, pvk_vk_gamma_beta_g1_precomp, proof_g_B_g_precomp, proof_g_K_precomp, pvk_vk_gamma_g2_precomp); GT K = ppT::final_exponentiation(K_miller); - if (K != GT::zero()) + if (K != GT::one()) { print_indent(); printf("Same-coefficient check failed.\n"); result = false; @@ -796,17 +796,15 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke /*****Ariel stuff from here ****/ template -r1cs_ppzksnark_processed_batch_verification_key r1cs_ppzksnark_batch_verifier_process_vk(const r1cs_ppzksnark_verification_key &vk, - const r1cs_ppzksnark_primary_input &primary_input, - const r1cs_ppzksnark_proof &proof) +r1cs_ppzksnark_processed_batch_verification_key r1cs_ppzksnark_batch_verifier_process_vk(const r1cs_ppzksnark_verification_key &vk) { enter_block("Call to r1cs_ppzksnark_batch_verifier_process_vk"); - r1cs_ppzksnark_processed_verification_key pvk; + r1cs_ppzksnark_processed_batch_verification_key pvk; pvk.pair1 = ppT::precompute_G2(vk.alphaA_g2); //computing the second input for the second ML factor // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 - pvk.pair2 = alt_bn128_pp::precompute_G2(-G2::zero()); + pvk.pair2 = alt_bn128_pp::precompute_G2(-G2::one()); //computing the second input for the third ML factor // r3Pi_c and vk_C pvk.pair3 = alt_bn128_pp::precompute_G2(vk.alphaC_g2) ; @@ -834,10 +832,10 @@ r1cs_ppzksnark_processed_batch_verification_key r1cs_ppzksnark_batch_verifi * accumulate another proof inside acc for the final batch check */ template -void r1cs_ppzksnark_batcher(const r1cs_ppzksnark_verification_key &vk, - batch_verification_accumulator &acc, - const r1cs_ppzksnark_primary_input &primary_input, - const r1cs_ppzksnark_proof &proof) +void r1cs_ppzksnark_batcher(const r1cs_ppzksnark_verification_key &vk, + batch_verification_accumulator &acc, + const r1cs_ppzksnark_primary_input &primary_input, + const r1cs_ppzksnark_proof &proof) { enter_block("Call to r1cs_ppzksnark_batcher"); const accumulation_vector > accumulated_IC = vk.encoded_IC_query.template accumulate_chunk >(primary_input.begin(), primary_input.end(), 0); @@ -852,31 +850,31 @@ void r1cs_ppzksnark_batcher(const r1cs_ppzksnark_verification_key //computing left input for the first ML factor // r1Pi_a and vk_A - acc.pair1 += r_1*proof.g_A.g; + acc.pair1 = acc.pair1 + r_1*proof.g_A.g; //computing left input for the second ML factor // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 - acc.pair2 += r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g; + acc.pair2 = acc.pair2 + r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g; //computing left input for the third ML factor // r3Pi_c and vk_C - acc.pair3 = r_3*proof.g_C.g; + acc.pair3 = acc.pair3 + r_3*proof.g_C.g; //computing left input for the fourth ML factor // r4Pi_K and vk_gamma - acc.pair4 = r_4*proof.g_K; + acc.pair4 = acc.pair4 + r_4*proof.g_K; //computing left input for the fifth ML factor //−r 4(vk x + πA + πC) and vk^2_betagamma - acc.pair5 = vk.gamma_beta_g2; + acc.pair5 = acc.pair5 + -r_4*(accu + proof.g_A.g + proof.g_C.g); //computing left input for the six ML factor //r5Pi_H and -vk_Z - acc.pair6 = r_5*proof.g_H; + acc.pair6 = acc.pair6 + r_5*proof.g_H; //computing left input for the seventh ML factor //r_2 vk_B-r_4 vk^1_{\beta\gamma}+r_5(vk_x + \pi_A) and pi_B auto left_7 = ppT::precompute_G1(r_2*vk.alphaB_g1-r_4*vk.gamma_beta_g1+r_5*(accu+proof.g_A.g)); auto right_7 = ppT::precompute_G2(proof.g_B.g); - acc.pair7 *= ppT::miller_loop(left_7,right_7); + acc.pair7 = acc.pair7*ppT::miller_loop(left_7,right_7); leave_block("Call to r1cs_ppzksnark_batcher"); } @@ -918,7 +916,7 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key //computing left input for the second ML factor // r1Pi'_a + R2Pi'_B+r3Pi'_C + r5Pi_C and -g2 auto left_2 = alt_bn128_pp::precompute_G1(r_1*proof.g_A.h+r_2*proof.g_B.h + r_3*proof.g_C.h + r_5*proof.g_C.g); - auto right_2 = alt_bn128_pp::precompute_G2(-G2::zero()); + auto right_2 = alt_bn128_pp::precompute_G2(-G2::one()); auto pair_2 = std::make_pair(left_2,right_2); //computing left input for the third ML factor // r3Pi_c and vk_C @@ -960,7 +958,7 @@ bool r1cs_ppzksnark_probabilistic_verifier(const r1cs_ppzksnark_verification_key }); auto FE= alt_bn128_pp::final_exponentiation(ML); leave_block("Call to r1cs_ppzksnark_probabilistic_verifier"); - return (FE==GT::zero()); + return (FE==GT::one()); } template @@ -1014,8 +1012,8 @@ bool r1cs_ppzksnark_batch_verifier(const r1cs_ppzksnark_processed_batch_verifica pair_1,pair_2,pair_3,pair_4,pair_5,pair_6 })*acc.pair7; auto FE= alt_bn128_pp::final_exponentiation(ML); - leave_block("Call to r1cs_ppzksnark_probabilistic_verifier"); - return (FE==GT::zero()); + leave_block("Call to r1cs_ppzksnark_batch_verifier"); + return (FE==GT::one()); } template diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp index 6f8b575f..f352a532 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp @@ -19,6 +19,41 @@ using namespace libsnark; +//****Ariel code-testing batch verifier****/// +template +void test_r1cs_ppzksnark_batch_verifier(size_t num_constraints, + size_t input_size) +{ + //silly test that takes the same proof/primary input multiple times + print_header("(enter) Test R1CS ppzkSNARK batch verifier"); + print_header("Preprocess verification key"); + r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size); + r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(example.constraint_system); + r1cs_ppzksnark_processed_batch_verification_key pvk = r1cs_ppzksnark_batch_verifier_process_vk(keypair.vk); + r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input); + + batch_verification_accumulator acc; + const bool test_serialization = true; + for(auto i=0; i<15;i++) + { + //r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size); + print_header("(enter)R1CS ppzkSNARK batcher (adding proof)"); + add_proof_in_batch_verifier_test(acc,proof,keypair.vk,example.primary_input); + print_header("(leave)R1CS ppzkSNARK batcher (adding proof)"); + + + } + + const bool bit = r1cs_ppzksnark_batch_verifier(pvk, acc,example.primary_input,proof); + assert(bit); + + print_header("(leave) Test R1CS ppzkSNARK batch verifier"); +} + + +///****End Ariel code ******////// + + template void test_r1cs_ppzksnark(size_t num_constraints, size_t input_size) @@ -38,5 +73,7 @@ int main() default_r1cs_ppzksnark_pp::init_public_params(); start_profiling(); - test_r1cs_ppzksnark(1000, 100); + //test_r1cs_ppzksnark(1000, 100); + //ariel change + test_r1cs_ppzksnark_batch_verifier(1000,100); } From ffec346de8055ea6a2d03230e8e59c5c8a569bf6 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Mon, 12 Dec 2016 16:33:30 +0100 Subject: [PATCH 06/14] batch verifier test passes --- .../tests/test_r1cs_ppzksnark.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp index f352a532..fe5bf292 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/tests/test_r1cs_ppzksnark.cpp @@ -25,29 +25,30 @@ void test_r1cs_ppzksnark_batch_verifier(size_t num_constraints, size_t input_size) { //silly test that takes the same proof/primary input multiple times - print_header("(enter) Test R1CS ppzkSNARK batch verifier"); - print_header("Preprocess verification key"); + enter_block("Test R1CS ppzkSNARK batch verifier"); r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size); r1cs_ppzksnark_keypair keypair = r1cs_ppzksnark_generator(example.constraint_system); - r1cs_ppzksnark_processed_batch_verification_key pvk = r1cs_ppzksnark_batch_verifier_process_vk(keypair.vk); r1cs_ppzksnark_proof proof = r1cs_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input); + enter_block("In test_r1cs_ppzksnark_batch_verifier after generating example and proof"); + + r1cs_ppzksnark_processed_batch_verification_key pvk = r1cs_ppzksnark_batch_verifier_process_vk(keypair.vk); + batch_verification_accumulator acc; const bool test_serialization = true; - for(auto i=0; i<15;i++) + for(auto i=0; i<10;i++) { //r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size); - print_header("(enter)R1CS ppzkSNARK batcher (adding proof)"); add_proof_in_batch_verifier_test(acc,proof,keypair.vk,example.primary_input); - print_header("(leave)R1CS ppzkSNARK batcher (adding proof)"); - + } const bool bit = r1cs_ppzksnark_batch_verifier(pvk, acc,example.primary_input,proof); assert(bit); - - print_header("(leave) Test R1CS ppzkSNARK batch verifier"); + leave_block("In test_r1cs_ppzksnark_batch_verifier after generating example and proof"); + + leave_block("Test R1CS ppzkSNARK batch verifier"); } From 59f460bbe24c43096fd60c72f4bba133d684eef5 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Mon, 12 Dec 2016 19:04:08 +0100 Subject: [PATCH 07/14] added multiplemillerloop to edwards curve --- .../curves/edwards/edwards_pairing.cpp | 58 +++++++++++++++++++ .../curves/edwards/edwards_pairing.hpp | 7 +++ 2 files changed, 65 insertions(+) diff --git a/src/algebra/curves/edwards/edwards_pairing.cpp b/src/algebra/curves/edwards/edwards_pairing.cpp index 22fab017..a4a5d319 100644 --- a/src/algebra/curves/edwards/edwards_pairing.cpp +++ b/src/algebra/curves/edwards/edwards_pairing.cpp @@ -772,4 +772,62 @@ edwards_GT edwards_reduced_pairing(const edwards_G1 &P, { return edwards_ate_reduced_pairing(P, Q); } + + +/***Ariel Code ** implementing multiple miller loop***/ +//computes the product of miller loops of all pairs in the list v +edwards_Fq6 edwards_ate_multiple_miller_loop( const std::initializer_list >& v +) +{ + enter_block("Call to edwards_ate_multiple_miller_loop"); + const bigint &loop_count = edwards_ate_loop_count; + + edwards_Fq6 f = edwards_Fq6::one(); + bool found_one = false; + size_t idx = 0; + for (long i = loop_count.max_bits()-1; i >= 0; --i) + { + const bool bit = loop_count.test_bit(i); + if (!found_one) + { + /* this skips the MSB itself */ + found_one |= bit; + continue; + } + + /* code below gets executed for all bits (EXCEPT the MSB itself) of + edwards_param_p (skipping leading zeros) in MSB to LSB + order */ + f=f.squared(); + for(auto& p:v){ + auto cc = p.second[idx]; + auto g_RR_at_P = edwards_Fq6(p.first.P_XY * cc.c_XY + p.first.P_XZ * cc.c_XZ, + p.first.P_ZZplusYZ * cc.c_ZZ); + f=f*g_RR_at_P; + + } + ++idx; + + if (bit) + { + for(auto& p:v){ + auto cc = p.second[idx]; + auto g_RQ_at_P = edwards_Fq6(p.first.P_ZZplusYZ * cc.c_ZZ, + p.first.P_XY * cc.c_XY + p.first.P_XZ * cc.c_XZ); + f=f*g_RQ_at_P; + + } + ++idx; + } + } + leave_block("Call to edwards_ate_multiple_miller_loop"); + + return f; +} + + + } // libsnark diff --git a/src/algebra/curves/edwards/edwards_pairing.hpp b/src/algebra/curves/edwards/edwards_pairing.hpp index f838ae39..fdf7174b 100644 --- a/src/algebra/curves/edwards/edwards_pairing.hpp +++ b/src/algebra/curves/edwards/edwards_pairing.hpp @@ -118,5 +118,12 @@ edwards_Fq6 edwards_pairing(const edwards_G1& P, edwards_GT edwards_reduced_pairing(const edwards_G1 &P, const edwards_G2 &Q); +/**Ariel additions***/ +edwards_Fq6 edwards_ate_multiple_miller_loop(const std::initializer_list >& v +); + } // libsnark #endif // EDWARDS_PAIRING_HPP_ From a48b0853c86ca90be5c92ccfb49716d571414588 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Tue, 13 Dec 2016 18:39:01 +0100 Subject: [PATCH 08/14] edwards multiple_miller_loop passing tests --- src/algebra/curves/edwards/edwards_pairing.cpp | 13 +++++++++++-- src/algebra/curves/edwards/edwards_pairing.hpp | 2 +- src/algebra/curves/edwards/edwards_pp.cpp | 9 +++++++++ src/algebra/curves/edwards/edwards_pp.hpp | 6 ++++++ src/algebra/curves/tests/test_bilinearity.cpp | 3 ++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/algebra/curves/edwards/edwards_pairing.cpp b/src/algebra/curves/edwards/edwards_pairing.cpp index a4a5d319..8d7a9ab6 100644 --- a/src/algebra/curves/edwards/edwards_pairing.cpp +++ b/src/algebra/curves/edwards/edwards_pairing.cpp @@ -782,7 +782,7 @@ edwards_Fq6 edwards_ate_multiple_miller_loop( const std::initializer_list >& v ) { - enter_block("Call to edwards_ate_multiple_miller_loop"); + enter_block("Call to edwards_multiple_miller_loop"); const bigint &loop_count = edwards_ate_loop_count; edwards_Fq6 f = edwards_Fq6::one(); @@ -823,11 +823,20 @@ edwards_Fq6 edwards_ate_multiple_miller_loop( const std::initializer_list >& v +) +{ + return edwards_ate_multiple_miller_loop(v); +} + } // libsnark diff --git a/src/algebra/curves/edwards/edwards_pairing.hpp b/src/algebra/curves/edwards/edwards_pairing.hpp index fdf7174b..fa0d1d23 100644 --- a/src/algebra/curves/edwards/edwards_pairing.hpp +++ b/src/algebra/curves/edwards/edwards_pairing.hpp @@ -119,7 +119,7 @@ edwards_GT edwards_reduced_pairing(const edwards_G1 &P, const edwards_G2 &Q); /**Ariel additions***/ -edwards_Fq6 edwards_ate_multiple_miller_loop(const std::initializer_list >& v diff --git a/src/algebra/curves/edwards/edwards_pp.cpp b/src/algebra/curves/edwards/edwards_pp.cpp index 5af40104..7845299e 100644 --- a/src/algebra/curves/edwards/edwards_pp.cpp +++ b/src/algebra/curves/edwards/edwards_pp.cpp @@ -42,6 +42,15 @@ edwards_Fq6 edwards_pp::double_miller_loop(const edwards_G1_precomp &prec_P1, { return edwards_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); } +edwards_Fq6 edwards_pp::multiple_miller_loop( + const std::initializer_list >& v +) +{ + return edwards_multiple_miller_loop(v); +} edwards_Fq6 edwards_pp::pairing(const edwards_G1 &P, const edwards_G2 &Q) diff --git a/src/algebra/curves/edwards/edwards_pp.hpp b/src/algebra/curves/edwards/edwards_pp.hpp index 32ca85dd..4803a123 100644 --- a/src/algebra/curves/edwards/edwards_pp.hpp +++ b/src/algebra/curves/edwards/edwards_pp.hpp @@ -39,6 +39,12 @@ class edwards_pp { const edwards_G2_precomp &prec_Q1, const edwards_G1_precomp &prec_P2, const edwards_G2_precomp &prec_Q2); + static edwards_Fq6 multiple_miller_loop( + const std::initializer_list >& v + ); /* the following are used in test files */ static edwards_Fq6 pairing(const edwards_G1 &P, const edwards_G2 &Q); diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index c162f631..d3262b6e 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -73,6 +73,7 @@ void double_miller_loop_test() assert(ans_1 * ans_2 == ans_12); } +//checking multiple_miller_loop really computes the product of corresponding miller loops template void multiple_miller_loop_test() { @@ -155,7 +156,7 @@ int main(void) edwards_pp::init_public_params(); pairing_test(); double_miller_loop_test(); - + multiple_miller_loop_test(); mnt6_pp::init_public_params(); pairing_test(); double_miller_loop_test(); From 763fc438c150efc47dadba6850bf1aa827fe1833 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Tue, 27 Dec 2016 20:19:19 +0100 Subject: [PATCH 09/14] multiple_miller_loop working for mnt4 --- src/algebra/curves/mnt/mnt4/mnt4_pairing.cpp | 84 +++++++++++++++++++ src/algebra/curves/mnt/mnt4/mnt4_pairing.hpp | 12 +++ src/algebra/curves/mnt/mnt4/mnt4_pp.cpp | 9 +- src/algebra/curves/mnt/mnt4/mnt4_pp.hpp | 5 ++ src/algebra/curves/tests/test_bilinearity.cpp | 12 ++- .../r1cs_ppzksnark/r1cs_ppzksnark.hpp | 2 + 6 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/algebra/curves/mnt/mnt4/mnt4_pairing.cpp b/src/algebra/curves/mnt/mnt4/mnt4_pairing.cpp index 6334283a..abaa0e3f 100644 --- a/src/algebra/curves/mnt/mnt4/mnt4_pairing.cpp +++ b/src/algebra/curves/mnt/mnt4/mnt4_pairing.cpp @@ -738,5 +738,89 @@ mnt4_GT mnt4_affine_reduced_pairing(const mnt4_G1 &P, const mnt4_GT result = mnt4_final_exponentiation(f); return result; } +//Ariel additions +mnt4_Fq4 mnt4_ate_multiple_miller_loop(const std::initializer_list >& v +) +{ + enter_block("Call to mnt4_ate_multiple_miller_loop"); + + + mnt4_Fq4 f = mnt4_Fq4::one(); + + bool found_one = false; + size_t dbl_idx = 0; + size_t add_idx = 0; + + const bigint &loop_count = mnt4_ate_loop_count; + for (long i = loop_count.max_bits() - 1; i >= 0; --i) + { + const bool bit = loop_count.test_bit(i); + + if (!found_one) + { + /* this skips the MSB itself */ + found_one |= bit; + continue; + } + + /* code below gets executed for all bits (EXCEPT the MSB itself) of + mnt4_param_p (skipping leading zeros) in MSB to LSB + order */ + + f=f.squared(); + for(auto& p:v) + { + mnt4_ate_dbl_coeffs dc = p.second.dbl_coeffs[dbl_idx]; + mnt4_Fq4 g_RR_at_P = mnt4_Fq4(- dc.c_4C - dc.c_J * p.first.PX_twist + dc.c_L, + dc.c_H * p.first.PY_twist); + f=f*g_RR_at_P; + + } + ++dbl_idx; + + if (bit) + { + for(auto& p:v) + { + auto L1_coeff = mnt4_Fq2(p.first.PX, mnt4_Fq::zero()) - p.second.QX_over_twist; + auto ac = p.second.add_coeffs[add_idx]; + mnt4_Fq4 g_RQ_at_P = mnt4_Fq4(ac.c_RZ * p.first.PY_twist, + -(p.second.QY_over_twist * ac.c_RZ + L1_coeff * ac.c_L1)); + f =f* g_RQ_at_P; + + } + ++add_idx; + } + + } + if (mnt4_ate_is_loop_count_neg) + { + for(auto& p:v) + { + auto L1_coeff = mnt4_Fq2(p.first.PX, mnt4_Fq::zero()) - p.second.QX_over_twist; + auto ac = p.second.add_coeffs[add_idx]; + mnt4_Fq4 g_RnegR_at_P = mnt4_Fq4(ac.c_RZ * p.first.PY_twist, + -(p.second.QY_over_twist * ac.c_RZ + L1_coeff * ac.c_L1)); + f =f* g_RnegR_at_P; + + } + f=f.inverse(); + ++add_idx; + } + leave_block("Call to mnt4_ate_multiple_miller_loop"); + return f; + +} +mnt4_Fq4 mnt4_multiple_miller_loop(const std::initializer_list >& v +) +{ + return mnt4_ate_multiple_miller_loop(v); +} } // libsnark diff --git a/src/algebra/curves/mnt/mnt4/mnt4_pairing.hpp b/src/algebra/curves/mnt/mnt4/mnt4_pairing.hpp index 00471151..a41f7e09 100644 --- a/src/algebra/curves/mnt/mnt4/mnt4_pairing.hpp +++ b/src/algebra/curves/mnt/mnt4/mnt4_pairing.hpp @@ -143,6 +143,18 @@ mnt4_GT mnt4_reduced_pairing(const mnt4_G1 &P, mnt4_GT mnt4_affine_reduced_pairing(const mnt4_G1 &P, const mnt4_G2 &Q); +//Ariel additions +mnt4_Fq4 mnt4_multiple_miller_loop(const std::initializer_list >& v +); +mnt4_Fq4 mnt4_ate_multiple_miller_loop(const std::initializer_list >& v +); + } // libsnark #endif // MNT4_PAIRING_HPP_ diff --git a/src/algebra/curves/mnt/mnt4/mnt4_pp.cpp b/src/algebra/curves/mnt/mnt4/mnt4_pp.cpp index 17eccab0..d1b727bd 100644 --- a/src/algebra/curves/mnt/mnt4/mnt4_pp.cpp +++ b/src/algebra/curves/mnt/mnt4/mnt4_pp.cpp @@ -83,7 +83,14 @@ mnt4_Fq4 mnt4_pp::double_miller_loop(const mnt4_G1_precomp &prec_P1, { return mnt4_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); } - +mnt4_Fq4 mnt4_pp::multiple_miller_loop(const std::initializer_list >& v +) +{ + return mnt4_multiple_miller_loop(v); +} mnt4_Fq4 mnt4_pp::pairing(const mnt4_G1 &P, const mnt4_G2 &Q) { diff --git a/src/algebra/curves/mnt/mnt4/mnt4_pp.hpp b/src/algebra/curves/mnt/mnt4/mnt4_pp.hpp index f93fc313..51bdf623 100644 --- a/src/algebra/curves/mnt/mnt4/mnt4_pp.hpp +++ b/src/algebra/curves/mnt/mnt4/mnt4_pp.hpp @@ -65,6 +65,11 @@ class mnt4_pp { const mnt4_G2_precomp &prec_Q1, const mnt4_G1_precomp &prec_P2, const mnt4_G2_precomp &prec_Q2); + static mnt4_Fq4 multiple_miller_loop(const std::initializer_list >& v +); /* the following are used in test files */ static mnt4_Fq4 pairing(const mnt4_G1 &P, diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index d3262b6e..ee3a7d7b 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -105,7 +105,12 @@ void multiple_miller_loop_test() const Fqk ans_3 = ppT::miller_loop(prec_P3, prec_Q3); const Fqk ans_4 = ppT::miller_loop(prec_P4, prec_Q4); const Fqk ans_5 = ppT::miller_loop(prec_P5, prec_Q5); - + const Fqk ans_1234 = ppT::multiple_miller_loop({ + std::make_pair(prec_P1, prec_Q1), + std::make_pair(prec_P2, prec_Q2), + std::make_pair(prec_P3, prec_Q3), + std::make_pair(prec_P4, prec_Q4), + }); const Fqk ans_12345 = ppT::multiple_miller_loop({ std::make_pair(prec_P1, prec_Q1), std::make_pair(prec_P2, prec_Q2), @@ -114,6 +119,9 @@ void multiple_miller_loop_test() std::make_pair(prec_P5, prec_Q5) }); assert(ans_1 * ans_2 * ans_3 * ans_4 * ans_5 == ans_12345); + assert(ans_1 * ans_2 * ans_3 * ans_4 == ans_1234); + + assert(!(ans_1*ans_3*ans_4==ans_12345)); } template @@ -171,6 +179,8 @@ int main(void) pairing_test(); double_miller_loop_test(); multiple_miller_loop_test(); + multiple_miller_loop_test(); + #ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled bn128_pp::init_public_params(); pairing_test(); diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index b9b6d7dc..4eded905 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -258,6 +258,8 @@ class r1cs_ppzksnark_processed_verification_key { * and are used in each invocation of r1cs_ppzksnark_probabilistic_verifier * and also r1cs_ppzksnark_batcher */ + +//Better explanation of names, have this inherit from processed_verification_key template class r1cs_ppzksnark_processed_batch_verification_key { public: From 8aac4d2143e5164979d4ddde0c66e67a17e9f780 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Tue, 27 Dec 2016 20:55:14 +0100 Subject: [PATCH 10/14] multiple_miller_loop working for mnt_6 --- src/algebra/curves/mnt/mnt6/mnt6_pairing.cpp | 87 +++++++++++++++++++ src/algebra/curves/mnt/mnt6/mnt6_pairing.hpp | 13 +++ src/algebra/curves/mnt/mnt6/mnt6_pp.cpp | 8 ++ src/algebra/curves/mnt/mnt6/mnt6_pp.hpp | 6 ++ src/algebra/curves/tests/test_bilinearity.cpp | 7 +- 5 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/algebra/curves/mnt/mnt6/mnt6_pairing.cpp b/src/algebra/curves/mnt/mnt6/mnt6_pairing.cpp index 41fc2f5d..64aecf81 100644 --- a/src/algebra/curves/mnt/mnt6/mnt6_pairing.cpp +++ b/src/algebra/curves/mnt/mnt6/mnt6_pairing.cpp @@ -749,5 +749,92 @@ mnt6_GT mnt6_affine_reduced_pairing(const mnt6_G1 &P, const mnt6_GT result = mnt6_final_exponentiation(f); return result; } +//Ariel additions +mnt6_Fq6 mnt6_ate_multiple_miller_loop(const std::initializer_list >& v +) +{ + enter_block("Call to mnt6_ate_multiple_miller_loop"); + + + mnt6_Fq6 f = mnt6_Fq6::one(); + + bool found_one = false; + size_t dbl_idx = 0; + size_t add_idx = 0; + + const bigint &loop_count = mnt6_ate_loop_count; + for (long i = loop_count.max_bits() - 1; i >= 0; --i) + { + const bool bit = loop_count.test_bit(i); + + if (!found_one) + { + /* this skips the MSB itself */ + found_one |= bit; + continue; + } + + /* code below gets executed for all bits (EXCEPT the MSB itself) of + mnt6_param_p (skipping leading zeros) in MSB to LSB + order */ + + f=f.squared(); + for(auto& p:v) + { + mnt6_ate_dbl_coeffs dc = p.second.dbl_coeffs[dbl_idx]; + mnt6_Fq6 g_RR_at_P = mnt6_Fq6(- dc.c_4C - dc.c_J * p.first.PX_twist + dc.c_L, + dc.c_H * p.first.PY_twist); + f=f*g_RR_at_P; + + } + ++dbl_idx; + + if (bit) + { + for(auto& p:v) + { + //mnt6_Fq3 L1_coeff1 = mnt6_Fq3(prec_P1.PX, mnt6_Fq::zero(), mnt6_Fq::zero()) - prec_Q1.QX_over_twist; + + mnt6_Fq3 L1_coeff = mnt6_Fq3(p.first.PX, mnt6_Fq::zero(),mnt6_Fq::zero()) - p.second.QX_over_twist; + auto ac = p.second.add_coeffs[add_idx]; + mnt6_Fq6 g_RQ_at_P = mnt6_Fq6(ac.c_RZ * p.first.PY_twist, + -(p.second.QY_over_twist * ac.c_RZ + L1_coeff * ac.c_L1)); + f =f* g_RQ_at_P; + + } + ++add_idx; + } + + } + + if (mnt6_ate_is_loop_count_neg) + { + for(auto& p:v) + { + auto L1_coeff = mnt6_Fq3(p.first.PX, mnt6_Fq::zero(),mnt6_Fq::zero()) - p.second.QX_over_twist; + auto ac = p.second.add_coeffs[add_idx]; + mnt6_Fq6 g_RnegR_at_P = mnt6_Fq6(ac.c_RZ * p.first.PY_twist, + -(p.second.QY_over_twist * ac.c_RZ + L1_coeff * ac.c_L1)); + f =f* g_RnegR_at_P; + + } + f=f.inverse(); + ++add_idx; + } + leave_block("Call to mnt6_ate_multiple_miller_loop"); + return f; + +} +mnt6_Fq6 mnt6_multiple_miller_loop(const std::initializer_list >& v +) +{ + return mnt6_ate_multiple_miller_loop(v); +} } // libsnark diff --git a/src/algebra/curves/mnt/mnt6/mnt6_pairing.hpp b/src/algebra/curves/mnt/mnt6/mnt6_pairing.hpp index f5c21177..7491cfa1 100644 --- a/src/algebra/curves/mnt/mnt6/mnt6_pairing.hpp +++ b/src/algebra/curves/mnt/mnt6/mnt6_pairing.hpp @@ -143,6 +143,19 @@ mnt6_GT mnt6_reduced_pairing(const mnt6_G1 &P, mnt6_GT mnt6_affine_reduced_pairing(const mnt6_G1 &P, const mnt6_G2 &Q); +//Ariel additions +mnt6_Fq6 mnt6_multiple_miller_loop(const std::initializer_list >& v +); +mnt6_Fq6 mnt6_ate_multiple_miller_loop(const std::initializer_list >& v +); + + } // libsnark #endif // MNT6_PAIRING_HPP_ diff --git a/src/algebra/curves/mnt/mnt6/mnt6_pp.cpp b/src/algebra/curves/mnt/mnt6/mnt6_pp.cpp index 9983d503..648eaf92 100644 --- a/src/algebra/curves/mnt/mnt6/mnt6_pp.cpp +++ b/src/algebra/curves/mnt/mnt6/mnt6_pp.cpp @@ -66,6 +66,14 @@ mnt6_Fq6 mnt6_pp::double_miller_loop(const mnt6_G1_precomp &prec_P1, return mnt6_double_miller_loop(prec_P1, prec_Q1, prec_P2, prec_Q2); } +mnt6_Fq6 mnt6_pp::multiple_miller_loop(const std::initializer_list >& v +) +{ + return mnt6_multiple_miller_loop(v); +} mnt6_Fq6 mnt6_pp::affine_ate_e_over_e_miller_loop(const mnt6_affine_ate_G1_precomputation &prec_P1, const mnt6_affine_ate_G2_precomputation &prec_Q1, const mnt6_affine_ate_G1_precomputation &prec_P2, diff --git a/src/algebra/curves/mnt/mnt6/mnt6_pp.hpp b/src/algebra/curves/mnt/mnt6/mnt6_pp.hpp index bbe05a4e..e674937a 100644 --- a/src/algebra/curves/mnt/mnt6/mnt6_pp.hpp +++ b/src/algebra/curves/mnt/mnt6/mnt6_pp.hpp @@ -61,6 +61,12 @@ class mnt6_pp { const mnt6_G1_precomp &prec_P2, const mnt6_G2_precomp &prec_Q2); + static mnt6_Fq6 multiple_miller_loop(const std::initializer_list >& v +); + /* the following are used in test files */ static mnt6_Fq6 pairing(const mnt6_G1 &P, const mnt6_G2 &Q); diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index ee3a7d7b..53f43466 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -169,17 +169,18 @@ int main(void) pairing_test(); double_miller_loop_test(); affine_pairing_test(); - + multiple_miller_loop_test(); + mnt4_pp::init_public_params(); pairing_test(); double_miller_loop_test(); affine_pairing_test(); - + multiple_miller_loop_test(); + alt_bn128_pp::init_public_params(); pairing_test(); double_miller_loop_test(); multiple_miller_loop_test(); - multiple_miller_loop_test(); #ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled bn128_pp::init_public_params(); From c526e1b286afe4386bfae26dd6798cde18fbc1ed Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Tue, 27 Dec 2016 22:35:26 +0100 Subject: [PATCH 11/14] multiple_miller_loop for BN128 --- src/algebra/curves/bn128/bn128_pairing.cpp | 17 +++++++++++++++++ src/algebra/curves/bn128/bn128_pp.cpp | 13 +++++++++++++ src/algebra/curves/bn128/bn128_pp.hpp | 7 +++++++ src/algebra/curves/tests/test_bilinearity.cpp | 1 + 4 files changed, 38 insertions(+) diff --git a/src/algebra/curves/bn128/bn128_pairing.cpp b/src/algebra/curves/bn128/bn128_pairing.cpp index ac4290ac..13e29f0b 100644 --- a/src/algebra/curves/bn128/bn128_pairing.cpp +++ b/src/algebra/curves/bn128/bn128_pairing.cpp @@ -200,7 +200,24 @@ bn128_Fq12 bn128_double_ate_miller_loop(const bn128_ate_G1_precomp &prec_P1, bn::components::millerLoop2(f.elem, prec_Q1.coeffs, prec_P1.P, prec_Q2.coeffs, prec_P2.P); return f; } +bn128_Fq12 alt_bn128_ate_multiple_miller_loop( + const std::initializer_list >& v +) +{ + enter_block("Call to alt_bn128_ate_multiple_miller_loop"); + bn128_Fq12 f = bn128_Fq12::one(); + for(auto& p:v) + { + bn128_Fq12 g + bn:components::millerLoop(g.elem,p.first,p.second); + f = f * g; + } + return f; +} bn128_GT bn128_final_exponentiation(const bn128_Fq12 &elt) { enter_block("Call to bn128_final_exponentiation"); diff --git a/src/algebra/curves/bn128/bn128_pp.cpp b/src/algebra/curves/bn128/bn128_pp.cpp index 6557f042..06a5ede9 100644 --- a/src/algebra/curves/bn128/bn128_pp.cpp +++ b/src/algebra/curves/bn128/bn128_pp.cpp @@ -50,6 +50,19 @@ bn128_Fq12 bn128_pp::double_miller_loop(const bn128_ate_G1_precomp &prec_P1, return result; } +//returns the product of Miller loops of all pairs in the list +bn128_Fq12 bn128_pp::multiple_miller_loop( + const std::initializer_list >& v +) +{ + enter_block("Call to multiple_miller_loop"); + bn128_Fq12 result = bn128_multiple_ate_miller_loop(v); + enter_block("Call to multiple_miller_loop"); + return result; +} bn128_Fq12 bn128_pp::pairing(const bn128_G1 &P, const bn128_G2 &Q) { diff --git a/src/algebra/curves/bn128/bn128_pp.hpp b/src/algebra/curves/bn128/bn128_pp.hpp index dfd1c6b4..82c4800d 100644 --- a/src/algebra/curves/bn128/bn128_pp.hpp +++ b/src/algebra/curves/bn128/bn128_pp.hpp @@ -39,6 +39,13 @@ class bn128_pp { const bn128_ate_G2_precomp &prec_Q1, const bn128_ate_G1_precomp &prec_P2, const bn128_ate_G2_precomp &prec_Q2); + //returns the product of Miller loops of all pairs in the list + static bn128_Fq12 alt_bn128_ate_multiple_miller_loop( + const std::initializer_list >& v +) /* the following are used in test files */ static bn128_GT pairing(const bn128_G1 &P, diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index 53f43466..0f86959f 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -186,5 +186,6 @@ int main(void) bn128_pp::init_public_params(); pairing_test(); double_miller_loop_test(); + multiple_miller_loop_test(); #endif } From bb6283adf33f34cb61a9b763755685cf18b4422f Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Tue, 27 Dec 2016 22:53:37 +0100 Subject: [PATCH 12/14] multiple_miller_loop for BN128 --- src/algebra/curves/bn128/bn128_pairing.cpp | 15 +++++++++------ src/algebra/curves/bn128/bn128_pairing.hpp | 7 +++++++ src/algebra/curves/bn128/bn128_pp.cpp | 8 ++++---- src/algebra/curves/bn128/bn128_pp.hpp | 8 ++++---- src/algebra/curves/tests/test_bilinearity.cpp | 2 +- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/algebra/curves/bn128/bn128_pairing.cpp b/src/algebra/curves/bn128/bn128_pairing.cpp index 13e29f0b..ae78e08d 100644 --- a/src/algebra/curves/bn128/bn128_pairing.cpp +++ b/src/algebra/curves/bn128/bn128_pairing.cpp @@ -200,21 +200,24 @@ bn128_Fq12 bn128_double_ate_miller_loop(const bn128_ate_G1_precomp &prec_P1, bn::components::millerLoop2(f.elem, prec_Q1.coeffs, prec_P1.P, prec_Q2.coeffs, prec_P2.P); return f; } -bn128_Fq12 alt_bn128_ate_multiple_miller_loop( + + +bn128_Fq12 bn128_ate_multiple_miller_loop( const std::initializer_list >& v ) { - enter_block("Call to alt_bn128_ate_multiple_miller_loop"); + enter_block("Call to bn128_ate_multiple_miller_loop"); bn128_Fq12 f = bn128_Fq12::one(); for(auto& p:v) { - bn128_Fq12 g - bn:components::millerLoop(g.elem,p.first,p.second); + bn128_Fq12 g; + bn::components::millerLoop(g.elem,p.second.coeffs,p.first.P); f = f * g; } + leave_block("Call to bn128_ate_multiple_miller_loop"); return f; } diff --git a/src/algebra/curves/bn128/bn128_pairing.hpp b/src/algebra/curves/bn128/bn128_pairing.hpp index f639d0a0..3e22bf22 100644 --- a/src/algebra/curves/bn128/bn128_pairing.hpp +++ b/src/algebra/curves/bn128/bn128_pairing.hpp @@ -46,6 +46,13 @@ bn128_Fq12 bn128_double_ate_miller_loop(const bn128_ate_G1_precomp &prec_P1, bn128_Fq12 bn128_ate_miller_loop(const bn128_ate_G1_precomp &prec_P, const bn128_ate_G2_precomp &prec_Q); +//returns the product of Miller loops of all pairs in the list +bn128_Fq12 bn128_ate_multiple_miller_loop( + const std::initializer_list >& v +); bn128_GT bn128_final_exponentiation(const bn128_Fq12 &elt); } // libsnark diff --git a/src/algebra/curves/bn128/bn128_pp.cpp b/src/algebra/curves/bn128/bn128_pp.cpp index 06a5ede9..ec8d1480 100644 --- a/src/algebra/curves/bn128/bn128_pp.cpp +++ b/src/algebra/curves/bn128/bn128_pp.cpp @@ -53,14 +53,14 @@ bn128_Fq12 bn128_pp::double_miller_loop(const bn128_ate_G1_precomp &prec_P1, //returns the product of Miller loops of all pairs in the list bn128_Fq12 bn128_pp::multiple_miller_loop( const std::initializer_list >& v ) { enter_block("Call to multiple_miller_loop"); - bn128_Fq12 result = bn128_multiple_ate_miller_loop(v); - enter_block("Call to multiple_miller_loop"); + bn128_Fq12 result = bn128_ate_multiple_miller_loop(v); + leave_block("Call to multiple_miller_loop"); return result; } bn128_Fq12 bn128_pp::pairing(const bn128_G1 &P, diff --git a/src/algebra/curves/bn128/bn128_pp.hpp b/src/algebra/curves/bn128/bn128_pp.hpp index 82c4800d..ddce3b65 100644 --- a/src/algebra/curves/bn128/bn128_pp.hpp +++ b/src/algebra/curves/bn128/bn128_pp.hpp @@ -40,12 +40,12 @@ class bn128_pp { const bn128_ate_G1_precomp &prec_P2, const bn128_ate_G2_precomp &prec_Q2); //returns the product of Miller loops of all pairs in the list - static bn128_Fq12 alt_bn128_ate_multiple_miller_loop( + static bn128_Fq12 multiple_miller_loop( const std::initializer_list >& v -) +); /* the following are used in test files */ static bn128_GT pairing(const bn128_G1 &P, diff --git a/src/algebra/curves/tests/test_bilinearity.cpp b/src/algebra/curves/tests/test_bilinearity.cpp index 0f86959f..b98e4950 100644 --- a/src/algebra/curves/tests/test_bilinearity.cpp +++ b/src/algebra/curves/tests/test_bilinearity.cpp @@ -181,7 +181,7 @@ int main(void) pairing_test(); double_miller_loop_test(); multiple_miller_loop_test(); - +#define CURVE_BN128 #ifdef CURVE_BN128 // BN128 has fancy dependencies so it may be disabled bn128_pp::init_public_params(); pairing_test(); From 09ad71fca712040196b060d9bf1bfd67b1a94001 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Wed, 28 Dec 2016 16:24:54 +0100 Subject: [PATCH 13/14] added file with explanation of batch verification --- src/zk_proof_systems/batchverification.pdf | Bin 0 -> 125095 bytes .../r1cs_ppzksnark/r1cs_ppzksnark.hpp | 14 ++++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 src/zk_proof_systems/batchverification.pdf diff --git a/src/zk_proof_systems/batchverification.pdf b/src/zk_proof_systems/batchverification.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5aa6c346835d424065eb7a5c41e140e58c421e49 GIT binary patch literal 125095 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 SU;}Y*adDwgQ%fjHqWl-;0o=s^ literal 0 HcmV?d00001 diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index 4eded905..a1e3e653 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -263,12 +263,12 @@ class r1cs_ppzksnark_processed_verification_key { template class r1cs_ppzksnark_processed_batch_verification_key { public: - G2_precomp pair1; - G2_precomp pair2; - G2_precomp pair3; - G2_precomp pair4; - G2_precomp pair5; - G2_precomp pair6; + G2_precomp pair1_G2arg; + G2_precomp pair2_G2arg; + G2_precomp pair3_G2arg; + G2_precomp pair4_G2arg; + G2_precomp pair5_G2arg; + G2_precomp pair6_G2arg; // accumulation_vector > encoded_IC_query; @@ -289,8 +289,6 @@ class batch_verification_accumulator{ G1 pair6; Fqk pair7; -//batch_verification_accumulator() = default; - batch_verification_accumulator():pair1(G1::zero()),pair2(G1::zero()),pair3(G1::zero()),pair4(G1::zero()), pair5(G1::zero()),pair6(G1::zero()),pair7(Fqk::one()) { From 0c7f3c0808c72eaff9efbc7ffe553172d1d48ed3 Mon Sep 17 00:00:00 2001 From: Ariel Gabizon Date: Wed, 28 Dec 2016 18:06:08 +0100 Subject: [PATCH 14/14] improved documentation --- .../r1cs_ppzksnark/r1cs_ppzksnark.hpp | 108 +++++++++++------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp index a1e3e653..2760895b 100644 --- a/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp +++ b/src/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp @@ -252,49 +252,6 @@ class r1cs_ppzksnark_processed_verification_key { friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); }; -/** - * The purpose of this class is to enable one time precomputation of elements that are - * functions of the verification key - * and are used in each invocation of r1cs_ppzksnark_probabilistic_verifier - * and also r1cs_ppzksnark_batcher - */ - -//Better explanation of names, have this inherit from processed_verification_key -template -class r1cs_ppzksnark_processed_batch_verification_key { -public: - G2_precomp pair1_G2arg; - G2_precomp pair2_G2arg; - G2_precomp pair3_G2arg; - G2_precomp pair4_G2arg; - G2_precomp pair5_G2arg; - G2_precomp pair6_G2arg; - - // accumulation_vector > encoded_IC_query; - - // bool operator==(const r1cs_ppzksnark_processed_verification_key &other) const; - // friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_processed_verification_key &pvk); - // friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); -}; - -//a data structure to store intermediate results from various proofs before the final batch verification -template -class batch_verification_accumulator{ -public: - G1 pair1; - G1 pair2; - G1 pair3; - G1 pair4; - G1 pair5; - G1 pair6; - Fqk pair7; - -batch_verification_accumulator():pair1(G1::zero()),pair2(G1::zero()),pair3(G1::zero()),pair4(G1::zero()), -pair5(G1::zero()),pair6(G1::zero()),pair7(Fqk::one()) -{ - -}; -}; /********************************** Key pair *********************************/ @@ -505,7 +462,70 @@ bool r1cs_ppzksnark_affine_verifier_weak_IC(const r1cs_ppzksnark_verification_ke const r1cs_ppzksnark_primary_input &primary_input, const r1cs_ppzksnark_proof &proof); -/****Ariel stuff ***/ +/****Batch and probabilistic verification + * using randomness and the bilinearity of the pairing operation, the Pinocchio verifier can be made more efficient + * with the price of making the verification procedure probabilisitc, introducing a negligible chance of accepting a bad proofs + * in a similar way verifying a batch of proofs can be bundled into verifying a single pairing equation using randomness + * such that the verifier always accepts if all proofs in the batch are valid, and the verifier accepts with a negligible + * probability when one of the proofs in the batch is invalid. + * The methods below implemement these verifiers, + * and the logic behind them are described in https://github.com/arielgabizon/libsnark/blob/multimiller/src/zk_proof_systems/batchverification.pdf + * + */ +/** + * The purpose of this class is to enable one time precomputation of elements that are + * functions of the verification key + * and are used in each invocation of r1cs_ppzksnark_probabilistic_verifier + * and also r1cs_ppzksnark_batcher + * these elements are the G2 arguments of the pairings in + * https://github.com/arielgabizon/libsnark/blob/multimiller/src/zk_proof_systems/batchverification.pdf + */ + +//Better explanation of names, have this inherit from processed_verification_key +template +class r1cs_ppzksnark_processed_batch_verification_key : public r1cs_ppzksnark_processed_verification_key { +public: + //here pair_i is actually the G2 argument in the i'th pairing of the batch verifier equation + G2_precomp pair1; + G2_precomp pair2; + G2_precomp pair3; + G2_precomp pair4; + G2_precomp pair5; + G2_precomp pair6; + + // accumulation_vector > encoded_IC_query; + + // bool operator==(const r1cs_ppzksnark_processed_verification_key &other) const; + // friend std::ostream& operator<< (std::ostream &out, const r1cs_ppzksnark_processed_verification_key &pvk); + // friend std::istream& operator>> (std::istream &in, r1cs_ppzksnark_processed_verification_key &pvk); +}; + +//a data structure to store intermediate G1 argument results from various proofs before the final batch verification +// (except for the seventh member where we store the result of a product of Miller Loops. See Section 2 of https://github.com/arielgabizon/libsnark/blob/multimiller/src/zk_proof_systems/batchverification.pdf. +template +class batch_verification_accumulator{ +public: + //here pair_i is actually the G1 argument in the i'th pairing of the batch verifier equation + G1 pair1; + G1 pair2; + G1 pair3; + G1 pair4; + G1 pair5; + G1 pair6; + //as explained in the document, the 7th pairing contains elements of the proof in both arguments, so we need to store + //a product of Miller Loops rather than just the first argument + Fqk pair7; + +batch_verification_accumulator():pair1(G1::zero()),pair2(G1::zero()),pair3(G1::zero()),pair4(G1::zero()), +pair5(G1::zero()),pair6(G1::zero()),pair7(Fqk::one()) +{ + +}; +}; + + + + /* * accumulate another proof inside acc for the final batch check