From 4ba7fde2f64bf93c07e223d3e89b9f1b2f347bd1 Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Wed, 7 Dec 2022 11:29:49 +0000 Subject: [PATCH 1/9] plonk: added helper function for preparing the gates matrix (Issue https://github.com/clearmatics/libsnark/issues/89) --- .../plonk/tests/test_plonk.cpp | 13 ++++++++ libsnark/zk_proof_systems/plonk/utils.hpp | 7 ++++ libsnark/zk_proof_systems/plonk/utils.tcc | 33 +++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index 416de03ff..9751a5cf5 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1095,6 +1095,18 @@ template void test_plonk_gates_matrix_transpose() ASSERT_EQ(gates_matrix_transpose, example.gates_matrix_transpose); } +template void test_plonk_prepare_gates_matrix() +{ + using Field = libff::Fr; + const size_t num_public_inputs = 8; + const std::vector> gates_matrix_init = + plonk_prepare_gates_matrix(num_public_inputs); + const std::vector PI_selector_vector{1, 0, 0, 0, 0}; + for (size_t i = 0; i < num_public_inputs; ++i) { + ASSERT_EQ(gates_matrix_init[i], PI_selector_vector); + } +} + // generic test for all curves template void test_plonk_constants_k1_k2() { @@ -1254,6 +1266,7 @@ TEST(TestPlonk, BLS12_381) libff::bls12_381_pp, bls12_381_test_vector_transcript_hasher>(); test_plonk_gates_matrix_transpose(); + test_plonk_prepare_gates_matrix(); } } // namespace libsnark diff --git a/libsnark/zk_proof_systems/plonk/utils.hpp b/libsnark/zk_proof_systems/plonk/utils.hpp index a93787365..a9035a5d7 100644 --- a/libsnark/zk_proof_systems/plonk/utils.hpp +++ b/libsnark/zk_proof_systems/plonk/utils.hpp @@ -227,6 +227,13 @@ void plonk_generate_random_constants_k1_k2( template bool plonk_are_valid_constants_k1_k2(const FieldT &k1, const FieldT &k2); +/// Return a matrix with top N rows filled in with the public inputs +/// selector vectors where N = num_public_inputs is the number of +/// public inputs passed as an input parameter by the caller. +template +std::vector>> plonk_prepare_gates_matrix( + const size_t &num_public_inputs); + } // namespace libsnark #include "libsnark/zk_proof_systems/plonk/utils.tcc" diff --git a/libsnark/zk_proof_systems/plonk/utils.tcc b/libsnark/zk_proof_systems/plonk/utils.tcc index cc85aabe7..0eb268a1f 100644 --- a/libsnark/zk_proof_systems/plonk/utils.tcc +++ b/libsnark/zk_proof_systems/plonk/utils.tcc @@ -383,6 +383,39 @@ bool plonk_are_valid_constants_k1_k2(const FieldT &k1, const FieldT &k2) return (k1_outside_H && k2_outside_H && k1_over_k2_outside_H); } +// In general, the i-th row of the gates matrix contains the i-th +// component of the selector vectors q_L, q_M, q_R, q_O, q_C (see +// Section 6 [GWC19]). The i-th compoment of each selector vector is +// determined by the i-th gate of the arithmetic circuit, which can be +// one of the following: addition, multiplication, multiplication by +// constant, public input. In particular, when the i-th gate is a +// public input, the i-th components of the selector vectors are: +// +// (q_L[i], q_M[i], q_R[i], q_O[i], q_C[i]) = (1, 0, 0, 0, 0) +// +// Therefore the top N rows of the initialized gates matrix will have +// the above form. See also Section 6 [GWC19]. +// +// \attention The convention for selector vector values corresponding +// to public inputs (PI) used in this function follows [GWC19] (as +// shown above) and is different from the one used in the example +// circuit for BLS12-381 (class example) where q_R[i]=1 rather than +// q_L[i]=1 i.e. +// +// (q_L[i], q_M[i], q_R[i], q_O[i], q_C[i]) = (0, 1, 0, 0, 0) +template +std::vector>> plonk_prepare_gates_matrix( + const size_t &num_public_inputs) +{ + using FieldT = libff::Fr; + std::vector> gates_matrix_init; + const std::vector PI_selector_vector{1, 0, 0, 0, 0}; + for (size_t i = 0; i < num_public_inputs; ++i) { + gates_matrix_init.push_back(PI_selector_vector); + } + return gates_matrix_init; +} + } // namespace libsnark #endif // LIBSNARK_ZK_PROOF_SYSTEMS_PLONK_UTILS_TCC_ From b5c3d864a860dd5b9b09e79e7cc108216c28875a Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Fri, 9 Dec 2022 12:32:11 +0000 Subject: [PATCH 2/9] plonk: added assert for the size of the returned gates matrix (https://github.com/clearmatics/libsnark/pull/97#discussion_r1042471794) --- libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index 9751a5cf5..ebc5ac59b 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1101,6 +1101,7 @@ template void test_plonk_prepare_gates_matrix() const size_t num_public_inputs = 8; const std::vector> gates_matrix_init = plonk_prepare_gates_matrix(num_public_inputs); + ASSERT_EQ(gates_matrix_init.size(), num_public_inputs); const std::vector PI_selector_vector{1, 0, 0, 0, 0}; for (size_t i = 0; i < num_public_inputs; ++i) { ASSERT_EQ(gates_matrix_init[i], PI_selector_vector); From 1c231c953eb30fd2cce79ec5a5ee47475c571601 Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Mon, 12 Dec 2022 13:05:47 +0000 Subject: [PATCH 3/9] plonk: added proof and verfication for a simple circuit to test the convenience of the gates helper function (https://github.com/clearmatics/libsnark/pull/97#pullrequestreview-1208811967) --- .../plonk/tests/test_plonk.cpp | 139 +++++++++++++++++- libsnark/zk_proof_systems/plonk/utils.hpp | 13 ++ libsnark/zk_proof_systems/plonk/utils.tcc | 27 +++- 3 files changed, 168 insertions(+), 11 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index ebc5ac59b..3034c4940 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1095,17 +1095,140 @@ template void test_plonk_gates_matrix_transpose() ASSERT_EQ(gates_matrix_transpose, example.gates_matrix_transpose); } -template void test_plonk_prepare_gates_matrix() +// We test the example circuit y^2 = x mod r where x=49 is a public +// input and y is the witness. We prove that we know y=7 such that y +// is the quadratic residue of x modulo the modulus r of the scalar +// field Fr of the given elliptic curve. The circuit is represented by +// one public input (PI) and one multiplication gate. According to the +// Plonk arithmetization rules, each gate (including the PI) is +// represented as: +// +// qL a + qR b + qO c + qM ab + qC = 0 +// +// where qL,qR,qO,qM,qC are the selector polynomials and +// a,b,c are respectively the vectors of left inputs, right inputs +// and outputs to the gates. +// +// Using the above representation, the PI and the multiplication +// gates are given resp. as +// +// PI: 1 a1 + 0 b1 + 0 c1 + 0 a1 b1 + 0 = 49 +// MUL: 0 a1 + 0 b1 + (-1) c1 + 1 a1 b1 + 0 = 0 +// +// where +// +// a = (a1, a2) = (x, y) +// b = (b1, b2) = (1, y) +// c = (c1, c2) = (x, x) +// qL = (1, 0) +// qR = (0, 0) +// qO = (0, -1) +// qM = (0, 1) +// qC = (0, 0) +// +// The selector polynomials as given above define the following gates +// matrix: +// +// qL qR qO qM qC +// 1 0 0 0 0 +// 0 0 -1 1 0 +// +// and the permutation expressing the copy-constraints is (second +// line below): +// +// (a1 a2 b1 b2 c1 c2) +// (c1 b2 b1 a2 c2 a1) +// +// reflecting the fact that a1=c1=c2=x and a2=b2=y +template +void test_plonk_prepare_gates_matrix() { using Field = libff::Fr; - const size_t num_public_inputs = 8; - const std::vector> gates_matrix_init = + + // 0 Arithmetization of test circuit y^2 = x mod r + + // The number of gates is 2: one public input (PI) gate and one + // multiplication gate. Tis nummer is also conveniently a power of + // 2 (needed for the FFT/iFFT) + const size_t num_gates = 2; + // The tested circuit has 1 public input + const size_t num_public_inputs = 1; + std::vector> gates_matrix = plonk_prepare_gates_matrix(num_public_inputs); - ASSERT_EQ(gates_matrix_init.size(), num_public_inputs); - const std::vector PI_selector_vector{1, 0, 0, 0, 0}; + ASSERT_EQ(gates_matrix.size(), num_public_inputs); + // Add the PI gate/s + const std::vector PI_gate{1, 0, 0, 0, 0}; for (size_t i = 0; i < num_public_inputs; ++i) { - ASSERT_EQ(gates_matrix_init[i], PI_selector_vector); + ASSERT_EQ(gates_matrix[i], PI_gate); + } + // Add the multiplication gate + const std::vector MUL_gate{0, 0, -1, 1, 0}; + gates_matrix.push_back(MUL_gate); + ASSERT_EQ(gates_matrix.size(), 2); + ASSERT_EQ(gates_matrix[0].size(), 5); + // Extract the PI indices from the gates matrix + std::vector PI_wire_indices = + plonk_public_input_indices_from_gates_matrix(gates_matrix); + ASSERT_EQ(PI_wire_indices.size(), 1); + ASSERT_EQ(PI_wire_indices[0], 0); + // Hard-code the wire permutation for the tested circuit. Note + // that counting of indices starts from 1. TODO: implement a + // general function to compute the wire permutation for any + // circuit + std::vector wire_permutation{5, 4, 3, 2, 6, 1}; + // maximum degree of the encoded monomials in the usrs + size_t max_degree = PLONK_MAX_DEGREE; + // Random hidden element kept secret (toxic waste) + Field secret = Field::random_element(); + + std::shared_ptr> domain = + libfqfft::get_evaluation_domain(num_gates); + + // 1 Generate SRS + + // compute usrs + usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); + // compute srs + srs srs = plonk_srs_derive_from_usrs( + usrs, gates_matrix, wire_permutation, PI_wire_indices); + + // 2 Compute proof + + // initialize prover hasher + transcript_hasher prover_hasher; + // prepare witness vector w = a+b+c + std::vector witness{49, 7, 1, 7, 49, 49}; + // nine random blinding constants for the prover polynomials + std::vector> blind_scalars; + const size_t nscalars = 9; + for (size_t i = 0; i < nscalars; ++i) { + Field r = Field::random_element(); + blind_scalars.push_back(r); + } + // initialize prover + plonk_prover prover; + // compute proof + plonk_proof proof = + prover.compute_proof(srs, witness, blind_scalars, prover_hasher); + + // 3 Verify proof + + // initialize verifier hasher + transcript_hasher verifier_hasher; + // Prepare the list of PI values. for the example circuit + std::vector PI_value_list; + for (size_t i = 0; i < PI_wire_indices.size(); i++) { + Field PI_value = witness[PI_wire_indices[i]]; + PI_value_list.push_back(PI_value); } + ASSERT_EQ(PI_value_list.size(), 1); + ASSERT_EQ(PI_value_list[0], Field(49)); + // initialize verifier + plonk_verifier verifier; + // verify proof + bool b_valid_proof = + verifier.verify_proof(proof, srs, PI_value_list, verifier_hasher); + ASSERT_TRUE(b_valid_proof); } // generic test for all curves @@ -1267,7 +1390,9 @@ TEST(TestPlonk, BLS12_381) libff::bls12_381_pp, bls12_381_test_vector_transcript_hasher>(); test_plonk_gates_matrix_transpose(); - test_plonk_prepare_gates_matrix(); + test_plonk_prepare_gates_matrix< + libff::bls12_381_pp, + bls12_381_test_vector_transcript_hasher>(); } } // namespace libsnark diff --git a/libsnark/zk_proof_systems/plonk/utils.hpp b/libsnark/zk_proof_systems/plonk/utils.hpp index a9035a5d7..6a10159aa 100644 --- a/libsnark/zk_proof_systems/plonk/utils.hpp +++ b/libsnark/zk_proof_systems/plonk/utils.hpp @@ -234,6 +234,19 @@ template std::vector>> plonk_prepare_gates_matrix( const size_t &num_public_inputs); +/// The function extracts the indices of the public inputs (PI) from the input +/// gates matrix under the convention that the i-th components of the selector +/// vectors q_L, q_R, q_O, q_M, q_C corresponding to the i-th selector vector +/// have the form +/// +/// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (1, 0, 0, 0, 0) +/// +/// The vector of PI indices is equal to the vector of indices of all rows in +/// the gates matrix that are equal to (1, 0, 0, 0, 0) +template +std::vector plonk_public_input_indices_from_gates_matrix( + const std::vector>> &gates_matrix); + } // namespace libsnark #include "libsnark/zk_proof_systems/plonk/utils.tcc" diff --git a/libsnark/zk_proof_systems/plonk/utils.tcc b/libsnark/zk_proof_systems/plonk/utils.tcc index 0eb268a1f..38c26f8cc 100644 --- a/libsnark/zk_proof_systems/plonk/utils.tcc +++ b/libsnark/zk_proof_systems/plonk/utils.tcc @@ -308,8 +308,8 @@ std::vector> plonk_gates_matrix_transpose( std::vector> gates_matrix_transpose( nrows_transpose, std::vector(ncols_transpose)); for (size_t irow = 0; irow < nrows; ++irow) { + assert(gates_matrix[irow].size() == ncols); for (size_t icol = 0; icol < ncols; ++icol) { - assert(gates_matrix[icol].size() == ncols); gates_matrix_transpose[icol][irow] = gates_matrix[irow][icol]; } } @@ -384,14 +384,14 @@ bool plonk_are_valid_constants_k1_k2(const FieldT &k1, const FieldT &k2) } // In general, the i-th row of the gates matrix contains the i-th -// component of the selector vectors q_L, q_M, q_R, q_O, q_C (see +// component of the selector vectors q_L, q_R, q_O, q_M, q_C (see // Section 6 [GWC19]). The i-th compoment of each selector vector is // determined by the i-th gate of the arithmetic circuit, which can be // one of the following: addition, multiplication, multiplication by // constant, public input. In particular, when the i-th gate is a // public input, the i-th components of the selector vectors are: // -// (q_L[i], q_M[i], q_R[i], q_O[i], q_C[i]) = (1, 0, 0, 0, 0) +// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (1, 0, 0, 0, 0) // // Therefore the top N rows of the initialized gates matrix will have // the above form. See also Section 6 [GWC19]. @@ -402,7 +402,7 @@ bool plonk_are_valid_constants_k1_k2(const FieldT &k1, const FieldT &k2) // circuit for BLS12-381 (class example) where q_R[i]=1 rather than // q_L[i]=1 i.e. // -// (q_L[i], q_M[i], q_R[i], q_O[i], q_C[i]) = (0, 1, 0, 0, 0) +// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (0, 1, 0, 0, 0) template std::vector>> plonk_prepare_gates_matrix( const size_t &num_public_inputs) @@ -416,6 +416,25 @@ std::vector>> plonk_prepare_gates_matrix( return gates_matrix_init; } +// Examine the gates matrix row by row and store the indices of those rows that +// are equal to (1, 0, 0, 0, 0). These are the rows corresponding to public +// inputs. +template +std::vector plonk_public_input_indices_from_gates_matrix( + const std::vector>> &gates_matrix) +{ + using FieldT = libff::Fr; + // Vector of indices of wires corresponding to public inputs (PI) + std::vector PI_wire_indices; + const std::vector PI_selector_vector{1, 0, 0, 0, 0}; + for (size_t i = 0; i < gates_matrix.size(); ++i) { + // store the index of the gates matrix row encoding a PI + if (gates_matrix[i] == PI_selector_vector) { + PI_wire_indices.push_back(i); + } + } + return PI_wire_indices; +} } // namespace libsnark #endif // LIBSNARK_ZK_PROOF_SYSTEMS_PLONK_UTILS_TCC_ From a8df063d70cc50308bdc76a9359999a985899bff Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Thu, 15 Dec 2022 11:04:32 +0000 Subject: [PATCH 4/9] plonk: added edits and clarifications in comments regarding preparation of gates matrix code --- .../plonk/tests/test_plonk.cpp | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index 3034c4940..57c40d30d 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1095,13 +1095,14 @@ template void test_plonk_gates_matrix_transpose() ASSERT_EQ(gates_matrix_transpose, example.gates_matrix_transpose); } -// We test the example circuit y^2 = x mod r where x=49 is a public -// input and y is the witness. We prove that we know y=7 such that y -// is the quadratic residue of x modulo the modulus r of the scalar -// field Fr of the given elliptic curve. The circuit is represented by -// one public input (PI) and one multiplication gate. According to the -// Plonk arithmetization rules, each gate (including the PI) is -// represented as: +// We test the example circuit y^2 = x mod r where x is a public input +// and y is the witness. We prove that we know y such that x is the +// quadratic residue of y modulo the modulus r of the scalar field Fr +// of the given elliptic curve. For example x=49, y=7. +// +// The circuit is represented by one public input (PI) and one +// multiplication gate. According to the Plonk arithmetization rules, +// each gate (including the PI) is represented as: // // qL a + qR b + qO c + qM ab + qC = 0 // @@ -1113,7 +1114,7 @@ template void test_plonk_gates_matrix_transpose() // gates are given resp. as // // PI: 1 a1 + 0 b1 + 0 c1 + 0 a1 b1 + 0 = 49 -// MUL: 0 a1 + 0 b1 + (-1) c1 + 1 a1 b1 + 0 = 0 +// MUL: 0 a2 + 0 b2 + (-1) c2 + 1 a2 b2 + 0 = 0 // // where // @@ -1148,7 +1149,7 @@ void test_plonk_prepare_gates_matrix() // 0 Arithmetization of test circuit y^2 = x mod r // The number of gates is 2: one public input (PI) gate and one - // multiplication gate. Tis nummer is also conveniently a power of + // multiplication gate. Tis number is also conveniently a power of // 2 (needed for the FFT/iFFT) const size_t num_gates = 2; // The tested circuit has 1 public input @@ -1156,7 +1157,8 @@ void test_plonk_prepare_gates_matrix() std::vector> gates_matrix = plonk_prepare_gates_matrix(num_public_inputs); ASSERT_EQ(gates_matrix.size(), num_public_inputs); - // Add the PI gate/s + // Assert that the PI gates are located at the top N rows of the gates + // matrix const std::vector PI_gate{1, 0, 0, 0, 0}; for (size_t i = 0; i < num_public_inputs; ++i) { ASSERT_EQ(gates_matrix[i], PI_gate); From 712e50dc6c77aa70df74cf14efcef455e6bad303 Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Thu, 15 Dec 2022 11:13:21 +0000 Subject: [PATCH 5/9] plonk: moved the gates matrix check into a separate unit test (https://github.com/clearmatics/libsnark/pull/97#discussion_r1048696516) --- .../plonk/tests/test_plonk.cpp | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index 57c40d30d..fb2e19f53 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1157,12 +1157,6 @@ void test_plonk_prepare_gates_matrix() std::vector> gates_matrix = plonk_prepare_gates_matrix(num_public_inputs); ASSERT_EQ(gates_matrix.size(), num_public_inputs); - // Assert that the PI gates are located at the top N rows of the gates - // matrix - const std::vector PI_gate{1, 0, 0, 0, 0}; - for (size_t i = 0; i < num_public_inputs; ++i) { - ASSERT_EQ(gates_matrix[i], PI_gate); - } // Add the multiplication gate const std::vector MUL_gate{0, 0, -1, 1, 0}; gates_matrix.push_back(MUL_gate); @@ -1233,6 +1227,25 @@ void test_plonk_prepare_gates_matrix() ASSERT_TRUE(b_valid_proof); } +template void test_plonk_gates_matrix() +{ + using Field = libff::Fr; + // Test for 10 random values of num_public_inputs + const size_t ntests = 10; + for (size_t i = 0; i < ntests; ++i) { + const size_t num_public_inputs = random() % 100; + std::vector> gates_matrix = + plonk_prepare_gates_matrix(num_public_inputs); + ASSERT_EQ(gates_matrix.size(), num_public_inputs); + // Assert that the PI gates are located at the top N rows of the gates + // matrix + const std::vector PI_gate{1, 0, 0, 0, 0}; + for (size_t i = 0; i < num_public_inputs; ++i) { + ASSERT_EQ(gates_matrix[i], PI_gate); + } + } +} + // generic test for all curves template void test_plonk_constants_k1_k2() { @@ -1395,6 +1408,7 @@ TEST(TestPlonk, BLS12_381) test_plonk_prepare_gates_matrix< libff::bls12_381_pp, bls12_381_test_vector_transcript_hasher>(); + test_plonk_gates_matrix(); } } // namespace libsnark From 38d9067a559e7037e4a554231ea0d3bbda343ee3 Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Mon, 9 Jan 2023 08:58:29 +0000 Subject: [PATCH 6/9] plonk: added correction in the copy-constraints permutation, according to comments in https://github.com/clearmatics/libsnark/pull/97#discussion_r1055570124 --- .../plonk/tests/test_plonk.cpp | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index fb2e19f53..27c462cb8 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1134,13 +1134,21 @@ template void test_plonk_gates_matrix_transpose() // 1 0 0 0 0 // 0 0 -1 1 0 // -// and the permutation expressing the copy-constraints is (second -// line below): +// To compute the permutation of inputs and outputs that would reflect +// the copy-constraints, note the following. Of all the elements of +// the input/output vector V = (a1, a2, b1, b2, c1, c2), the circuit +// uses a1=x, a2=y, b2=a2=y and c2=a1=x, while b1 and c1 are not used +// (see the variables with non-zero coefficients in the PI and MUL +// equations above). If we number the elements of V from 1 to 6, the +// permutation reflecting the copy constraints should be (6, 4, 3, 2, +// 5, 1) i.e. 6 and 1 are permuted to reflect c2=a1; 4 and 2 are +// permuted to reflect b2=a2 and 3 and 5 remain in place to reflect +// that b1 and c1 are not used. Equivalently, the permutation is ("/" +// means "unused"): // -// (a1 a2 b1 b2 c1 c2) -// (c1 b2 b1 a2 c2 a1) -// -// reflecting the fact that a1=c1=c2=x and a2=b2=y +// ( x y / y / x) +// (a1 a2 b1 b2 c1 c2) -> (1 2 3 4 5 6) +// (c2 b2 b1 a2 c1 a1) -> (6 4 3 2 5 1) template void test_plonk_prepare_gates_matrix() { @@ -1171,7 +1179,7 @@ void test_plonk_prepare_gates_matrix() // that counting of indices starts from 1. TODO: implement a // general function to compute the wire permutation for any // circuit - std::vector wire_permutation{5, 4, 3, 2, 6, 1}; + std::vector wire_permutation{6, 4, 3, 2, 5, 1}; // maximum degree of the encoded monomials in the usrs size_t max_degree = PLONK_MAX_DEGREE; // Random hidden element kept secret (toxic waste) From 960506bbc6eb04babb6a251a19bcc17fd24dc11e Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Mon, 9 Jan 2023 11:19:08 +0000 Subject: [PATCH 7/9] plonk: added edits in comments --- libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index 27c462cb8..ac42d7942 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1096,9 +1096,8 @@ template void test_plonk_gates_matrix_transpose() } // We test the example circuit y^2 = x mod r where x is a public input -// and y is the witness. We prove that we know y such that x is the -// quadratic residue of y modulo the modulus r of the scalar field Fr -// of the given elliptic curve. For example x=49, y=7. +// and y is the witness. Thus the circuit shows that x is a quadratic +// residue in the field Fr. For example x=49, y=7. // // The circuit is represented by one public input (PI) and one // multiplication gate. According to the Plonk arithmetization rules, @@ -1157,8 +1156,8 @@ void test_plonk_prepare_gates_matrix() // 0 Arithmetization of test circuit y^2 = x mod r // The number of gates is 2: one public input (PI) gate and one - // multiplication gate. Tis number is also conveniently a power of - // 2 (needed for the FFT/iFFT) + // multiplication gate. This number is also conveniently a power of + // 2 (needed for the FFT/iFFT). const size_t num_gates = 2; // The tested circuit has 1 public input const size_t num_public_inputs = 1; @@ -1178,7 +1177,7 @@ void test_plonk_prepare_gates_matrix() // Hard-code the wire permutation for the tested circuit. Note // that counting of indices starts from 1. TODO: implement a // general function to compute the wire permutation for any - // circuit + // circuit. std::vector wire_permutation{6, 4, 3, 2, 5, 1}; // maximum degree of the encoded monomials in the usrs size_t max_degree = PLONK_MAX_DEGREE; From 7fb0106bb67656705b3452ad10e756fbafdf3068 Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Tue, 10 Jan 2023 19:00:02 +0000 Subject: [PATCH 8/9] plonk: added a wrapper for plonk_srs_derive_from_usrs_custom_PI_indices taking only the number of PIs as opposed to a vector of their indices (https://github.com/clearmatics/libsnark/pull/97#discussion_r1061359709); added utility function to compute teh values of the PIs from the witness to be passed to the verifier. --- libsnark/zk_proof_systems/plonk/srs.hpp | 13 ++++- libsnark/zk_proof_systems/plonk/srs.tcc | 19 ++++++- .../plonk/tests/test_plonk.cpp | 46 +++++++---------- libsnark/zk_proof_systems/plonk/utils.hpp | 27 +++++----- libsnark/zk_proof_systems/plonk/utils.tcc | 49 ++++++++++++++----- libsnark/zk_proof_systems/plonk/verifier.hpp | 2 + 6 files changed, 102 insertions(+), 54 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/srs.hpp b/libsnark/zk_proof_systems/plonk/srs.hpp index 07c804849..1bfdbfe54 100644 --- a/libsnark/zk_proof_systems/plonk/srs.hpp +++ b/libsnark/zk_proof_systems/plonk/srs.hpp @@ -144,12 +144,23 @@ template class srs /// and the list of public input (PI) indices. The (plain) SRS is a /// specialization of the USRS for one particular circuit template -srs plonk_srs_derive_from_usrs( +srs plonk_srs_derive_from_usrs_custom_PI_indices( const usrs &usrs, const std::vector>> gates_matrix, const std::vector wire_permutation, const std::vector PI_wire_indices); +/// A wrapper for plonk_srs_derive_from_usrs_custom_PI_indices that +/// assumes that the PI indices are located in the top +/// num_public_inputs rows of the gates matrix. num_public_inputs is +/// the number of public inputs. +template +srs plonk_srs_derive_from_usrs( + const usrs &usrs, + const std::vector>> gates_matrix, + const std::vector wire_permutation, + const size_t num_public_inputs); + /// A proving key for Plonk template class plonk_proving_key { diff --git a/libsnark/zk_proof_systems/plonk/srs.tcc b/libsnark/zk_proof_systems/plonk/srs.tcc index fb9c1e584..e20df5d74 100644 --- a/libsnark/zk_proof_systems/plonk/srs.tcc +++ b/libsnark/zk_proof_systems/plonk/srs.tcc @@ -105,7 +105,7 @@ usrs plonk_usrs_derive_from_secret( } template -srs plonk_srs_derive_from_usrs( +srs plonk_srs_derive_from_usrs_custom_PI_indices( const usrs &usrs, const std::vector>> gates_matrix, const std::vector wire_permutation, @@ -253,6 +253,23 @@ srs plonk_srs_derive_from_usrs( return srs; } +template +srs plonk_srs_derive_from_usrs( + const usrs &usrs, + const std::vector>> gates_matrix, + const std::vector wire_permutation, + const size_t num_public_inputs) +{ + std::vector PI_wire_indices; + // store the indices of the PIs + for (size_t i = 0; i < num_public_inputs; ++i) { + PI_wire_indices.push_back(i); + } + srs srs = plonk_srs_derive_from_usrs_custom_PI_indices( + usrs, gates_matrix, wire_permutation, PI_wire_indices); + return srs; +} + } // namespace libsnark #endif // LIBSNARK_ZK_PROOF_SYSTEMS_PLONK_SRS_TCC_ diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index ac42d7942..da4034cf5 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -406,7 +406,7 @@ template void test_plonk_prover_rounds() // prepare srs usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); - srs srs = plonk_srs_derive_from_usrs( + srs srs = plonk_srs_derive_from_usrs_custom_PI_indices( usrs, example.gates_matrix, example.wire_permutation, @@ -585,7 +585,7 @@ template void test_plonk_srs() // secret^2*G1, ... and secret times G2: 1*G2, secret^1*G2. usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); // --- SRS --- - srs srs = plonk_srs_derive_from_usrs( + srs srs = plonk_srs_derive_from_usrs_custom_PI_indices( usrs, example.gates_matrix, example.wire_permutation, @@ -632,7 +632,7 @@ template void test_plonk_prover() // Perepare srs. usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); - srs srs = plonk_srs_derive_from_usrs( + srs srs = plonk_srs_derive_from_usrs_custom_PI_indices( usrs, example.gates_matrix, example.wire_permutation, @@ -919,7 +919,7 @@ template void test_plonk_verifier_steps() // Prepare srs. usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); - srs srs = plonk_srs_derive_from_usrs( + srs srs = plonk_srs_derive_from_usrs_custom_PI_indices( usrs, example.gates_matrix, example.wire_permutation, @@ -1043,7 +1043,7 @@ template void test_plonk_verifier() // Prepare srs. usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); - srs srs = plonk_srs_derive_from_usrs( + srs srs = plonk_srs_derive_from_usrs_custom_PI_indices( usrs, example.gates_matrix, example.wire_permutation, @@ -1159,7 +1159,7 @@ void test_plonk_prepare_gates_matrix() // multiplication gate. This number is also conveniently a power of // 2 (needed for the FFT/iFFT). const size_t num_gates = 2; - // The tested circuit has 1 public input + // The example circuit has 1 public input const size_t num_public_inputs = 1; std::vector> gates_matrix = plonk_prepare_gates_matrix(num_public_inputs); @@ -1169,11 +1169,6 @@ void test_plonk_prepare_gates_matrix() gates_matrix.push_back(MUL_gate); ASSERT_EQ(gates_matrix.size(), 2); ASSERT_EQ(gates_matrix[0].size(), 5); - // Extract the PI indices from the gates matrix - std::vector PI_wire_indices = - plonk_public_input_indices_from_gates_matrix(gates_matrix); - ASSERT_EQ(PI_wire_indices.size(), 1); - ASSERT_EQ(PI_wire_indices[0], 0); // Hard-code the wire permutation for the tested circuit. Note // that counting of indices starts from 1. TODO: implement a // general function to compute the wire permutation for any @@ -1189,19 +1184,19 @@ void test_plonk_prepare_gates_matrix() // 1 Generate SRS - // compute usrs + // Compute usrs. usrs usrs = plonk_usrs_derive_from_secret(secret, max_degree); - // compute srs + // Compute srs. srs srs = plonk_srs_derive_from_usrs( - usrs, gates_matrix, wire_permutation, PI_wire_indices); + usrs, gates_matrix, wire_permutation, num_public_inputs); // 2 Compute proof - // initialize prover hasher + // Initialize prover hasher. transcript_hasher prover_hasher; - // prepare witness vector w = a+b+c + // Prepare witness vector w = a+b+c. std::vector witness{49, 7, 1, 7, 49, 49}; - // nine random blinding constants for the prover polynomials + // Nine random blinding constants for the prover polynomials. std::vector> blind_scalars; const size_t nscalars = 9; for (size_t i = 0; i < nscalars; ++i) { @@ -1216,19 +1211,14 @@ void test_plonk_prepare_gates_matrix() // 3 Verify proof - // initialize verifier hasher + // Initialize verifier hasher. transcript_hasher verifier_hasher; - // Prepare the list of PI values. for the example circuit - std::vector PI_value_list; - for (size_t i = 0; i < PI_wire_indices.size(); i++) { - Field PI_value = witness[PI_wire_indices[i]]; - PI_value_list.push_back(PI_value); - } - ASSERT_EQ(PI_value_list.size(), 1); - ASSERT_EQ(PI_value_list[0], Field(49)); - // initialize verifier + // Prepare the list of PI values for the example circuit. + std::vector PI_value_list = + plonk_public_input_values_from_length(witness, num_public_inputs); + // Initialize verifier. plonk_verifier verifier; - // verify proof + // Verify proof. bool b_valid_proof = verifier.verify_proof(proof, srs, PI_value_list, verifier_hasher); ASSERT_TRUE(b_valid_proof); diff --git a/libsnark/zk_proof_systems/plonk/utils.hpp b/libsnark/zk_proof_systems/plonk/utils.hpp index 6a10159aa..615026046 100644 --- a/libsnark/zk_proof_systems/plonk/utils.hpp +++ b/libsnark/zk_proof_systems/plonk/utils.hpp @@ -234,18 +234,23 @@ template std::vector>> plonk_prepare_gates_matrix( const size_t &num_public_inputs); -/// The function extracts the indices of the public inputs (PI) from the input -/// gates matrix under the convention that the i-th components of the selector -/// vectors q_L, q_R, q_O, q_M, q_C corresponding to the i-th selector vector -/// have the form -/// -/// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (1, 0, 0, 0, 0) -/// -/// The vector of PI indices is equal to the vector of indices of all rows in -/// the gates matrix that are equal to (1, 0, 0, 0, 0) +/// Extract the values corresponing to the public inputs from the +/// witness using the respective wire indices passed as input. Those +/// values are passed on to the verifier together with the proof. template -std::vector plonk_public_input_indices_from_gates_matrix( - const std::vector>> &gates_matrix); +std::vector> plonk_public_input_values_from_indices( + const std::vector> &witness, + const std::vector &PI_wire_indices); + +/// A wrapper for plonk_public_input_values_from_indices. Extracts the +/// values corresponing to the public inputs from the witness, assuming +/// that they are in the first num_public_inputs positions. In other +/// words extracts the values of the public inputs from the *length* of +/// the public input vector. +template +std::vector> plonk_public_input_values_from_length( + const std::vector> &witness, + const size_t &num_public_inputs); } // namespace libsnark diff --git a/libsnark/zk_proof_systems/plonk/utils.tcc b/libsnark/zk_proof_systems/plonk/utils.tcc index 38c26f8cc..ccda500ee 100644 --- a/libsnark/zk_proof_systems/plonk/utils.tcc +++ b/libsnark/zk_proof_systems/plonk/utils.tcc @@ -416,25 +416,48 @@ std::vector>> plonk_prepare_gates_matrix( return gates_matrix_init; } -// Examine the gates matrix row by row and store the indices of those rows that -// are equal to (1, 0, 0, 0, 0). These are the rows corresponding to public -// inputs. +// Extract the values corresponing to the public inputs from the +// witness using the respective wire indices passed as input. Those +// values are passed on to the verifier together with the proof. template -std::vector plonk_public_input_indices_from_gates_matrix( - const std::vector>> &gates_matrix) +std::vector> plonk_public_input_values_from_indices( + const std::vector> &witness, + const std::vector &PI_wire_indices) { + assert(PI_wire_indices.size() <= witness.size()); + + using FieldT = libff::Fr; + std::vector PI_value_list; + for (size_t i = 0; i < PI_wire_indices.size(); i++) { + assert(PI_wire_indices[i] < witness.size()); + FieldT PI_value = witness[PI_wire_indices[i]]; + PI_value_list.push_back(PI_value); + } + return PI_value_list; +} + +// A wrapper for plonk_public_input_values_from_indices. Extracts the +// values corresponing to the public inputs from the witness, assuming +// that they are in the first num_public_inputs positions. In other +// words extracts the values of the public inputs from the *length* of +// the public input vector. +template +std::vector> plonk_public_input_values_from_length( + const std::vector> &witness, const size_t &num_public_inputs) +{ + assert(num_public_inputs <= witness.size()); + using FieldT = libff::Fr; - // Vector of indices of wires corresponding to public inputs (PI) std::vector PI_wire_indices; - const std::vector PI_selector_vector{1, 0, 0, 0, 0}; - for (size_t i = 0; i < gates_matrix.size(); ++i) { - // store the index of the gates matrix row encoding a PI - if (gates_matrix[i] == PI_selector_vector) { - PI_wire_indices.push_back(i); - } + // store the indices of the PIs + for (size_t i = 0; i < num_public_inputs; ++i) { + PI_wire_indices.push_back(i); } - return PI_wire_indices; + std::vector PI_value_list = + plonk_public_input_values_from_indices(witness, PI_wire_indices); + return PI_value_list; } + } // namespace libsnark #endif // LIBSNARK_ZK_PROOF_SYSTEMS_PLONK_UTILS_TCC_ diff --git a/libsnark/zk_proof_systems/plonk/verifier.hpp b/libsnark/zk_proof_systems/plonk/verifier.hpp index 391917ed6..6cf1babaa 100644 --- a/libsnark/zk_proof_systems/plonk/verifier.hpp +++ b/libsnark/zk_proof_systems/plonk/verifier.hpp @@ -371,6 +371,8 @@ template class plonk_verifier /// \param[in] proof: SNARK proof produced by the prover /// \param[in] srs: structured reference string containing also /// circuit-specific information + /// \param[in] PI_value_list: list of values corresponding to + /// public inputs /// \param[in] transcript_hasher: hashes of the communication /// transcript after prover rounds 1,2,3,4,5. /// From f748170462c946718c68e50b4c966930eff8b1af Mon Sep 17 00:00:00 2001 From: Vesselin Velichkov Date: Fri, 13 Jan 2023 09:14:38 +0000 Subject: [PATCH 9/9] plonk: addressed latest PR #97 comments: function renaming, comments edits, minor code optimisations --- .../zk_proof_systems/plonk/tests/example.hpp | 10 +++++++ .../plonk/tests/test_plonk.cpp | 16 +++++------ libsnark/zk_proof_systems/plonk/utils.hpp | 4 +-- libsnark/zk_proof_systems/plonk/utils.tcc | 27 +++---------------- 4 files changed, 23 insertions(+), 34 deletions(-) diff --git a/libsnark/zk_proof_systems/plonk/tests/example.hpp b/libsnark/zk_proof_systems/plonk/tests/example.hpp index 6d22456f0..4ee3e5966 100644 --- a/libsnark/zk_proof_systems/plonk/tests/example.hpp +++ b/libsnark/zk_proof_systems/plonk/tests/example.hpp @@ -105,6 +105,16 @@ namespace libsnark /// q_O = [-1, -1, -1, 0, 0, 0, 0, 0] /// q_M = [ 1, 1, 0, 0, 0, -1, 0, 0] /// q_C = [ 0, 0, 0, -1, 0, 0, 0, 0] +/// +/// \attention The convention for selector vector values corresponding +/// to public inputs (PI) used here is different from the one used in +/// plonk_prepare_gates_matrix which is consistent with [GWC19], namely: +/// +/// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (1, 0, 0, 0, 0) +/// +/// For comparsion, here (i.e. in the example class) q_R[i]=1 and so +/// +/// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (0, 1, 0, 0, 0) class plonk_example { public: diff --git a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp index da4034cf5..ad19495ca 100644 --- a/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp +++ b/libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp @@ -1112,14 +1112,14 @@ template void test_plonk_gates_matrix_transpose() // Using the above representation, the PI and the multiplication // gates are given resp. as // -// PI: 1 a1 + 0 b1 + 0 c1 + 0 a1 b1 + 0 = 49 +// PI: 1 a1 + 0 b1 + 0 c1 + 0 a1 b1 + 0 ( + PI) = 0 // MUL: 0 a2 + 0 b2 + (-1) c2 + 1 a2 b2 + 0 = 0 // -// where +// where ("/" means "unused"): // // a = (a1, a2) = (x, y) -// b = (b1, b2) = (1, y) -// c = (c1, c2) = (x, x) +// b = (b1, b2) = (/, y) +// c = (c1, c2) = (/, x) // qL = (1, 0) // qR = (0, 0) // qO = (0, -1) @@ -1175,7 +1175,7 @@ void test_plonk_prepare_gates_matrix() // circuit. std::vector wire_permutation{6, 4, 3, 2, 5, 1}; // maximum degree of the encoded monomials in the usrs - size_t max_degree = PLONK_MAX_DEGREE; + const size_t max_degree = PLONK_MAX_DEGREE; // Random hidden element kept secret (toxic waste) Field secret = Field::random_element(); @@ -1215,7 +1215,7 @@ void test_plonk_prepare_gates_matrix() transcript_hasher verifier_hasher; // Prepare the list of PI values for the example circuit. std::vector PI_value_list = - plonk_public_input_values_from_length(witness, num_public_inputs); + plonk_public_input_values(witness, num_public_inputs); // Initialize verifier. plonk_verifier verifier; // Verify proof. @@ -1224,7 +1224,7 @@ void test_plonk_prepare_gates_matrix() ASSERT_TRUE(b_valid_proof); } -template void test_plonk_gates_matrix() +template void test_plonk_prepare_gates_matrix() { using Field = libff::Fr; // Test for 10 random values of num_public_inputs @@ -1405,7 +1405,7 @@ TEST(TestPlonk, BLS12_381) test_plonk_prepare_gates_matrix< libff::bls12_381_pp, bls12_381_test_vector_transcript_hasher>(); - test_plonk_gates_matrix(); + test_plonk_prepare_gates_matrix(); } } // namespace libsnark diff --git a/libsnark/zk_proof_systems/plonk/utils.hpp b/libsnark/zk_proof_systems/plonk/utils.hpp index 615026046..52a7bd1f6 100644 --- a/libsnark/zk_proof_systems/plonk/utils.hpp +++ b/libsnark/zk_proof_systems/plonk/utils.hpp @@ -245,10 +245,10 @@ std::vector> plonk_public_input_values_from_indices( /// A wrapper for plonk_public_input_values_from_indices. Extracts the /// values corresponing to the public inputs from the witness, assuming /// that they are in the first num_public_inputs positions. In other -/// words extracts the values of the public inputs from the *length* of +/// words extracts the values of the public inputs given the *length* of /// the public input vector. template -std::vector> plonk_public_input_values_from_length( +std::vector> plonk_public_input_values( const std::vector> &witness, const size_t &num_public_inputs); diff --git a/libsnark/zk_proof_systems/plonk/utils.tcc b/libsnark/zk_proof_systems/plonk/utils.tcc index ccda500ee..bb91002c6 100644 --- a/libsnark/zk_proof_systems/plonk/utils.tcc +++ b/libsnark/zk_proof_systems/plonk/utils.tcc @@ -395,14 +395,6 @@ bool plonk_are_valid_constants_k1_k2(const FieldT &k1, const FieldT &k2) // // Therefore the top N rows of the initialized gates matrix will have // the above form. See also Section 6 [GWC19]. -// -// \attention The convention for selector vector values corresponding -// to public inputs (PI) used in this function follows [GWC19] (as -// shown above) and is different from the one used in the example -// circuit for BLS12-381 (class example) where q_R[i]=1 rather than -// q_L[i]=1 i.e. -// -// (q_L[i], q_R[i], q_O[i], q_M[i], q_C[i]) = (0, 1, 0, 0, 0) template std::vector>> plonk_prepare_gates_matrix( const size_t &num_public_inputs) @@ -436,26 +428,13 @@ std::vector> plonk_public_input_values_from_indices( return PI_value_list; } -// A wrapper for plonk_public_input_values_from_indices. Extracts the -// values corresponing to the public inputs from the witness, assuming -// that they are in the first num_public_inputs positions. In other -// words extracts the values of the public inputs from the *length* of -// the public input vector. template -std::vector> plonk_public_input_values_from_length( +std::vector> plonk_public_input_values( const std::vector> &witness, const size_t &num_public_inputs) { assert(num_public_inputs <= witness.size()); - - using FieldT = libff::Fr; - std::vector PI_wire_indices; - // store the indices of the PIs - for (size_t i = 0; i < num_public_inputs; ++i) { - PI_wire_indices.push_back(i); - } - std::vector PI_value_list = - plonk_public_input_values_from_indices(witness, PI_wire_indices); - return PI_value_list; + return std::vector>( + witness.begin(), witness.begin() + num_public_inputs); } } // namespace libsnark