hyperplonk/hyperplonk/src/snark.rs
hyperplonk/src/snark.rsRaw
1use crate::{
2 errors::HyperPlonkErrors,
3 structs::{HyperPlonkIndex, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkVerifyingKey},
4 utils::{build_f, eval_f, eval_perm_gate, prover_sanity_check, PcsAccumulator},
5 witness::WitnessColumn,
6 HyperPlonkSNARK,
7};
8use arithmetic::{evaluate_opt, gen_eval_point, VPAuxInfo};
9use ark_ec::PairingEngine;
10use ark_poly::DenseMultilinearExtension;
11use ark_std::{end_timer, log2, start_timer, One, Zero};
12use std::{marker::PhantomData, rc::Rc};
13use subroutines::{
14 pcs::prelude::{Commitment, PolynomialCommitmentScheme},
15 poly_iop::{
16 prelude::{PermutationCheck, ZeroCheck},
17 PolyIOP,
18 },
19 BatchProof,
20};
21use transcript::IOPTranscript;
22
23impl<E, PCS> HyperPlonkSNARK<E, PCS> for PolyIOP<E::Fr>
24where
25 E: PairingEngine,
26 // Ideally we want to access polynomial as PCS::Polynomial, instead of instantiating it here.
27 // But since PCS::Polynomial can be both univariate or multivariate in our implementation
28 // we cannot bound PCS::Polynomial with a property trait bound.
29 PCS: PolynomialCommitmentScheme<
30 E,
31 Polynomial = Rc<DenseMultilinearExtension<E::Fr>>,
32 Point = Vec<E::Fr>,
33 Evaluation = E::Fr,
34 Commitment = Commitment<E>,
35 BatchProof = BatchProof<E, PCS>,
36 >,
37{
38 type Index = HyperPlonkIndex<E::Fr>;
39 type ProvingKey = HyperPlonkProvingKey<E, PCS>;
40 type VerifyingKey = HyperPlonkVerifyingKey<E, PCS>;
41 type Proof = HyperPlonkProof<E, Self, PCS>;
42
43 fn preprocess(
44 index: &Self::Index,
45 pcs_srs: &PCS::SRS,
46 ) -> Result<(Self::ProvingKey, Self::VerifyingKey), HyperPlonkErrors> {
47 let num_vars = index.num_variables();
48 let supported_ml_degree = num_vars;
49
50 // extract PCS prover and verifier keys from SRS
51 let (pcs_prover_param, pcs_verifier_param) =
52 PCS::trim(pcs_srs, None, Some(supported_ml_degree))?;
53
54 // build permutation oracles
55 let mut permutation_oracles = vec![];
56 let mut perm_comms = vec![];
57 let chunk_size = 1 << num_vars;
58 for i in 0..index.num_witness_columns() {
59 let perm_oracle = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
60 num_vars,
61 &index.permutation[i * chunk_size..(i + 1) * chunk_size],
62 ));
63 let perm_comm = PCS::commit(&pcs_prover_param, &perm_oracle)?;
64 permutation_oracles.push(perm_oracle);
65 perm_comms.push(perm_comm);
66 }
67
68 // build selector oracles and commit to it
69 let selector_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>> = index
70 .selectors
71 .iter()
72 .map(|s| Rc::new(DenseMultilinearExtension::from(s)))
73 .collect();
74
75 let selector_commitments = selector_oracles
76 .iter()
77 .map(|poly| PCS::commit(&pcs_prover_param, poly))
78 .collect::<Result<Vec<_>, _>>()?;
79
80 Ok((
81 Self::ProvingKey {
82 params: index.params.clone(),
83 permutation_oracles,
84 selector_oracles,
85 selector_commitments: selector_commitments.clone(),
86 permutation_commitments: perm_comms.clone(),
87 pcs_param: pcs_prover_param,
88 },
89 Self::VerifyingKey {
90 params: index.params.clone(),
91 pcs_param: pcs_verifier_param,
92 selector_commitments,
93 perm_commitments: perm_comms,
94 },
95 ))
96 }
97
98 /// Generate HyperPlonk SNARK proof.
99 ///
100 /// Inputs:
101 /// - `pk`: circuit proving key
102 /// - `pub_input`: online public input of length 2^\ell
103 /// - `witness`: witness assignment of length 2^n
104 /// Outputs:
105 /// - The HyperPlonk SNARK proof.
106 ///
107 /// Steps:
108 ///
109 /// 1. Commit Witness polynomials `w_i(x)` and append commitment to
110 /// transcript
111 ///
112 /// 2. Run ZeroCheck on
113 ///
114 /// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
115 ///
116 /// where `f` is the constraint polynomial i.e.,
117 /// ```ignore
118 /// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
119 /// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
120 /// ```
121 /// in vanilla plonk, and obtain a ZeroCheckSubClaim
122 ///
123 /// 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracle`, and
124 /// obtain a PermCheckSubClaim.
125 ///
126 /// 4. Generate evaluations and corresponding proofs
127 /// - 4.1. (deferred) batch opening prod(x) at
128 /// - [0, perm_check_point]
129 /// - [1, perm_check_point]
130 /// - [perm_check_point, 0]
131 /// - [perm_check_point, 1]
132 /// - [1,...1, 0]
133 ///
134 /// - 4.2. permutation check evaluations and proofs
135 /// - 4.2.1. (deferred) wi_poly(perm_check_point)
136 ///
137 /// - 4.3. zero check evaluations and proofs
138 /// - 4.3.1. (deferred) wi_poly(zero_check_point)
139 /// - 4.3.2. (deferred) selector_poly(zero_check_point)
140 ///
141 /// - 4.4. public input consistency checks
142 /// - pi_poly(r_pi) where r_pi is sampled from transcript
143 ///
144 /// - 5. deferred batch opening
145 fn prove(
146 pk: &Self::ProvingKey,
147 pub_input: &[E::Fr],
148 witnesses: &[WitnessColumn<E::Fr>],
149 ) -> Result<Self::Proof, HyperPlonkErrors> {
150 let start = start_timer!(|| "hyperplonk proving");
151 let mut transcript = IOPTranscript::<E::Fr>::new(b"hyperplonk");
152
153 prover_sanity_check(&pk.params, pub_input, witnesses)?;
154
155 // witness assignment of length 2^n
156 let num_vars = pk.params.num_variables();
157
158 // online public input of length 2^\ell
159 let ell = log2(pk.params.num_pub_input) as usize;
160
161 // We use accumulators to store the polynomials and their eval points.
162 // They are batch opened at a later stage.
163 let mut pcs_acc = PcsAccumulator::<E, PCS>::new(num_vars);
164
165 // =======================================================================
166 // 1. Commit Witness polynomials `w_i(x)` and append commitment to
167 // transcript
168 // =======================================================================
169 let step = start_timer!(|| "commit witnesses");
170
171 let witness_polys: Vec<Rc<DenseMultilinearExtension<E::Fr>>> = witnesses
172 .iter()
173 .map(|w| Rc::new(DenseMultilinearExtension::from(w)))
174 .collect();
175
176 let witness_commits = witness_polys
177 .iter()
178 .map(|x| PCS::commit(&pk.pcs_param, x).unwrap())
179 .collect::<Vec<_>>();
180 for w_com in witness_commits.iter() {
181 transcript.append_serializable_element(b"w", w_com)?;
182 }
183
184 end_timer!(step);
185 // =======================================================================
186 // 2 Run ZeroCheck on
187 //
188 // `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
189 //
190 // where `f` is the constraint polynomial i.e.,
191 //
192 // f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
193 // = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
194 //
195 // in vanilla plonk, and obtain a ZeroCheckSubClaim
196 // =======================================================================
197 let step = start_timer!(|| "ZeroCheck on f");
198
199 let fx = build_f(
200 &pk.params.gate_func,
201 pk.params.num_variables(),
202 &pk.selector_oracles,
203 &witness_polys,
204 )?;
205
206 let zero_check_proof = <Self as ZeroCheck<E::Fr>>::prove(&fx, &mut transcript)?;
207 end_timer!(step);
208 // =======================================================================
209 // 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracle`, and
210 // obtain a PermCheckSubClaim.
211 // =======================================================================
212 let step = start_timer!(|| "Permutation check on w_i(x)");
213
214 let (perm_check_proof, prod_x, frac_poly) = <Self as PermutationCheck<E, PCS>>::prove(
215 &pk.pcs_param,
216 &witness_polys,
217 &witness_polys,
218 &pk.permutation_oracles,
219 &mut transcript,
220 )?;
221 let perm_check_point = &perm_check_proof.zero_check_proof.point;
222
223 end_timer!(step);
224 // =======================================================================
225 // 4. Generate evaluations and corresponding proofs
226 // - permcheck
227 // 1. (deferred) batch opening prod(x) at
228 // - [perm_check_point]
229 // - [perm_check_point[2..n], 0]
230 // - [perm_check_point[2..n], 1]
231 // - [1,...1, 0]
232 // 2. (deferred) batch opening frac(x) at
233 // - [perm_check_point]
234 // - [perm_check_point[2..n], 0]
235 // - [perm_check_point[2..n], 1]
236 // 3. (deferred) batch opening s_id(x) at
237 // - [perm_check_point]
238 // 4. (deferred) batch opening perms(x) at
239 // - [perm_check_point]
240 // 5. (deferred) batch opening witness_i(x) at
241 // - [perm_check_point]
242 //
243 // - zero check evaluations and proofs
244 // - 4.3.1. (deferred) wi_poly(zero_check_point)
245 // - 4.3.2. (deferred) selector_poly(zero_check_point)
246 //
247 // - 4.4. (deferred) public input consistency checks
248 // - pi_poly(r_pi) where r_pi is sampled from transcript
249 // =======================================================================
250 let step = start_timer!(|| "opening and evaluations");
251
252 // (perm_check_point[2..n], 0)
253 let perm_check_point_0 = [&[E::Fr::zero()], &perm_check_point[0..num_vars - 1]].concat();
254 // (perm_check_point[2..n], 1)
255 let perm_check_point_1 = [&[E::Fr::one()], &perm_check_point[0..num_vars - 1]].concat();
256 // (1, ..., 1, 0)
257 let prod_final_query_point =
258 [vec![E::Fr::zero()], vec![E::Fr::one(); num_vars - 1]].concat();
259
260 // prod(x)'s points
261 pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, perm_check_point);
262 pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &perm_check_point_0);
263 pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &perm_check_point_1);
264 pcs_acc.insert_poly_and_points(
265 &prod_x,
266 &perm_check_proof.prod_x_comm,
267 &prod_final_query_point,
268 );
269
270 // frac(x)'s points
271 pcs_acc.insert_poly_and_points(&frac_poly, &perm_check_proof.frac_comm, perm_check_point);
272 pcs_acc.insert_poly_and_points(
273 &frac_poly,
274 &perm_check_proof.frac_comm,
275 &perm_check_point_0,
276 );
277 pcs_acc.insert_poly_and_points(
278 &frac_poly,
279 &perm_check_proof.frac_comm,
280 &perm_check_point_1,
281 );
282
283 // perms(x)'s points
284 for (perm, pcom) in pk
285 .permutation_oracles
286 .iter()
287 .zip(pk.permutation_commitments.iter())
288 {
289 pcs_acc.insert_poly_and_points(perm, pcom, perm_check_point);
290 }
291
292 // witnesses' points
293 // TODO: refactor so it remains correct even if the order changed
294 for (wpoly, wcom) in witness_polys.iter().zip(witness_commits.iter()) {
295 pcs_acc.insert_poly_and_points(wpoly, wcom, perm_check_point);
296 }
297 for (wpoly, wcom) in witness_polys.iter().zip(witness_commits.iter()) {
298 pcs_acc.insert_poly_and_points(wpoly, wcom, &zero_check_proof.point);
299 }
300
301 // - 4.3.2. (deferred) selector_poly(zero_check_point)
302 pk.selector_oracles
303 .iter()
304 .zip(pk.selector_commitments.iter())
305 .for_each(|(poly, com)| {
306 pcs_acc.insert_poly_and_points(poly, com, &zero_check_proof.point)
307 });
308
309 // - 4.4. public input consistency checks
310 // - pi_poly(r_pi) where r_pi is sampled from transcript
311 let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?;
312 // padded with zeros
313 let r_pi_padded = [r_pi, vec![E::Fr::zero(); num_vars - ell]].concat();
314 // Evaluate witness_poly[0] at r_pi||0s which is equal to public_input evaluated
315 // at r_pi. Assumes that public_input is a power of 2
316 pcs_acc.insert_poly_and_points(&witness_polys[0], &witness_commits[0], &r_pi_padded);
317 end_timer!(step);
318
319 // =======================================================================
320 // 5. deferred batch opening
321 // =======================================================================
322 let step = start_timer!(|| "deferred batch openings prod(x)");
323 let batch_openings = pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?;
324 end_timer!(step);
325
326 end_timer!(start);
327
328 Ok(HyperPlonkProof {
329 // PCS commit for witnesses
330 witness_commits,
331 // batch_openings,
332 batch_openings,
333 // =======================================================================
334 // IOP proofs
335 // =======================================================================
336 // the custom gate zerocheck proof
337 zero_check_proof,
338 // the permutation check proof for copy constraints
339 perm_check_proof,
340 })
341 }
342
343 /// Verify the HyperPlonk proof.
344 ///
345 /// Inputs:
346 /// - `vk`: verification key
347 /// - `pub_input`: online public input
348 /// - `proof`: HyperPlonk SNARK proof
349 /// Outputs:
350 /// - Return a boolean on whether the verification is successful
351 ///
352 /// 1. Verify zero_check_proof on
353 ///
354 /// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
355 ///
356 /// where `f` is the constraint polynomial i.e.,
357 /// ```ignore
358 /// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
359 /// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
360 /// ```
361 /// in vanilla plonk, and obtain a ZeroCheckSubClaim
362 ///
363 /// 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracles`
364 ///
365 /// 3. check subclaim validity
366 ///
367 /// 4. Verify the opening against the commitment:
368 /// - check permutation check evaluations
369 /// - check zero check evaluations
370 /// - public input consistency checks
371 fn verify(
372 vk: &Self::VerifyingKey,
373 pub_input: &[E::Fr],
374 proof: &Self::Proof,
375 ) -> Result<bool, HyperPlonkErrors> {
376 let start = start_timer!(|| "hyperplonk verification");
377
378 let mut transcript = IOPTranscript::<E::Fr>::new(b"hyperplonk");
379
380 let num_selectors = vk.params.num_selector_columns();
381 let num_witnesses = vk.params.num_witness_columns();
382 let num_vars = vk.params.num_variables();
383
384 // online public input of length 2^\ell
385 let ell = log2(vk.params.num_pub_input) as usize;
386
387 // =======================================================================
388 // 0. sanity checks
389 // =======================================================================
390 // public input length
391 if pub_input.len() != vk.params.num_pub_input {
392 return Err(HyperPlonkErrors::InvalidProver(format!(
393 "Public input length is not correct: got {}, expect {}",
394 pub_input.len(),
395 1 << ell
396 )));
397 }
398
399 // Extract evaluations from openings
400 let prod_evals = &proof.batch_openings.f_i_eval_at_point_i[0..4];
401 let frac_evals = &proof.batch_openings.f_i_eval_at_point_i[4..7];
402 let perm_evals = &proof.batch_openings.f_i_eval_at_point_i[7..7 + num_witnesses];
403 let witness_perm_evals =
404 &proof.batch_openings.f_i_eval_at_point_i[7 + num_witnesses..7 + 2 * num_witnesses];
405 let witness_gate_evals =
406 &proof.batch_openings.f_i_eval_at_point_i[7 + 2 * num_witnesses..7 + 3 * num_witnesses];
407 let selector_evals = &proof.batch_openings.f_i_eval_at_point_i
408 [7 + 3 * num_witnesses..7 + 3 * num_witnesses + num_selectors];
409 let pi_eval = proof.batch_openings.f_i_eval_at_point_i.last().unwrap();
410
411 // =======================================================================
412 // 1. Verify zero_check_proof on
413 // `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
414 //
415 // where `f` is the constraint polynomial i.e.,
416 //
417 // f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
418 // = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
419 //
420 // =======================================================================
421 let step = start_timer!(|| "verify zero check");
422 // Zero check and perm check have different AuxInfo
423 let zero_check_aux_info = VPAuxInfo::<E::Fr> {
424 max_degree: vk.params.gate_func.degree(),
425 num_variables: num_vars,
426 phantom: PhantomData::default(),
427 };
428 // push witness to transcript
429 for w_com in proof.witness_commits.iter() {
430 transcript.append_serializable_element(b"w", w_com)?;
431 }
432
433 let zero_check_sub_claim = <Self as ZeroCheck<E::Fr>>::verify(
434 &proof.zero_check_proof,
435 &zero_check_aux_info,
436 &mut transcript,
437 )?;
438
439 let zero_check_point = zero_check_sub_claim.point.clone();
440
441 // check zero check subclaim
442 let f_eval = eval_f(&vk.params.gate_func, selector_evals, witness_gate_evals)?;
443 if f_eval != zero_check_sub_claim.expected_evaluation {
444 return Err(HyperPlonkErrors::InvalidProof(
445 "zero check evaluation failed".to_string(),
446 ));
447 }
448
449 end_timer!(step);
450 // =======================================================================
451 // 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracle`
452 // =======================================================================
453 let step = start_timer!(|| "verify permutation check");
454
455 // Zero check and perm check have different AuxInfo
456 let perm_check_aux_info = VPAuxInfo::<E::Fr> {
457 // Prod(x) has a max degree of witnesses.len() + 1
458 max_degree: proof.witness_commits.len() + 1,
459 num_variables: num_vars,
460 phantom: PhantomData::default(),
461 };
462 let perm_check_sub_claim = <Self as PermutationCheck<E, PCS>>::verify(
463 &proof.perm_check_proof,
464 &perm_check_aux_info,
465 &mut transcript,
466 )?;
467
468 let perm_check_point = perm_check_sub_claim
469 .product_check_sub_claim
470 .zero_check_sub_claim
471 .point
472 .clone();
473
474 let alpha = perm_check_sub_claim.product_check_sub_claim.alpha;
475 let (beta, gamma) = perm_check_sub_claim.challenges;
476
477 let mut id_evals = vec![];
478 for i in 0..num_witnesses {
479 let ith_point = gen_eval_point(i, log2(num_witnesses) as usize, &perm_check_point[..]);
480 id_evals.push(vk.params.eval_id_oracle(&ith_point[..])?);
481 }
482
483 // check evaluation subclaim
484 let perm_gate_eval = eval_perm_gate(
485 prod_evals,
486 frac_evals,
487 witness_perm_evals,
488 &id_evals[..],
489 perm_evals,
490 alpha,
491 beta,
492 gamma,
493 *perm_check_point.last().unwrap(),
494 )?;
495 if perm_gate_eval
496 != perm_check_sub_claim
497 .product_check_sub_claim
498 .zero_check_sub_claim
499 .expected_evaluation
500 {
501 return Err(HyperPlonkErrors::InvalidVerifier(
502 "evaluation failed".to_string(),
503 ));
504 }
505
506 end_timer!(step);
507 // =======================================================================
508 // 3. Verify the opening against the commitment
509 // =======================================================================
510 let step = start_timer!(|| "assemble commitments");
511
512 // generate evaluation points and commitments
513 let mut comms = vec![];
514 let mut points = vec![];
515
516 let perm_check_point_0 = [&[E::Fr::zero()], &perm_check_point[0..num_vars - 1]].concat();
517 let perm_check_point_1 = [&[E::Fr::one()], &perm_check_point[0..num_vars - 1]].concat();
518 let prod_final_query_point =
519 [vec![E::Fr::zero()], vec![E::Fr::one(); num_vars - 1]].concat();
520
521 // prod(x)'s points
522 comms.push(proof.perm_check_proof.prod_x_comm);
523 comms.push(proof.perm_check_proof.prod_x_comm);
524 comms.push(proof.perm_check_proof.prod_x_comm);
525 comms.push(proof.perm_check_proof.prod_x_comm);
526 points.push(perm_check_point.clone());
527 points.push(perm_check_point_0.clone());
528 points.push(perm_check_point_1.clone());
529 points.push(prod_final_query_point);
530 // frac(x)'s points
531 comms.push(proof.perm_check_proof.frac_comm);
532 comms.push(proof.perm_check_proof.frac_comm);
533 comms.push(proof.perm_check_proof.frac_comm);
534 points.push(perm_check_point.clone());
535 points.push(perm_check_point_0);
536 points.push(perm_check_point_1);
537
538 // perms' points
539 for &pcom in vk.perm_commitments.iter() {
540 comms.push(pcom);
541 points.push(perm_check_point.clone());
542 }
543
544 // witnesses' points
545 // TODO: merge points
546 for &wcom in proof.witness_commits.iter() {
547 comms.push(wcom);
548 points.push(perm_check_point.clone());
549 }
550 for &wcom in proof.witness_commits.iter() {
551 comms.push(wcom);
552 points.push(zero_check_point.clone());
553 }
554
555 // selector_poly(zero_check_point)
556 for &com in vk.selector_commitments.iter() {
557 comms.push(com);
558 points.push(zero_check_point.clone());
559 }
560
561 // - 4.4. public input consistency checks
562 // - pi_poly(r_pi) where r_pi is sampled from transcript
563 let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?;
564
565 // check public evaluation
566 let pi_step = start_timer!(|| "check public evaluation");
567 let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input);
568 let expect_pi_eval = evaluate_opt(&pi_poly, &r_pi[..]);
569 if expect_pi_eval != *pi_eval {
570 return Err(HyperPlonkErrors::InvalidProver(format!(
571 "Public input eval mismatch: got {}, expect {}",
572 pi_eval, expect_pi_eval,
573 )));
574 }
575 let r_pi_padded = [r_pi, vec![E::Fr::zero(); num_vars - ell]].concat();
576
577 comms.push(proof.witness_commits[0]);
578 points.push(r_pi_padded);
579 assert_eq!(comms.len(), proof.batch_openings.f_i_eval_at_point_i.len());
580 end_timer!(pi_step);
581
582 end_timer!(step);
583 let step = start_timer!(|| "PCS batch verify");
584 // check proof
585 let res = PCS::batch_verify(
586 &vk.pcs_param,
587 &comms,
588 &points,
589 &proof.batch_openings,
590 &mut transcript,
591 )?;
592
593 end_timer!(step);
594 end_timer!(start);
595 Ok(res)
596 }
597}
598
599#[cfg(test)]
600mod tests {
601 use super::*;
602 use crate::{
603 custom_gate::CustomizedGates, selectors::SelectorColumn, structs::HyperPlonkParams,
604 witness::WitnessColumn,
605 };
606 use arithmetic::{identity_permutation, random_permutation};
607 use ark_bls12_381::Bls12_381;
608 use ark_std::test_rng;
609 use subroutines::pcs::prelude::MultilinearKzgPCS;
610
611 #[test]
612 fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> {
613 // Example:
614 // q_L(X) * W_1(X)^5 - W_2(X) = 0
615 // is represented as
616 // vec![
617 // ( 1, Some(id_qL), vec![id_W1, id_W1, id_W1, id_W1, id_W1]),
618 // (-1, None, vec![id_W2])
619 // ]
620 //
621 // 4 public input
622 // 1 selector,
623 // 2 witnesses,
624 // 2 variables for MLE,
625 // 4 wires,
626 let gates = CustomizedGates {
627 gates: vec![(1, Some(0), vec![0, 0, 0, 0, 0]), (-1, None, vec![1])],
628 };
629 test_hyperplonk_helper::<Bls12_381>(gates)
630 }
631
632 fn test_hyperplonk_helper<E: PairingEngine>(
633 gate_func: CustomizedGates,
634 ) -> Result<(), HyperPlonkErrors> {
635 let mut rng = test_rng();
636 let pcs_srs = MultilinearKzgPCS::<E>::gen_srs_for_testing(&mut rng, 16)?;
637
638 let num_constraints = 4;
639 let num_pub_input = 4;
640 let nv = log2(num_constraints) as usize;
641 let num_witnesses = 2;
642
643 // generate index
644 let params = HyperPlonkParams {
645 num_constraints,
646 num_pub_input,
647 gate_func,
648 };
649 let permutation = identity_permutation(nv, num_witnesses);
650 let q1 = SelectorColumn(vec![E::Fr::one(), E::Fr::one(), E::Fr::one(), E::Fr::one()]);
651 let index = HyperPlonkIndex {
652 params,
653 permutation,
654 selectors: vec![q1],
655 };
656
657 // generate pk and vks
658 let (pk, vk) = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::preprocess(
659 &index, &pcs_srs,
660 )?;
661
662 // w1 := [0, 1, 2, 3]
663 let w1 = WitnessColumn(vec![
664 E::Fr::zero(),
665 E::Fr::one(),
666 E::Fr::from(2u64),
667 E::Fr::from(3u64),
668 ]);
669 // w2 := [0^5, 1^5, 2^5, 3^5]
670 let w2 = WitnessColumn(vec![
671 E::Fr::zero(),
672 E::Fr::one(),
673 E::Fr::from(32u64),
674 E::Fr::from(243u64),
675 ]);
676 // public input = w1
677 let pi = w1.clone();
678
679 // generate a proof and verify
680 let proof = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::prove(
681 &pk,
682 &pi.0,
683 &[w1.clone(), w2.clone()],
684 )?;
685
686 let _verify = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::verify(
687 &vk, &pi.0, &proof,
688 )?;
689
690 // bad path 1: wrong permutation
691 let rand_perm: Vec<E::Fr> = random_permutation(nv, num_witnesses, &mut rng);
692 let mut bad_index = index.clone();
693 bad_index.permutation = rand_perm;
694 // generate pk and vks
695 let (_, bad_vk) = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::preprocess(
696 &bad_index, &pcs_srs,
697 )?;
698 assert_eq!(
699 <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::verify(
700 &bad_vk, &pi.0, &proof,
701 )?,
702 false
703 );
704
705 // bad path 2: wrong witness
706 let mut w1_bad = w1.clone();
707 w1_bad.0[0] = E::Fr::one();
708 assert!(
709 <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::prove(
710 &pk,
711 &pi.0,
712 &[w1_bad, w2],
713 )
714 .is_err()
715 );
716
717 Ok(())
718 }
719}