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