arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
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}