1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
3 // SPDX-License-Identifier: LGPL-3.0+
5 #ifndef __ZETH_MPC_GROTH16_PHASE2_TCC__
6 #define __ZETH_MPC_GROTH16_PHASE2_TCC__
8 #include "libzeth/core/chacha_rng.hpp"
9 #include "libzeth/core/hash_stream.hpp"
10 #include "libzeth/core/utils.hpp"
11 #include "libzeth/mpc/groth16/mpc_utils.hpp"
12 #include "libzeth/mpc/groth16/phase2.hpp"
13 #include "libzeth/mpc/groth16/powersoftau_utils.hpp"
15 #include <libff/common/rng.hpp>
20 template<typename ppT>
21 srs_mpc_phase2_accumulator<ppT>::srs_mpc_phase2_accumulator(
22 const mpc_hash_t cs_hash,
23 const libff::G1<ppT> &delta_g1,
24 const libff::G2<ppT> &delta_g2,
25 libff::G1_vector<ppT> &&H_g1,
26 libff::G1_vector<ppT> &&L_g1)
27 : delta_g1(delta_g1), delta_g2(delta_g2), H_g1(H_g1), L_g1(L_g1)
29 memcpy(this->cs_hash, cs_hash, sizeof(mpc_hash_t));
32 template<typename ppT>
33 bool srs_mpc_phase2_accumulator<ppT>::operator==(
34 const srs_mpc_phase2_accumulator<ppT> &other) const
36 return !memcmp(cs_hash, other.cs_hash, sizeof(mpc_hash_t)) &&
37 (delta_g1 == other.delta_g1) && (delta_g2 == other.delta_g2) &&
38 (H_g1 == other.H_g1) && (L_g1 == other.L_g1);
41 template<typename ppT>
42 bool srs_mpc_phase2_accumulator<ppT>::is_well_formed() const
44 return delta_g1.is_well_formed() && delta_g2.is_well_formed() &&
45 container_is_well_formed(H_g1) && container_is_well_formed(L_g1);
48 template<typename ppT>
49 void srs_mpc_phase2_accumulator<ppT>::write(std::ostream &out) const
51 using G1 = libff::G1<ppT>;
52 check_well_formed(*this, "mpc_layer2 (write)");
54 // Write cs_hash and sizes first.
56 const size_t H_size = H_g1.size();
57 const size_t L_size = L_g1.size();
58 out.write((const char *)cs_hash, sizeof(mpc_hash_t));
59 out.write((const char *)&H_size, sizeof(H_size));
60 out.write((const char *)&L_size, sizeof(L_size));
64 for (const G1 h : H_g1) {
67 for (const G1 l : L_g1) {
72 template<typename ppT>
73 void srs_mpc_phase2_accumulator<ppT>::write_compressed(std::ostream &out) const
75 using G1 = libff::G1<ppT>;
76 check_well_formed(*this, "mpc_layer2 (write)");
78 // Write cs_hash and sizes first.
80 const size_t H_size = H_g1.size();
81 const size_t L_size = L_g1.size();
82 out.write((const char *)cs_hash, sizeof(mpc_hash_t));
83 out.write((const char *)&H_size, sizeof(H_size));
84 out.write((const char *)&L_size, sizeof(L_size));
86 delta_g1.write_compressed(out);
87 delta_g2.write_compressed(out);
88 for (const G1 &h : H_g1) {
89 h.write_compressed(out);
91 for (const G1 &l : L_g1) {
92 l.write_compressed(out);
96 template<typename ppT>
97 srs_mpc_phase2_accumulator<ppT> srs_mpc_phase2_accumulator<ppT>::read(
100 using G1 = libff::G1<ppT>;
106 in.read((char *)cs_hash, sizeof(mpc_hash_t));
107 in.read((char *)&H_size, sizeof(H_size));
108 in.read((char *)&L_size, sizeof(L_size));
110 libff::G1<ppT> delta_g1;
111 libff::G2<ppT> delta_g2;
112 libff::G1_vector<ppT> H_g1(H_size);
113 libff::G1_vector<ppT> L_g1(L_size);
124 srs_mpc_phase2_accumulator<ppT> accum(
125 cs_hash, delta_g1, delta_g2, std::move(H_g1), std::move(L_g1));
126 check_well_formed(accum, "phase2_accumulator (read)");
130 template<typename ppT>
131 srs_mpc_phase2_accumulator<ppT> srs_mpc_phase2_accumulator<
132 ppT>::read_compressed(std::istream &in)
134 using G1 = libff::G1<ppT>;
135 using G2 = libff::G2<ppT>;
140 in.read((char *)cs_hash, sizeof(mpc_hash_t));
141 in.read((char *)&H_size, sizeof(H_size));
142 in.read((char *)&L_size, sizeof(L_size));
145 G1::read_compressed(in, delta_g1);
147 G2::read_compressed(in, delta_g2);
149 libff::G1_vector<ppT> H_g1(H_size);
151 G1::read_compressed(in, h);
154 libff::G1_vector<ppT> L_g1(L_size);
156 G1::read_compressed(in, l);
159 srs_mpc_phase2_accumulator<ppT> l2(
160 cs_hash, delta_g1, delta_g2, std::move(H_g1), std::move(L_g1));
161 check_well_formed(l2, "mpc_layer2 (read)");
165 template<typename ppT>
166 srs_mpc_phase2_publickey<ppT>::srs_mpc_phase2_publickey(
167 const mpc_hash_t transcript_digest,
168 const libff::G1<ppT> &new_delta_g1,
169 const libff::G1<ppT> &s_g1,
170 const libff::G1<ppT> &s_delta_j_g1,
171 const libff::G2<ppT> &r_delta_j_g2)
172 : new_delta_g1(new_delta_g1)
174 , s_delta_j_g1(s_delta_j_g1)
175 , r_delta_j_g2(r_delta_j_g2)
177 memcpy(this->transcript_digest, transcript_digest, sizeof(mpc_hash_t));
180 template<typename ppT>
181 bool srs_mpc_phase2_publickey<ppT>::operator==(
182 const srs_mpc_phase2_publickey<ppT> &other) const
184 const bool transcript_matches =
185 !memcmp(transcript_digest, other.transcript_digest, sizeof(mpc_hash_t));
186 return transcript_matches && (new_delta_g1 == other.new_delta_g1) &&
187 (s_g1 == other.s_g1) && (s_delta_j_g1 == other.s_delta_j_g1) &&
188 (r_delta_j_g2 == other.r_delta_j_g2);
191 template<typename ppT>
192 bool srs_mpc_phase2_publickey<ppT>::is_well_formed() const
194 return new_delta_g1.is_well_formed() && s_g1.is_well_formed() &&
195 s_delta_j_g1.is_well_formed() && r_delta_j_g2.is_well_formed();
198 template<typename ppT>
199 void srs_mpc_phase2_publickey<ppT>::write(std::ostream &out) const
201 check_well_formed(*this, "srs_mpc_phase2_publickey");
202 out.write((const char *)transcript_digest, sizeof(mpc_hash_t));
203 out << new_delta_g1 << s_g1 << s_delta_j_g1 << r_delta_j_g2;
206 template<typename ppT>
207 srs_mpc_phase2_publickey<ppT> srs_mpc_phase2_publickey<ppT>::read(
210 mpc_hash_t transcript_digest;
211 libff::G1<ppT> new_delta_g1;
213 libff::G1<ppT> s_delta_j_g1;
214 libff::G2<ppT> r_delta_j_g2;
215 in.read((char *)transcript_digest, sizeof(mpc_hash_t));
216 in >> new_delta_g1 >> s_g1 >> s_delta_j_g1 >> r_delta_j_g2;
217 srs_mpc_phase2_publickey pubkey(
218 transcript_digest, new_delta_g1, s_g1, s_delta_j_g1, r_delta_j_g2);
219 check_well_formed(pubkey, "srs_mpc_phase2_publickey::read");
223 template<typename ppT>
224 void srs_mpc_phase2_publickey<ppT>::compute_digest(mpc_hash_t out_digest) const
228 hs.get_hash(out_digest);
231 template<typename ppT>
232 srs_mpc_phase2_challenge<ppT>::srs_mpc_phase2_challenge(
233 const mpc_hash_t transcript_digest,
234 srs_mpc_phase2_accumulator<ppT> &&accumulator)
235 : transcript_digest(), accumulator(accumulator)
237 memcpy(this->transcript_digest, transcript_digest, sizeof(mpc_hash_t));
240 template<typename ppT>
241 bool srs_mpc_phase2_challenge<ppT>::operator==(
242 const srs_mpc_phase2_challenge<ppT> &other) const
244 const bool digest_match =
245 !memcmp(transcript_digest, other.transcript_digest, sizeof(mpc_hash_t));
246 return digest_match && (accumulator == other.accumulator);
249 template<typename ppT>
250 bool srs_mpc_phase2_challenge<ppT>::is_well_formed() const
252 return accumulator.is_well_formed();
255 template<typename ppT>
256 void srs_mpc_phase2_challenge<ppT>::write(std::ostream &out) const
258 check_well_formed(*this, "srs_mpc_phase2_challenge::write");
259 out.write((const char *)transcript_digest, sizeof(mpc_hash_t));
260 accumulator.write(out);
263 template<typename ppT>
264 srs_mpc_phase2_challenge<ppT> srs_mpc_phase2_challenge<ppT>::read(
267 mpc_hash_t last_response_digest;
268 in.read((char *)last_response_digest, sizeof(mpc_hash_t));
269 srs_mpc_phase2_accumulator<ppT> accum =
270 srs_mpc_phase2_accumulator<ppT>::read(in);
271 srs_mpc_phase2_challenge<ppT> challenge(
272 last_response_digest, std::move(accum));
273 check_well_formed(challenge, "srs_mpc_phase2_challenge::read");
277 template<typename ppT>
278 srs_mpc_phase2_response<ppT>::srs_mpc_phase2_response(
279 srs_mpc_phase2_accumulator<ppT> &&new_accumulator,
280 srs_mpc_phase2_publickey<ppT> &&publickey)
281 : new_accumulator(new_accumulator), publickey(publickey)
285 template<typename ppT>
286 bool srs_mpc_phase2_response<ppT>::operator==(
287 const srs_mpc_phase2_response<ppT> &other) const
289 return (new_accumulator == other.new_accumulator) &&
290 (publickey == other.publickey);
293 template<typename ppT> bool srs_mpc_phase2_response<ppT>::is_well_formed() const
295 return new_accumulator.is_well_formed() && publickey.is_well_formed();
298 template<typename ppT>
299 void srs_mpc_phase2_response<ppT>::write(std::ostream &out) const
301 check_well_formed(*this, "srs_mpc_phase2_response::write");
302 new_accumulator.write_compressed(out);
303 publickey.write(out);
306 template<typename ppT>
307 srs_mpc_phase2_response<ppT> srs_mpc_phase2_response<ppT>::read(
310 srs_mpc_phase2_accumulator<ppT> accumulator =
311 srs_mpc_phase2_accumulator<ppT>::read_compressed(in);
312 srs_mpc_phase2_publickey<ppT> pubkey =
313 srs_mpc_phase2_publickey<ppT>::read(in);
315 srs_mpc_phase2_response<ppT> response(
316 std::move(accumulator), std::move(pubkey));
317 check_well_formed(response, "srs_mpc_phase2_response::read");
321 template<mp_size_t n, const libff::bigint<n> &modulus>
322 void srs_mpc_digest_to_fp(
323 const mpc_hash_t transcript_digest, libff::Fp_model<n, modulus> &out_fr)
325 // Fill a U512 with random data and compute the representation mod m.
326 libff::bigint<2 * n> random;
327 libff::bigint<n + 1> _quotient;
329 chacha_rng rng(transcript_digest, sizeof(mpc_hash_t));
330 rng.random(random.data, sizeof(random));
333 out_fr.mont_repr.data,
341 /// Deterministically choose a value $r$ in G2, given some $s$ and $s_delta_j$
342 /// in G1, and the current transcript digest.
343 template<typename ppT>
344 libff::G2<ppT> srs_mpc_digest_to_g2(const mpc_hash_t transcript_digest)
347 srs_mpc_digest_to_fp(transcript_digest, fr);
348 return fr * libff::G2<ppT>::one();
351 template<typename ppT>
352 srs_mpc_phase2_accumulator<ppT> srs_mpc_phase2_begin(
353 const mpc_hash_t cs_hash,
354 const srs_mpc_layer_L1<ppT> &layer_L1,
357 // In layer_L1 output, there should be num_variables+1 entries in
360 // - The first 1+num_inputs entries are used directly in the
363 // - The remaining num_variables-num_inputs entries will be
364 // divided by delta to create layer2.
366 return srs_mpc_phase2_accumulator<ppT>(
368 libff::G1<ppT>::one(),
369 libff::G2<ppT>::one(),
370 libff::G1_vector<ppT>(layer_L1.T_tau_powers_g1),
371 libff::G1_vector<ppT>(
372 layer_L1.ABC_g1.begin() + num_inputs + 1, layer_L1.ABC_g1.end()));
375 template<typename ppT>
376 srs_mpc_phase2_publickey<ppT> srs_mpc_phase2_compute_public_key(
377 const mpc_hash_t transcript_digest,
378 const libff::G1<ppT> &last_delta,
379 const libff::Fr<ppT> &delta_j)
381 libff::enter_block("call to srs_mpc_phase2_compute_public_key");
382 const libff::G1<ppT> new_delta_g1 = delta_j * last_delta;
383 const libff::G1<ppT> s_g1 = libff::G1<ppT>::random_element();
384 const libff::G1<ppT> s_delta_j_g1 = delta_j * s_g1;
385 const libff::G2<ppT> r_g2 = srs_mpc_digest_to_g2<ppT>(transcript_digest);
386 const libff::G2<ppT> r_delta_j_g2 = delta_j * r_g2;
387 libff::leave_block("call to srs_mpc_phase2_compute_public_key");
389 return srs_mpc_phase2_publickey<ppT>(
390 transcript_digest, new_delta_g1, s_g1, s_delta_j_g1, r_delta_j_g2);
393 template<typename ppT>
394 bool srs_mpc_phase2_verify_publickey(
395 const libff::G1<ppT> last_delta_g1,
396 const srs_mpc_phase2_publickey<ppT> &publickey,
397 libff::G2<ppT> &out_r_g2)
399 const libff::G1<ppT> &s_g1 = publickey.s_g1;
400 const libff::G1<ppT> &s_delta_j_g1 = publickey.s_delta_j_g1;
401 out_r_g2 = srs_mpc_digest_to_g2<ppT>(publickey.transcript_digest);
402 const libff::G2<ppT> &r_delta_j_g2 = publickey.r_delta_j_g2;
403 const libff::G1<ppT> &new_delta_g1 = publickey.new_delta_g1;
405 // Step 1 (from [BoweGM17]). Check the proof of knowledge.
406 if (!same_ratio<ppT>(s_g1, s_delta_j_g1, out_r_g2, r_delta_j_g2)) {
410 // Step 2. Check new_delta_g1 is correct.
411 if (!same_ratio<ppT>(last_delta_g1, new_delta_g1, out_r_g2, r_delta_j_g2)) {
418 template<typename ppT>
419 bool srs_mpc_phase2_verify_publickey(
420 const libff::G1<ppT> last_delta_g1,
421 const srs_mpc_phase2_publickey<ppT> &publickey)
424 return srs_mpc_phase2_verify_publickey<ppT>(last_delta_g1, publickey, r_g2);
427 template<typename ppT>
428 srs_mpc_phase2_accumulator<ppT> srs_mpc_phase2_update_accumulator(
429 const srs_mpc_phase2_accumulator<ppT> &last_accum,
430 const libff::Fr<ppT> &delta_j)
432 libff::enter_block("call to srs_mpc_phase2_update_accumulator");
433 const libff::Fr<ppT> delta_j_inverse = delta_j.inverse();
435 // Step 3 (from [BoweGM17]): Update accumulated $\delta$
436 const libff::G1<ppT> new_delta_g1 = delta_j * last_accum.delta_g1;
437 const libff::G2<ppT> new_delta_g2 = delta_j * last_accum.delta_g2;
439 // Step 3: Update $L_i$ by dividing by $\delta$ ('K' in the paper, but we
440 // use L here to be consistent with the final keypair in libsnark).
441 libff::enter_block("updating L_g1");
442 const size_t num_L_elements = last_accum.L_g1.size();
443 if (!libff::inhibit_profiling_info) {
444 libff::print_indent();
445 printf("%zu entries\n", num_L_elements);
447 libff::G1_vector<ppT> L_g1(num_L_elements);
449 #pragma omp parallel for
451 for (size_t i = 0; i < num_L_elements; ++i) {
452 L_g1[i] = delta_j_inverse * last_accum.L_g1[i];
455 libff::leave_block("updating L_g1");
457 // Step 5: Update $H_i$ by dividing by our contribution.
458 libff::enter_block("updating H_g1");
459 const size_t H_size = last_accum.H_g1.size();
460 if (!libff::inhibit_profiling_info) {
461 libff::print_indent();
462 printf("%zu entries\n", H_size);
464 libff::G1_vector<ppT> H_g1(H_size);
466 #pragma omp parallel for
468 for (size_t i = 0; i < H_size; ++i) {
469 H_g1[i] = delta_j_inverse * last_accum.H_g1[i];
471 libff::leave_block("updating H_g1");
473 libff::leave_block("call to srs_mpc_phase2_update_accumulator");
475 return srs_mpc_phase2_accumulator<ppT>(
483 template<typename ppT>
484 bool srs_mpc_phase2_update_is_consistent(
485 const srs_mpc_phase2_accumulator<ppT> &last,
486 const srs_mpc_phase2_accumulator<ppT> &updated)
488 libff::enter_block("call to srs_mpc_phase2_update_is_consistent");
490 // Check basic compatibility between 'last' and 'updated'
491 if (memcmp(last.cs_hash, updated.cs_hash, sizeof(mpc_hash_t)) ||
492 last.H_g1.size() != updated.H_g1.size() ||
493 last.L_g1.size() != updated.L_g1.size()) {
497 const libff::G2<ppT> &old_delta_g2 = last.delta_g2;
498 const libff::G2<ppT> &new_delta_g2 = updated.delta_g2;
500 // Check that, that the delta_g1 and delta_2 ratios match.
501 if (!same_ratio<ppT>(
502 last.delta_g1, updated.delta_g1, old_delta_g2, new_delta_g2)) {
506 // Step 3. Check that the updates to L values are consistent. Each
507 // entry should have been divided by $\delta_j$, so SameRatio((updated,
508 // last), (old_delta_g2, new_delta_g2)) should hold.
509 if (!same_ratio_vectors<ppT>(
510 updated.L_g1, last.L_g1, old_delta_g2, new_delta_g2)) {
514 // Step 4. Similar consistency checks for H
515 if (!same_ratio_vectors<ppT>(
516 updated.H_g1, last.H_g1, old_delta_g2, new_delta_g2)) {
520 libff::leave_block("call to srs_mpc_phase2_update_is_consistent");
525 template<typename ppT>
526 bool srs_mpc_phase2_verify_update(
527 const srs_mpc_phase2_accumulator<ppT> &last,
528 const srs_mpc_phase2_accumulator<ppT> &updated,
529 const srs_mpc_phase2_publickey<ppT> &publickey)
531 // Step 1 and 2 (from [BoweGM17]). Check the proof-of-knowledge in the
532 // public key, and the updated delta value. Obtain r_g2 to avoid
535 if (!srs_mpc_phase2_verify_publickey(last.delta_g1, publickey, r_g2)) {
539 if (publickey.new_delta_g1 != updated.delta_g1) {
543 return srs_mpc_phase2_update_is_consistent(last, updated);
546 template<typename ppT>
547 srs_mpc_phase2_challenge<ppT> srs_mpc_phase2_initial_challenge(
548 srs_mpc_phase2_accumulator<ppT> &&accumulator)
550 mpc_hash_t initial_transcript_digest;
552 initial_transcript_digest, accumulator.cs_hash, sizeof(mpc_hash_t));
553 return srs_mpc_phase2_challenge<ppT>(
554 initial_transcript_digest, std::move(accumulator));
557 template<typename ppT>
558 srs_mpc_phase2_response<ppT> srs_mpc_phase2_compute_response(
559 const srs_mpc_phase2_challenge<ppT> &challenge,
560 const libff::Fr<ppT> &delta_j)
562 libff::enter_block("computing contribution public key");
563 srs_mpc_phase2_publickey<ppT> pubkey =
564 srs_mpc_phase2_compute_public_key<ppT>(
565 challenge.transcript_digest,
566 challenge.accumulator.delta_g1,
568 libff::leave_block("computing contribution public key");
570 srs_mpc_phase2_accumulator<ppT> new_accum =
571 srs_mpc_phase2_update_accumulator(challenge.accumulator, delta_j);
573 return srs_mpc_phase2_response<ppT>(
574 std::move(new_accum), std::move(pubkey));
577 template<typename ppT>
578 bool srs_mpc_phase2_verify_response(
579 const srs_mpc_phase2_challenge<ppT> &challenge,
580 const srs_mpc_phase2_response<ppT> &response)
582 // Ensure that response.pubkey corresponds to challenge.transcript_digest
583 const bool digest_match = !memcmp(
584 challenge.transcript_digest,
585 response.publickey.transcript_digest,
591 return srs_mpc_phase2_verify_update(
592 challenge.accumulator, response.new_accumulator, response.publickey);
595 template<typename ppT>
596 srs_mpc_phase2_challenge<ppT> srs_mpc_phase2_compute_challenge(
597 srs_mpc_phase2_response<ppT> &&response)
599 mpc_hash_t new_transcript_digest;
600 response.publickey.compute_digest(new_transcript_digest);
601 return srs_mpc_phase2_challenge<ppT>(
602 new_transcript_digest, std::move(response.new_accumulator));
605 template<typename ppT, bool enable_contribution_check>
606 bool srs_mpc_phase2_verify_transcript(
607 const mpc_hash_t initial_transcript_digest,
608 const libff::G1<ppT> &initial_delta,
609 const mpc_hash_t check_for_contribution,
610 std::istream &transcript_stream,
611 libff::G1<ppT> &out_final_delta,
612 mpc_hash_t out_final_transcript_digest,
613 bool &out_contribution_found)
616 memcpy(digest, initial_transcript_digest, sizeof(mpc_hash_t));
617 libff::G1<ppT> delta = initial_delta;
619 bool contribution_found = false;
620 while (EOF != transcript_stream.peek()) {
621 const srs_mpc_phase2_publickey<ppT> publickey =
622 srs_mpc_phase2_publickey<ppT>::read(transcript_stream);
624 const bool digests_match =
625 !memcmp(digest, publickey.transcript_digest, sizeof(mpc_hash_t));
626 if (!digests_match) {
630 publickey.compute_digest(digest);
631 if (enable_contribution_check && !contribution_found &&
632 0 == memcmp(digest, check_for_contribution, sizeof(mpc_hash_t))) {
633 contribution_found = true;
636 if (!srs_mpc_phase2_verify_publickey(delta, publickey)) {
640 // Contribution is valid. Update state and read next publickey.
641 delta = publickey.new_delta_g1;
644 out_final_delta = delta;
645 memcpy(out_final_transcript_digest, digest, sizeof(mpc_hash_t));
646 if (enable_contribution_check) {
647 out_contribution_found = contribution_found;
653 template<typename ppT>
654 bool srs_mpc_phase2_verify_transcript(
655 const mpc_hash_t initial_transcript_digest,
656 const libff::G1<ppT> &initial_delta,
657 std::istream &transcript_stream,
658 libff::G1<ppT> &out_final_delta,
659 mpc_hash_t out_final_transcript_digest)
661 const mpc_hash_t dummy_check_for_contribution{};
662 bool dummy_out_contribution_found;
663 return srs_mpc_phase2_verify_transcript<ppT, false>(
664 initial_transcript_digest,
666 dummy_check_for_contribution,
669 out_final_transcript_digest,
670 dummy_out_contribution_found);
673 template<typename ppT>
674 srs_mpc_phase2_challenge<ppT> srs_mpc_dummy_phase2(
675 const srs_mpc_layer_L1<ppT> &layer1,
676 const libff::Fr<ppT> &delta,
677 const size_t num_inputs)
679 // Start with a blank challenge and simulate one contribution of the MPC
681 mpc_hash_t init_hash;
683 mpc_compute_hash(init_hash, empty, 0);
684 srs_mpc_phase2_challenge<ppT> challenge_0 =
685 srs_mpc_phase2_initial_challenge(
686 srs_mpc_phase2_begin(init_hash, layer1, num_inputs));
687 srs_mpc_phase2_response<ppT> response_1 =
688 srs_mpc_phase2_compute_response(challenge_0, delta);
689 return srs_mpc_phase2_compute_challenge(std::move(response_1));
692 template<typename ppT, libff::multi_exp_base_form BaseForm>
693 libsnark::r1cs_gg_ppzksnark_keypair<ppT> mpc_create_key_pair(
694 srs_powersoftau<ppT> &&pot,
695 srs_mpc_layer_L1<ppT> &&layer1,
696 srs_mpc_phase2_accumulator<ppT> &&layer2,
697 libsnark::r1cs_constraint_system<libff::Fr<ppT>> &&cs,
698 const libsnark::qap_instance<libff::Fr<ppT>> &qap)
700 using G1 = libff::G1<ppT>;
701 using G2 = libff::G2<ppT>;
703 const size_t n = qap.degree();
704 const size_t num_variables = qap.num_variables();
705 const size_t num_inputs = qap.num_inputs();
707 // Some sanity checks.
708 // layer1.A, B, C, ABC should all have num_variables+1 entries.
709 // layer2.H should have n-1 entries.
710 // layer2.L should have num_variables-num_inputs entries.
711 // pot should have degree >= n
712 if (num_variables + 1 != layer1.A_g1.size()) {
713 throw std::invalid_argument(
714 "expected " + std::to_string(num_variables + 1) +
715 " A entries, but saw " + std::to_string(layer1.A_g1.size()));
717 if (num_variables + 1 != layer1.B_g1.size()) {
718 throw std::invalid_argument(
719 "expected " + std::to_string(num_variables + 1) +
720 " B_g1 entries, but saw " + std::to_string(layer1.B_g1.size()));
722 if (num_variables + 1 != layer1.B_g2.size()) {
723 throw std::invalid_argument(
724 "expected " + std::to_string(num_variables + 1) +
725 " B_g2 entries, but saw " + std::to_string(layer1.B_g2.size()));
727 if (num_variables + 1 != layer1.ABC_g1.size()) {
728 throw std::invalid_argument(
729 "expected " + std::to_string(num_variables + 1) +
730 " ABC entries, but saw " + std::to_string(layer1.ABC_g1.size()));
732 if (n - 1 != layer2.H_g1.size()) {
733 throw std::invalid_argument("mismatch in degrees of layers");
735 if (num_variables - num_inputs != layer2.L_g1.size()) {
736 throw std::invalid_argument(
737 "expected " + std::to_string(num_variables - num_inputs) +
738 " L entries, but saw " + std::to_string(layer2.L_g1.size()));
740 if (pot.tau_powers_g2.size() < n) {
741 throw std::invalid_argument("insufficient POT entries");
744 // { ( [B_i]_2, [B_i]_1 ) } i = 0 .. num_variables
745 std::vector<libsnark::knowledge_commitment<G2, G1>> B_i(num_variables + 1);
746 for (size_t i = 0; i < num_variables + 1; ++i) {
747 B_i[i] = libsnark::knowledge_commitment<G2, G1>(
748 layer1.B_g2[i], layer1.B_g1[i]);
750 assert(B_i.size() == num_variables + 1);
752 // [ ABC_0 ]_1, { [ABC_i]_1 }, i = 1 .. num_inputs
753 G1 ABC_0 = layer1.ABC_g1[0];
754 libff::G1_vector<ppT> ABC_i(num_inputs);
755 for (size_t i = 0; i < num_inputs; ++i) {
756 ABC_i[i] = layer1.ABC_g1[i + 1];
759 libsnark::r1cs_gg_ppzksnark_verification_key<ppT> vk(
760 pot.alpha_tau_powers_g1[0],
763 libsnark::accumulation_vector<G1>(std::move(ABC_0), std::move(ABC_i)));
765 // Convert all arrays to special form, if requested.
766 if (BaseForm == libff::multi_exp_base_form_special) {
767 libff::batch_to_special(layer1.A_g1);
768 libff::batch_to_special(B_i);
769 libff::batch_to_special(layer2.H_g1);
770 libff::batch_to_special(layer2.L_g1);
773 libsnark::r1cs_gg_ppzksnark_proving_key<ppT> pk(
774 G1(pot.alpha_tau_powers_g1[0]),
775 G1(pot.beta_tau_powers_g1[0]),
779 std::move(layer1.A_g1),
780 libsnark::knowledge_commitment_vector<G2, G1>(std::move(B_i)),
781 std::move(layer2.H_g1),
782 std::move(layer2.L_g1),
785 return libsnark::r1cs_gg_ppzksnark_keypair<ppT>(
786 std::move(pk), std::move(vk));
789 } // namespace libzeth
791 #endif // __ZETH_MPC_GROTH16_PHASE2_TCC__