Skip to content

Commit

Permalink
Merge pull request #97 from clearmatics/89-plonk-helper-prepare-matrix
Browse files Browse the repository at this point in the history
Plonk: helper function to build gates matrix (depends on #70)
  • Loading branch information
dtebbs authored Jan 17, 2023
2 parents 8fe3f2d + f748170 commit f2a21f2
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 8 deletions.
13 changes: 12 additions & 1 deletion libsnark/zk_proof_systems/plonk/srs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,23 @@ template<typename ppT> class srs
/// and the list of public input (PI) indices. The (plain) SRS is a
/// specialization of the USRS for one particular circuit
template<typename ppT>
srs<ppT> plonk_srs_derive_from_usrs(
srs<ppT> plonk_srs_derive_from_usrs_custom_PI_indices(
const usrs<ppT> &usrs,
const std::vector<std::vector<libff::Fr<ppT>>> gates_matrix,
const std::vector<size_t> wire_permutation,
const std::vector<size_t> 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<typename ppT>
srs<ppT> plonk_srs_derive_from_usrs(
const usrs<ppT> &usrs,
const std::vector<std::vector<libff::Fr<ppT>>> gates_matrix,
const std::vector<size_t> wire_permutation,
const size_t num_public_inputs);

/// A proving key for Plonk
template<typename ppT> class plonk_proving_key
{
Expand Down
19 changes: 18 additions & 1 deletion libsnark/zk_proof_systems/plonk/srs.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ usrs<ppT> plonk_usrs_derive_from_secret(
}

template<typename ppT>
srs<ppT> plonk_srs_derive_from_usrs(
srs<ppT> plonk_srs_derive_from_usrs_custom_PI_indices(
const usrs<ppT> &usrs,
const std::vector<std::vector<libff::Fr<ppT>>> gates_matrix,
const std::vector<size_t> wire_permutation,
Expand Down Expand Up @@ -253,6 +253,23 @@ srs<ppT> plonk_srs_derive_from_usrs(
return srs;
}

template<typename ppT>
srs<ppT> plonk_srs_derive_from_usrs(
const usrs<ppT> &usrs,
const std::vector<std::vector<libff::Fr<ppT>>> gates_matrix,
const std::vector<size_t> wire_permutation,
const size_t num_public_inputs)
{
std::vector<size_t> 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<ppT> srs = plonk_srs_derive_from_usrs_custom_PI_indices<ppT>(
usrs, gates_matrix, wire_permutation, PI_wire_indices);
return srs;
}

} // namespace libsnark

#endif // LIBSNARK_ZK_PROOF_SYSTEMS_PLONK_SRS_TCC_
10 changes: 10 additions & 0 deletions libsnark/zk_proof_systems/plonk/tests/example.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
162 changes: 157 additions & 5 deletions libsnark/zk_proof_systems/plonk/tests/test_plonk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ template<typename ppT, class transcript_hasher> void test_plonk_prover_rounds()

// prepare srs
usrs<ppT> usrs = plonk_usrs_derive_from_secret<ppT>(secret, max_degree);
srs<ppT> srs = plonk_srs_derive_from_usrs<ppT>(
srs<ppT> srs = plonk_srs_derive_from_usrs_custom_PI_indices<ppT>(
usrs,
example.gates_matrix,
example.wire_permutation,
Expand Down Expand Up @@ -585,7 +585,7 @@ template<typename ppT> void test_plonk_srs()
// secret^2*G1, ... and secret times G2: 1*G2, secret^1*G2.
usrs<ppT> usrs = plonk_usrs_derive_from_secret<ppT>(secret, max_degree);
// --- SRS ---
srs<ppT> srs = plonk_srs_derive_from_usrs<ppT>(
srs<ppT> srs = plonk_srs_derive_from_usrs_custom_PI_indices<ppT>(
usrs,
example.gates_matrix,
example.wire_permutation,
Expand Down Expand Up @@ -632,7 +632,7 @@ template<typename ppT, class transcript_hasher> void test_plonk_prover()

// Perepare srs.
usrs<ppT> usrs = plonk_usrs_derive_from_secret<ppT>(secret, max_degree);
srs<ppT> srs = plonk_srs_derive_from_usrs<ppT>(
srs<ppT> srs = plonk_srs_derive_from_usrs_custom_PI_indices<ppT>(
usrs,
example.gates_matrix,
example.wire_permutation,
Expand Down Expand Up @@ -919,7 +919,7 @@ template<typename ppT, class transcript_hasher> void test_plonk_verifier_steps()

// Prepare srs.
usrs<ppT> usrs = plonk_usrs_derive_from_secret<ppT>(secret, max_degree);
srs<ppT> srs = plonk_srs_derive_from_usrs<ppT>(
srs<ppT> srs = plonk_srs_derive_from_usrs_custom_PI_indices<ppT>(
usrs,
example.gates_matrix,
example.wire_permutation,
Expand Down Expand Up @@ -1043,7 +1043,7 @@ template<typename ppT, class transcript_hasher> void test_plonk_verifier()

// Prepare srs.
usrs<ppT> usrs = plonk_usrs_derive_from_secret<ppT>(secret, max_degree);
srs<ppT> srs = plonk_srs_derive_from_usrs<ppT>(
srs<ppT> srs = plonk_srs_derive_from_usrs_custom_PI_indices<ppT>(
usrs,
example.gates_matrix,
example.wire_permutation,
Expand Down Expand Up @@ -1095,6 +1095,154 @@ template<typename ppT> 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 is a public input
// 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,
// 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 ( + PI) = 0
// MUL: 0 a2 + 0 b2 + (-1) c2 + 1 a2 b2 + 0 = 0
//
// where ("/" means "unused"):
//
// a = (a1, a2) = (x, y)
// b = (b1, b2) = (/, y)
// c = (c1, c2) = (/, 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
//
// 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"):
//
// ( 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<typename ppT, class transcript_hasher>
void test_plonk_prepare_gates_matrix()
{
using Field = libff::Fr<ppT>;

// 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. This number is also conveniently a power of
// 2 (needed for the FFT/iFFT).
const size_t num_gates = 2;
// The example circuit has 1 public input
const size_t num_public_inputs = 1;
std::vector<std::vector<Field>> gates_matrix =
plonk_prepare_gates_matrix<ppT>(num_public_inputs);
ASSERT_EQ(gates_matrix.size(), num_public_inputs);
// Add the multiplication gate
const std::vector<Field> 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);
// 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<size_t> wire_permutation{6, 4, 3, 2, 5, 1};
// maximum degree of the encoded monomials in the usrs
const size_t max_degree = PLONK_MAX_DEGREE;
// Random hidden element kept secret (toxic waste)
Field secret = Field::random_element();

std::shared_ptr<libfqfft::evaluation_domain<Field>> domain =
libfqfft::get_evaluation_domain<Field>(num_gates);

// 1 Generate SRS

// Compute usrs.
usrs<ppT> usrs = plonk_usrs_derive_from_secret<ppT>(secret, max_degree);
// Compute srs.
srs<ppT> srs = plonk_srs_derive_from_usrs<ppT>(
usrs, gates_matrix, wire_permutation, num_public_inputs);

// 2 Compute proof

// Initialize prover hasher.
transcript_hasher prover_hasher;
// Prepare witness vector w = a+b+c.
std::vector<Field> witness{49, 7, 1, 7, 49, 49};
// Nine random blinding constants for the prover polynomials.
std::vector<libff::Fr<ppT>> 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<ppT, transcript_hasher> prover;
// compute proof
plonk_proof<ppT> 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<Field> PI_value_list =
plonk_public_input_values<ppT>(witness, num_public_inputs);
// Initialize verifier.
plonk_verifier<ppT, transcript_hasher> verifier;
// Verify proof.
bool b_valid_proof =
verifier.verify_proof(proof, srs, PI_value_list, verifier_hasher);
ASSERT_TRUE(b_valid_proof);
}

template<typename ppT> void test_plonk_prepare_gates_matrix()
{
using Field = libff::Fr<ppT>;
// 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<std::vector<Field>> gates_matrix =
plonk_prepare_gates_matrix<ppT>(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<Field> 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<typename ppT> void test_plonk_constants_k1_k2()
{
Expand Down Expand Up @@ -1254,6 +1402,10 @@ TEST(TestPlonk, BLS12_381)
libff::bls12_381_pp,
bls12_381_test_vector_transcript_hasher>();
test_plonk_gates_matrix_transpose<libff::bls12_381_pp>();
test_plonk_prepare_gates_matrix<
libff::bls12_381_pp,
bls12_381_test_vector_transcript_hasher>();
test_plonk_prepare_gates_matrix<libff::bls12_381_pp>();
}

} // namespace libsnark
25 changes: 25 additions & 0 deletions libsnark/zk_proof_systems/plonk/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,31 @@ void plonk_generate_random_constants_k1_k2(
template<typename FieldT>
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<typename ppT>
std::vector<std::vector<libff::Fr<ppT>>> plonk_prepare_gates_matrix(
const size_t &num_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<typename ppT>
std::vector<libff::Fr<ppT>> plonk_public_input_values_from_indices(
const std::vector<libff::Fr<ppT>> &witness,
const std::vector<size_t> &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 given the *length* of
/// the public input vector.
template<typename ppT>
std::vector<libff::Fr<ppT>> plonk_public_input_values(
const std::vector<libff::Fr<ppT>> &witness,
const size_t &num_public_inputs);

} // namespace libsnark

#include "libsnark/zk_proof_systems/plonk/utils.tcc"
Expand Down
56 changes: 55 additions & 1 deletion libsnark/zk_proof_systems/plonk/utils.tcc
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ std::vector<std::vector<FieldT>> plonk_gates_matrix_transpose(
std::vector<std::vector<FieldT>> gates_matrix_transpose(
nrows_transpose, std::vector<FieldT>(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];
}
}
Expand Down Expand Up @@ -383,6 +383,60 @@ 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_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_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].
template<typename ppT>
std::vector<std::vector<libff::Fr<ppT>>> plonk_prepare_gates_matrix(
const size_t &num_public_inputs)
{
using FieldT = libff::Fr<ppT>;
std::vector<std::vector<FieldT>> gates_matrix_init;
const std::vector<FieldT> 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;
}

// 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<typename ppT>
std::vector<libff::Fr<ppT>> plonk_public_input_values_from_indices(
const std::vector<libff::Fr<ppT>> &witness,
const std::vector<size_t> &PI_wire_indices)
{
assert(PI_wire_indices.size() <= witness.size());

using FieldT = libff::Fr<ppT>;
std::vector<FieldT> 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;
}

template<typename ppT>
std::vector<libff::Fr<ppT>> plonk_public_input_values(
const std::vector<libff::Fr<ppT>> &witness, const size_t &num_public_inputs)
{
assert(num_public_inputs <= witness.size());
return std::vector<libff::Fr<ppT>>(
witness.begin(), witness.begin() + num_public_inputs);
}

} // namespace libsnark

#endif // LIBSNARK_ZK_PROOF_SYSTEMS_PLONK_UTILS_TCC_
2 changes: 2 additions & 0 deletions libsnark/zk_proof_systems/plonk/verifier.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ template<typename ppT, class transcript_hasher> 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.
///
Expand Down

0 comments on commit f2a21f2

Please sign in to comment.