Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
phase2.tcc
Go to the documentation of this file.
1 // Copyright (c) 2015-2022 Clearmatics Technologies Ltd
2 //
3 // SPDX-License-Identifier: LGPL-3.0+
4 
5 #ifndef __ZETH_MPC_GROTH16_PHASE2_TCC__
6 #define __ZETH_MPC_GROTH16_PHASE2_TCC__
7 
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"
14 
15 #include <libff/common/rng.hpp>
16 
17 namespace libzeth
18 {
19 
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)
28 {
29  memcpy(this->cs_hash, cs_hash, sizeof(mpc_hash_t));
30 }
31 
32 template<typename ppT>
33 bool srs_mpc_phase2_accumulator<ppT>::operator==(
34  const srs_mpc_phase2_accumulator<ppT> &other) const
35 {
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);
39 }
40 
41 template<typename ppT>
42 bool srs_mpc_phase2_accumulator<ppT>::is_well_formed() const
43 {
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);
46 }
47 
48 template<typename ppT>
49 void srs_mpc_phase2_accumulator<ppT>::write(std::ostream &out) const
50 {
51  using G1 = libff::G1<ppT>;
52  check_well_formed(*this, "mpc_layer2 (write)");
53 
54  // Write cs_hash and sizes first.
55 
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));
61 
62  out << delta_g1;
63  out << delta_g2;
64  for (const G1 h : H_g1) {
65  out << h;
66  }
67  for (const G1 l : L_g1) {
68  out << l;
69  }
70 }
71 
72 template<typename ppT>
73 void srs_mpc_phase2_accumulator<ppT>::write_compressed(std::ostream &out) const
74 {
75  using G1 = libff::G1<ppT>;
76  check_well_formed(*this, "mpc_layer2 (write)");
77 
78  // Write cs_hash and sizes first.
79 
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));
85 
86  delta_g1.write_compressed(out);
87  delta_g2.write_compressed(out);
88  for (const G1 &h : H_g1) {
89  h.write_compressed(out);
90  }
91  for (const G1 &l : L_g1) {
92  l.write_compressed(out);
93  }
94 }
95 
96 template<typename ppT>
97 srs_mpc_phase2_accumulator<ppT> srs_mpc_phase2_accumulator<ppT>::read(
98  std::istream &in)
99 {
100  using G1 = libff::G1<ppT>;
101 
102  mpc_hash_t cs_hash;
103  size_t H_size;
104  size_t L_size;
105 
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));
109 
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);
114 
115  in >> delta_g1;
116  in >> delta_g2;
117  for (G1 &h : H_g1) {
118  in >> h;
119  }
120  for (G1 &l : L_g1) {
121  in >> l;
122  }
123 
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)");
127  return accum;
128 }
129 
130 template<typename ppT>
131 srs_mpc_phase2_accumulator<ppT> srs_mpc_phase2_accumulator<
132  ppT>::read_compressed(std::istream &in)
133 {
134  using G1 = libff::G1<ppT>;
135  using G2 = libff::G2<ppT>;
136 
137  mpc_hash_t cs_hash;
138  size_t H_size;
139  size_t L_size;
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));
143 
144  G1 delta_g1;
145  G1::read_compressed(in, delta_g1);
146  G2 delta_g2;
147  G2::read_compressed(in, delta_g2);
148 
149  libff::G1_vector<ppT> H_g1(H_size);
150  for (G1 &h : H_g1) {
151  G1::read_compressed(in, h);
152  }
153 
154  libff::G1_vector<ppT> L_g1(L_size);
155  for (G1 &l : L_g1) {
156  G1::read_compressed(in, l);
157  }
158 
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)");
162  return l2;
163 }
164 
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)
173  , s_g1(s_g1)
174  , s_delta_j_g1(s_delta_j_g1)
175  , r_delta_j_g2(r_delta_j_g2)
176 {
177  memcpy(this->transcript_digest, transcript_digest, sizeof(mpc_hash_t));
178 }
179 
180 template<typename ppT>
181 bool srs_mpc_phase2_publickey<ppT>::operator==(
182  const srs_mpc_phase2_publickey<ppT> &other) const
183 {
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);
189 }
190 
191 template<typename ppT>
192 bool srs_mpc_phase2_publickey<ppT>::is_well_formed() const
193 {
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();
196 }
197 
198 template<typename ppT>
199 void srs_mpc_phase2_publickey<ppT>::write(std::ostream &out) const
200 {
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;
204 }
205 
206 template<typename ppT>
207 srs_mpc_phase2_publickey<ppT> srs_mpc_phase2_publickey<ppT>::read(
208  std::istream &in)
209 {
210  mpc_hash_t transcript_digest;
211  libff::G1<ppT> new_delta_g1;
212  libff::G1<ppT> s_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");
220  return pubkey;
221 }
222 
223 template<typename ppT>
224 void srs_mpc_phase2_publickey<ppT>::compute_digest(mpc_hash_t out_digest) const
225 {
226  mpc_hash_ostream hs;
227  write(hs);
228  hs.get_hash(out_digest);
229 }
230 
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)
236 {
237  memcpy(this->transcript_digest, transcript_digest, sizeof(mpc_hash_t));
238 }
239 
240 template<typename ppT>
241 bool srs_mpc_phase2_challenge<ppT>::operator==(
242  const srs_mpc_phase2_challenge<ppT> &other) const
243 {
244  const bool digest_match =
245  !memcmp(transcript_digest, other.transcript_digest, sizeof(mpc_hash_t));
246  return digest_match && (accumulator == other.accumulator);
247 }
248 
249 template<typename ppT>
250 bool srs_mpc_phase2_challenge<ppT>::is_well_formed() const
251 {
252  return accumulator.is_well_formed();
253 }
254 
255 template<typename ppT>
256 void srs_mpc_phase2_challenge<ppT>::write(std::ostream &out) const
257 {
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);
261 }
262 
263 template<typename ppT>
264 srs_mpc_phase2_challenge<ppT> srs_mpc_phase2_challenge<ppT>::read(
265  std::istream &in)
266 {
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");
274  return challenge;
275 }
276 
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)
282 {
283 }
284 
285 template<typename ppT>
286 bool srs_mpc_phase2_response<ppT>::operator==(
287  const srs_mpc_phase2_response<ppT> &other) const
288 {
289  return (new_accumulator == other.new_accumulator) &&
290  (publickey == other.publickey);
291 }
292 
293 template<typename ppT> bool srs_mpc_phase2_response<ppT>::is_well_formed() const
294 {
295  return new_accumulator.is_well_formed() && publickey.is_well_formed();
296 }
297 
298 template<typename ppT>
299 void srs_mpc_phase2_response<ppT>::write(std::ostream &out) const
300 {
301  check_well_formed(*this, "srs_mpc_phase2_response::write");
302  new_accumulator.write_compressed(out);
303  publickey.write(out);
304 }
305 
306 template<typename ppT>
307 srs_mpc_phase2_response<ppT> srs_mpc_phase2_response<ppT>::read(
308  std::istream &in)
309 {
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);
314 
315  srs_mpc_phase2_response<ppT> response(
316  std::move(accumulator), std::move(pubkey));
317  check_well_formed(response, "srs_mpc_phase2_response::read");
318  return response;
319 }
320 
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)
324 {
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;
328 
329  chacha_rng rng(transcript_digest, sizeof(mpc_hash_t));
330  rng.random(random.data, sizeof(random));
331  mpn_tdiv_qr(
332  _quotient.data,
333  out_fr.mont_repr.data,
334  0,
335  random.data,
336  2 * n,
337  modulus.data,
338  n);
339 }
340 
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)
345 {
346  libff::Fr<ppT> fr;
347  srs_mpc_digest_to_fp(transcript_digest, fr);
348  return fr * libff::G2<ppT>::one();
349 }
350 
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,
355  size_t num_inputs)
356 {
357  // In layer_L1 output, there should be num_variables+1 entries in
358  // ABC_g1. Of these:
359  //
360  // - The first 1+num_inputs entries are used directly in the
361  // verification key.
362  //
363  // - The remaining num_variables-num_inputs entries will be
364  // divided by delta to create layer2.
365 
366  return srs_mpc_phase2_accumulator<ppT>(
367  cs_hash,
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()));
373 }
374 
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)
380 {
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");
388 
389  return srs_mpc_phase2_publickey<ppT>(
390  transcript_digest, new_delta_g1, s_g1, s_delta_j_g1, r_delta_j_g2);
391 }
392 
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)
398 {
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;
404 
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)) {
407  return false;
408  }
409 
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)) {
412  return false;
413  }
414 
415  return true;
416 }
417 
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)
422 {
423  libff::G2<ppT> r_g2;
424  return srs_mpc_phase2_verify_publickey<ppT>(last_delta_g1, publickey, r_g2);
425 }
426 
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)
431 {
432  libff::enter_block("call to srs_mpc_phase2_update_accumulator");
433  const libff::Fr<ppT> delta_j_inverse = delta_j.inverse();
434 
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;
438 
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);
446  }
447  libff::G1_vector<ppT> L_g1(num_L_elements);
448 #ifdef MULTICORE
449 #pragma omp parallel for
450 #endif
451  for (size_t i = 0; i < num_L_elements; ++i) {
452  L_g1[i] = delta_j_inverse * last_accum.L_g1[i];
453  }
454  putchar('\n');
455  libff::leave_block("updating L_g1");
456 
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);
463  }
464  libff::G1_vector<ppT> H_g1(H_size);
465 #ifdef MULTICORE
466 #pragma omp parallel for
467 #endif
468  for (size_t i = 0; i < H_size; ++i) {
469  H_g1[i] = delta_j_inverse * last_accum.H_g1[i];
470  }
471  libff::leave_block("updating H_g1");
472 
473  libff::leave_block("call to srs_mpc_phase2_update_accumulator");
474 
475  return srs_mpc_phase2_accumulator<ppT>(
476  last_accum.cs_hash,
477  new_delta_g1,
478  new_delta_g2,
479  std::move(H_g1),
480  std::move(L_g1));
481 }
482 
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)
487 {
488  libff::enter_block("call to srs_mpc_phase2_update_is_consistent");
489 
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()) {
494  return false;
495  }
496 
497  const libff::G2<ppT> &old_delta_g2 = last.delta_g2;
498  const libff::G2<ppT> &new_delta_g2 = updated.delta_g2;
499 
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)) {
503  return false;
504  }
505 
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)) {
511  return false;
512  }
513 
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)) {
517  return false;
518  }
519 
520  libff::leave_block("call to srs_mpc_phase2_update_is_consistent");
521 
522  return true;
523 }
524 
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)
530 {
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
533  // recomputing it.
534  libff::G2<ppT> r_g2;
535  if (!srs_mpc_phase2_verify_publickey(last.delta_g1, publickey, r_g2)) {
536  return false;
537  }
538 
539  if (publickey.new_delta_g1 != updated.delta_g1) {
540  return false;
541  }
542 
543  return srs_mpc_phase2_update_is_consistent(last, updated);
544 }
545 
546 template<typename ppT>
547 srs_mpc_phase2_challenge<ppT> srs_mpc_phase2_initial_challenge(
548  srs_mpc_phase2_accumulator<ppT> &&accumulator)
549 {
550  mpc_hash_t initial_transcript_digest;
551  mpc_compute_hash(
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));
555 }
556 
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)
561 {
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,
567  delta_j);
568  libff::leave_block("computing contribution public key");
569 
570  srs_mpc_phase2_accumulator<ppT> new_accum =
571  srs_mpc_phase2_update_accumulator(challenge.accumulator, delta_j);
572 
573  return srs_mpc_phase2_response<ppT>(
574  std::move(new_accum), std::move(pubkey));
575 }
576 
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)
581 {
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,
586  sizeof(mpc_hash_t));
587  if (!digest_match) {
588  return false;
589  }
590 
591  return srs_mpc_phase2_verify_update(
592  challenge.accumulator, response.new_accumulator, response.publickey);
593 }
594 
595 template<typename ppT>
596 srs_mpc_phase2_challenge<ppT> srs_mpc_phase2_compute_challenge(
597  srs_mpc_phase2_response<ppT> &&response)
598 {
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));
603 }
604 
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)
614 {
615  mpc_hash_t digest;
616  memcpy(digest, initial_transcript_digest, sizeof(mpc_hash_t));
617  libff::G1<ppT> delta = initial_delta;
618 
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);
623 
624  const bool digests_match =
625  !memcmp(digest, publickey.transcript_digest, sizeof(mpc_hash_t));
626  if (!digests_match) {
627  return false;
628  }
629 
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;
634  }
635 
636  if (!srs_mpc_phase2_verify_publickey(delta, publickey)) {
637  return false;
638  }
639 
640  // Contribution is valid. Update state and read next publickey.
641  delta = publickey.new_delta_g1;
642  }
643 
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;
648  }
649 
650  return true;
651 }
652 
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)
660 {
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,
665  initial_delta,
666  dummy_check_for_contribution,
667  transcript_stream,
668  out_final_delta,
669  out_final_transcript_digest,
670  dummy_out_contribution_found);
671 }
672 
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)
678 {
679  // Start with a blank challenge and simulate one contribution of the MPC
680  // using delta.
681  mpc_hash_t init_hash;
682  uint8_t empty[0];
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));
690 }
691 
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)
699 {
700  using G1 = libff::G1<ppT>;
701  using G2 = libff::G2<ppT>;
702 
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();
706 
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()));
716  }
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()));
721  }
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()));
726  }
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()));
731  }
732  if (n - 1 != layer2.H_g1.size()) {
733  throw std::invalid_argument("mismatch in degrees of layers");
734  }
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()));
739  }
740  if (pot.tau_powers_g2.size() < n) {
741  throw std::invalid_argument("insufficient POT entries");
742  }
743 
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]);
749  }
750  assert(B_i.size() == num_variables + 1);
751 
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];
757  }
758 
759  libsnark::r1cs_gg_ppzksnark_verification_key<ppT> vk(
760  pot.alpha_tau_powers_g1[0],
761  pot.beta_g2,
762  layer2.delta_g2,
763  libsnark::accumulation_vector<G1>(std::move(ABC_0), std::move(ABC_i)));
764 
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);
771  }
772 
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]),
776  G2(pot.beta_g2),
777  G1(layer2.delta_g1),
778  G2(layer2.delta_g2),
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),
783  std::move(cs));
784 
785  return libsnark::r1cs_gg_ppzksnark_keypair<ppT>(
786  std::move(pk), std::move(vk));
787 }
788 
789 } // namespace libzeth
790 
791 #endif // __ZETH_MPC_GROTH16_PHASE2_TCC__