Skip to content

Commit

Permalink
linear combination phase of MPC for given circuit
Browse files Browse the repository at this point in the history
  • Loading branch information
dtebbs committed Aug 21, 2019
1 parent 6cef625 commit 6966aa7
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ endfunction(zeth_test)

zeth_test(test_simple test/simple_test.cpp)
zeth_test(test_powersoftau test/powersoftau_test.cpp)
zeth_test(test_mpc test/mpc_test.cpp)
zeth_test(test_addition test/packed_addition_test.cpp)
zeth_test(test_hex_to_field test/hex_to_field_test.cpp)
zeth_test(test_note test/note_test.cpp)
Expand Down
101 changes: 101 additions & 0 deletions src/snarks/groth16/mpc_utils.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "mpc_utils.hpp"
#include "multi_exp.hpp"
#include "evaluation_from_lagrange.hpp"
#include <libff/algebra/scalar_multiplication/multiexp.hpp>

using namespace libsnark;

Expand Down Expand Up @@ -28,4 +31,102 @@ mpc_layer1::mpc_layer1(
{
}


mpc_layer1
mpc_compute_linearcombination(
const powersoftau &pot,
const qap_instance<Fr> &qap)
{
libfqfft::evaluation_domain<Fr> &domain = *qap.domain;

// n = number of constraints in qap / degree of t().
const size_t n = qap.degree();
const size_t num_variables = qap.num_variables();

// Langrange polynomials, and therefore A, B, C will have order
// (n-1). T has order n. H.t() has order 2n-2, => H(.) has
// order:
//
// 2n-2 - n = n-2
//
// Therefore { t(x) . x^i } has 0 .. n-2 (n-1 of them), requiring
// requires powers of tau 0 .. 2.n-2 (2n-1 of them). We should
// have at least this many, by definition.

assert(pot.tau_powers_g1.size() >= 2*n - 1);

// n+1 coefficients of t

std::vector<Fr> t_coefficients(n + 1, Fr::zero());
qap.domain->add_poly_Z(Fr::one(), t_coefficients);

// Compute [ t(x) . x^i ]_1 for i = 0 .. n-2

libff::G1_vector<ppT> t_x_pow_i(n-1);
for (size_t i = 0 ; i < n - 1 ; ++i)
{
// Use { [x^i] , ... , [x^(i+order_L+1)] } with coefficients
// of t to compute t(x).x^i.
t_x_pow_i[i] = multi_exp<ppT, G1>(
pot.tau_powers_g1.begin() + i,
pot.tau_powers_g1.begin() + i + n + 1,
t_coefficients.begin(),
t_coefficients.end());
}

// Compute [ beta.A_i(x) + alpha.B_i(x) + C_i(x) ]_1
//
// For each i, get the Lagrange factors of A_i, B_i, C_i. For
// each j, if A, B or C has a non-zero factor, grab the Lagrange
// coefficients, evaluate at [t]_1, multiply by the factor and
// accumulate.

libff::G1_vector<ppT> A_i_g1(num_variables + 1);
libff::G1_vector<ppT> B_i_g1(num_variables + 1);
libff::G2_vector<ppT> B_i_g2(num_variables + 1);
libff::G1_vector<ppT> ABC_i_g1(num_variables + 1);

evaluation_from_lagrange<ppT, G1> tau_eval_g1(pot.tau_powers_g1, domain);
evaluation_from_lagrange<ppT, G2> tau_eval_g2(pot.tau_powers_g2, domain);
evaluation_from_lagrange<ppT, G1> alpha_tau_eval(pot.alpha_tau_powers_g1, domain);
evaluation_from_lagrange<ppT, G1> beta_tau_eval(pot.beta_tau_powers_g1, domain);

for (size_t i = 0 ; i < num_variables + 1 ; ++i)
{
// Compute [beta.A_i(x)], [alpha.B_i(x)] . [C_i(x)]

const std::map<size_t, Fr> &A_i_in_lagrange = qap.A_in_Lagrange_basis[i];
const std::map<size_t, Fr> &B_i_in_lagrange = qap.B_in_Lagrange_basis[i];
const std::map<size_t, Fr> &C_i_in_lagrange = qap.C_in_Lagrange_basis[i];

A_i_g1[i] = tau_eval_g1.evaluate_from_langrange_factors(A_i_in_lagrange);
B_i_g1[i] = tau_eval_g1.evaluate_from_langrange_factors(B_i_in_lagrange);
B_i_g2[i] = tau_eval_g2.evaluate_from_langrange_factors(B_i_in_lagrange);

G1 beta_A_at_t = beta_tau_eval.evaluate_from_langrange_factors(
A_i_in_lagrange);
G1 alpha_B_at_t = alpha_tau_eval.evaluate_from_langrange_factors(
B_i_in_lagrange);
G1 C_at_t = tau_eval_g1.evaluate_from_langrange_factors(
C_i_in_lagrange);

ABC_i_g1[i] = beta_A_at_t + alpha_B_at_t + C_at_t;
}

assert(num_variables + 1 == A_i_g1.size());
assert(num_variables + 1 == B_i_g1.size());
assert(num_variables + 1 == B_i_g2.size());
assert(num_variables + 1 == ABC_i_g1.size());

// TODO: Sparse B

return mpc_layer1(
std::move(t_x_pow_i),
std::move(A_i_g1),
std::move(B_i_g1),
std::move(B_i_g2),
std::move(ABC_i_g1));
}


} // namespace libzeth
9 changes: 9 additions & 0 deletions src/snarks/groth16/mpc_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ class mpc_layer1
libff::G1_vector<ppT> &&ABC_g1);
};


/// Given a circuit and a powersoftau with pre-computed lagrange
/// polynomials, perform the correct linear combination for the CRS
/// MPC.
mpc_layer1
mpc_compute_linearcombination(
const powersoftau &pot,
const libsnark::qap_instance<libff::Fr<ppT>> &qap);

} // namespace libzeth

#endif // __ZETH_SNARKS_GROTH16_MPC_UTILS_HPP__
119 changes: 119 additions & 0 deletions src/test/mpc_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@

#include "snarks/groth16/mpc_utils.hpp"
#include "snarks/groth16/multi_exp.hpp"
#include "snarks/groth16/evaluation_from_lagrange.hpp"
#include "test/simple_test.hpp"
#include "util.hpp"
#include <gtest/gtest.h>

using ppT = libff::default_ec_pp;
using Fr = libff::Fr<ppT>;
using G1 = libff::G1<ppT>;
using G2 = libff::G2<ppT>;
using namespace libsnark;
using namespace libzeth;

namespace
{

TEST(MPCTests, LinearCombination)
{
// Compute the small test qap first, in order to extract the
// degree.

const r1cs_constraint_system<Fr> constraint_system = ([]
{
protoboard<Fr> pb;
libzeth::test::simple_circuit<ppT>(pb);
r1cs_constraint_system<Fr> cs = pb.get_constraint_system();
cs.swap_AB_if_beneficial();
return cs;
})();
qap_instance<Fr> qap = r1cs_to_qap_instance_map(constraint_system);

// dummy powersoftau

Fr tau = Fr::random_element();
Fr alpha = Fr::random_element();
Fr beta = Fr::random_element();
const powersoftau pot = dummy_powersoftau_from_secrets(
tau, alpha, beta, qap.degree());

// linear combination

const mpc_layer1 layer1 = mpc_compute_linearcombination(
pot,
qap);

// Without knowlege of tau, not many checks can be performed
// beyond the ratio of terms in [ t(x) . x^i ]_1.

const size_t qap_n = qap.degree();
ASSERT_EQ(qap_n - 1, layer1.T_tau_powers_g1.size());
ASSERT_EQ(qap.num_variables() + 1, layer1.ABC_g1.size());

for (size_t i = 1 ; i < qap_n - 1 ; ++i)
{
ASSERT_TRUE(::same_ratio<ppT>(
layer1.T_tau_powers_g1[i-1],
layer1.T_tau_powers_g1[i],
pot.tau_powers_g2[0],
pot.tau_powers_g2[1]))
<< "i = " << std::to_string(i);
}

// Use knowledge of secrets to confirm values.
// Check that:
//
// [ domain.Z(tau) ]_1 = layer1.T_tau_powers_g1[0]
// [ beta . A_i(tau) + alpha . B_i(tau) + C_i(tau) ]_1 = layer1.ABC_g1[i]

{
const qap_instance_evaluation<Fr> qap_evaluation = ([&tau]
{
protoboard<Fr> pb;
libzeth::test::simple_circuit<ppT>(pb);
r1cs_constraint_system<Fr> constraint_system =
pb.get_constraint_system();
constraint_system.swap_AB_if_beneficial();
return r1cs_to_qap_instance_map_with_evaluation(
constraint_system, tau);
})();

ASSERT_EQ(
qap_evaluation.domain->compute_vanishing_polynomial(tau) * G1::one(),
layer1.T_tau_powers_g1[0]);

for (size_t i = 0 ; i < qap_evaluation.num_variables() + 1 ; ++i)
{
// At
ASSERT_EQ(qap_evaluation.At[i] * G1::one(), layer1.A_g1[i]);

// Bt
ASSERT_EQ(qap_evaluation.Bt[i] * G1::one(), layer1.B_g1[i]);
ASSERT_EQ(qap_evaluation.Bt[i] * G2::one(), layer1.B_g2[i]);

// ABCt
const Fr ABC_i =
beta * qap_evaluation.At[i] +
alpha * qap_evaluation.Bt[i] +
qap_evaluation.Ct[i];
ASSERT_EQ(ABC_i * G1::one(), layer1.ABC_g1[i]);
}
}
}

} // namespace

int main(int argc, char **argv)
{
// !!! WARNING: Do not forget to do this once for all tests !!!
ppT::init_public_params();

// Remove stdout noise from libff
libff::inhibit_profiling_counters = true;
libff::inhibit_profiling_info = true;

::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit 6966aa7

Please sign in to comment.