hyperplonk/poly-iop/src/perm_check/mod.rs
poly-iop/src/perm_check/mod.rsRaw
1//! Main module for the Permutation Check protocol
2
3use self::util::computer_num_and_denom;
4use crate::{errors::PolyIOPErrors, prelude::ProductCheck, PolyIOP};
5use ark_ec::PairingEngine;
6use ark_poly::DenseMultilinearExtension;
7use ark_std::{end_timer, start_timer};
8use pcs::PolynomialCommitmentScheme;
9use std::rc::Rc;
10use transcript::IOPTranscript;
11
12/// A permutation subclaim consists of
13/// - the SubClaim from the ProductCheck
14/// - Challenges beta and gamma
15#[derive(Clone, Debug, Default, PartialEq)]
16pub struct PermutationCheckSubClaim<E, PCS, PC>
17where
18 E: PairingEngine,
19 PC: ProductCheck<E, PCS>,
20 PCS: PolynomialCommitmentScheme<E>,
21{
22 /// the SubClaim from the ProductCheck
23 pub product_check_sub_claim: PC::ProductCheckSubClaim,
24 /// Challenges beta and gamma
25 pub challenges: (E::Fr, E::Fr),
26}
27
28pub mod util;
29
30/// A PermutationCheck w.r.t. `(f, g, perm)`
31/// proves that g is a permutation of f under
32/// permutation `perm`
33/// It is derived from ProductCheck.
34///
35/// A Permutation Check IOP takes the following steps:
36///
37/// Inputs:
38/// - f(x)
39/// - g(x)
40/// - permutation s_perm(x)
41pub trait PermutationCheck<E, PCS>: ProductCheck<E, PCS>
42where
43 E: PairingEngine,
44 PCS: PolynomialCommitmentScheme<E>,
45{
46 type PermutationCheckSubClaim;
47 type PermutationProof;
48
49 /// Initialize the system with a transcript
50 ///
51 /// This function is optional -- in the case where a PermutationCheck is
52 /// an building block for a more complex protocol, the transcript
53 /// may be initialized by this complex protocol, and passed to the
54 /// PermutationCheck prover/verifier.
55 fn init_transcript() -> Self::Transcript;
56
57 /// Inputs:
58 /// - f(x)
59 /// - g(x)
60 /// - permutation s_perm(x)
61 /// Outputs:
62 /// - a permutation check proof proving that g is a permutation of f under
63 /// s_perm
64 /// - the product polynomial build during product check
65 ///
66 /// Cost: O(N)
67 fn prove(
68 pcs_param: &PCS::ProverParam,
69 fx: &Self::MultilinearExtension,
70 gx: &Self::MultilinearExtension,
71 s_perm: &Self::MultilinearExtension,
72 transcript: &mut IOPTranscript<E::Fr>,
73 ) -> Result<(Self::PermutationProof, Self::MultilinearExtension), PolyIOPErrors>;
74
75 /// Verify that an MLE g(x) is a permutation of
76 /// MLE f(x) over a permutation given by s_perm.
77 fn verify(
78 proof: &Self::PermutationProof,
79 aux_info: &Self::VPAuxInfo,
80 transcript: &mut Self::Transcript,
81 ) -> Result<Self::PermutationCheckSubClaim, PolyIOPErrors>;
82}
83
84impl<E, PCS> PermutationCheck<E, PCS> for PolyIOP<E::Fr>
85where
86 E: PairingEngine,
87 PCS: PolynomialCommitmentScheme<E, Polynomial = Rc<DenseMultilinearExtension<E::Fr>>>,
88{
89 type PermutationCheckSubClaim = PermutationCheckSubClaim<E, PCS, Self>;
90 type PermutationProof = Self::ProductCheckProof;
91
92 fn init_transcript() -> Self::Transcript {
93 IOPTranscript::<E::Fr>::new(b"Initializing PermutationCheck transcript")
94 }
95
96 fn prove(
97 pcs_param: &PCS::ProverParam,
98 fx: &Self::MultilinearExtension,
99 gx: &Self::MultilinearExtension,
100 s_perm: &Self::MultilinearExtension,
101 transcript: &mut IOPTranscript<E::Fr>,
102 ) -> Result<(Self::PermutationProof, Self::MultilinearExtension), PolyIOPErrors> {
103 let start = start_timer!(|| "Permutation check prove");
104 if fx.num_vars != gx.num_vars {
105 return Err(PolyIOPErrors::InvalidParameters(
106 "fx and gx have different number of variables".to_string(),
107 ));
108 }
109
110 if fx.num_vars != s_perm.num_vars {
111 return Err(PolyIOPErrors::InvalidParameters(
112 "fx and s_perm have different number of variables".to_string(),
113 ));
114 }
115
116 // generate challenge `beta` and `gamma` from current transcript
117 let beta = transcript.get_and_append_challenge(b"beta")?;
118 let gamma = transcript.get_and_append_challenge(b"gamma")?;
119 let (numerator, denominator) = computer_num_and_denom(&beta, &gamma, fx, gx, s_perm)?;
120
121 // invoke product check on numerator and denominator
122 let (proof, prod_poly) =
123 <Self as ProductCheck<E, PCS>>::prove(pcs_param, &numerator, &denominator, transcript)?;
124
125 end_timer!(start);
126 Ok((proof, prod_poly))
127 }
128
129 /// Verify that an MLE g(x) is a permutation of an
130 /// MLE f(x) over a permutation given by s_perm.
131 fn verify(
132 proof: &Self::PermutationProof,
133 aux_info: &Self::VPAuxInfo,
134 transcript: &mut Self::Transcript,
135 ) -> Result<Self::PermutationCheckSubClaim, PolyIOPErrors> {
136 let start = start_timer!(|| "Permutation check verify");
137
138 let beta = transcript.get_and_append_challenge(b"beta")?;
139 let gamma = transcript.get_and_append_challenge(b"gamma")?;
140
141 // invoke the zero check on the iop_proof
142 let product_check_sub_claim =
143 <Self as ProductCheck<E, PCS>>::verify(proof, aux_info, transcript)?;
144
145 end_timer!(start);
146 Ok(PermutationCheckSubClaim {
147 product_check_sub_claim,
148 challenges: (beta, gamma),
149 })
150 }
151}
152
153#[cfg(test)]
154mod test {
155 use super::PermutationCheck;
156 use crate::{errors::PolyIOPErrors, PolyIOP};
157 use arithmetic::{evaluate_opt, identity_permutation_mle, random_permutation_mle, VPAuxInfo};
158 use ark_bls12_381::Bls12_381;
159 use ark_ec::PairingEngine;
160 use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
161 use ark_std::test_rng;
162 use pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme};
163 use std::{marker::PhantomData, rc::Rc};
164
165 type KZG = MultilinearKzgPCS<Bls12_381>;
166
167 fn test_permutation_check_helper<E, PCS>(
168 pcs_param: &PCS::ProverParam,
169 fx: &Rc<DenseMultilinearExtension<E::Fr>>,
170 gx: &Rc<DenseMultilinearExtension<E::Fr>>,
171 s_perm: &Rc<DenseMultilinearExtension<E::Fr>>,
172 ) -> Result<(), PolyIOPErrors>
173 where
174 E: PairingEngine,
175 PCS: PolynomialCommitmentScheme<E, Polynomial = Rc<DenseMultilinearExtension<E::Fr>>>,
176 {
177 let nv = fx.num_vars;
178 let poly_info = VPAuxInfo {
179 max_degree: 2,
180 num_variables: nv,
181 phantom: PhantomData::default(),
182 };
183
184 // prover
185 let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
186 transcript.append_message(b"testing", b"initializing transcript for testing")?;
187 let (proof, prod_x) = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::prove(
188 pcs_param,
189 fx,
190 gx,
191 s_perm,
192 &mut transcript,
193 )?;
194
195 // verifier
196 let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
197 transcript.append_message(b"testing", b"initializing transcript for testing")?;
198 let perm_check_sub_claim = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::verify(
199 &proof,
200 &poly_info,
201 &mut transcript,
202 )?;
203
204 // check product subclaim
205 if evaluate_opt(
206 &prod_x,
207 &perm_check_sub_claim.product_check_sub_claim.final_query.0,
208 ) != perm_check_sub_claim.product_check_sub_claim.final_query.1
209 {
210 return Err(PolyIOPErrors::InvalidVerifier("wrong subclaim".to_string()));
211 };
212
213 Ok(())
214 }
215
216 fn test_permutation_check(nv: usize) -> Result<(), PolyIOPErrors> {
217 let mut rng = test_rng();
218
219 let srs = MultilinearKzgPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, nv + 1)?;
220 let (pcs_param, _) = MultilinearKzgPCS::<Bls12_381>::trim(&srs, nv + 1, Some(nv + 1))?;
221
222 {
223 // good path: w is a permutation of w itself under the identify map
224 let w = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
225 // s_perm is the identity map
226 let s_perm = identity_permutation_mle(nv);
227 test_permutation_check_helper::<Bls12_381, KZG>(&pcs_param, &w, &w, &s_perm)?;
228 }
229
230 {
231 // bad path 1: w is a not permutation of w itself under a random map
232 let w = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
233 // s_perm is a random map
234 let s_perm = random_permutation_mle(nv, &mut rng);
235
236 if nv == 1 {
237 test_permutation_check_helper::<Bls12_381, KZG>(&pcs_param, &w, &w, &s_perm)?;
238 } else {
239 assert!(test_permutation_check_helper::<Bls12_381, KZG>(
240 &pcs_param, &w, &w, &s_perm
241 )
242 .is_err());
243 }
244 }
245
246 {
247 // bad path 2: f is a not permutation of g under a identity map
248 let f = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
249 let g = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng));
250 // s_perm is the identity map
251 let s_perm = identity_permutation_mle(nv);
252
253 assert!(
254 test_permutation_check_helper::<Bls12_381, KZG>(&pcs_param, &f, &g, &s_perm)
255 .is_err()
256 );
257 }
258
259 Ok(())
260 }
261
262 #[test]
263 fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> {
264 test_permutation_check(1)
265 }
266 #[test]
267 fn test_normal_polynomial() -> Result<(), PolyIOPErrors> {
268 test_permutation_check(5)
269 }
270
271 #[test]
272 fn zero_polynomial_should_error() -> Result<(), PolyIOPErrors> {
273 assert!(test_permutation_check(0).is_err());
274 Ok(())
275 }
276}