arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
hyperplonk/poly-iop/src/zero_check/mod.rs
poly-iop/src/zero_check/mod.rsRaw
  1//! Main module for the ZeroCheck protocol.
  2
  3use crate::{errors::PolyIOPErrors, sum_check::SumCheck, PolyIOP};
  4use ark_ff::PrimeField;
  5use ark_std::{end_timer, start_timer};
  6use transcript::IOPTranscript;
  7
  8/// A zero check IOP subclaim for \hat f(x) is 0, consists of the following:
  9///   - the SubClaim from the SumCheck
 10///   - the initial challenge r which is used to build eq(x, r) in ZeroCheck
 11#[derive(Clone, Debug, Default, PartialEq)]
 12pub struct ZeroCheckSubClaim<F: PrimeField, SC: SumCheck<F>> {
 13    // the SubClaim from the SumCheck
 14    pub sum_check_sub_claim: SC::SumCheckSubClaim,
 15    // the initial challenge r which is used to build eq(x, r)
 16    pub init_challenge: Vec<F>,
 17}
 18
 19/// A ZeroCheck is derived from SumCheck.
 20pub trait ZeroCheck<F: PrimeField>: SumCheck<F> {
 21    type ZeroCheckSubClaim;
 22    type ZeroCheckProof;
 23
 24    /// Initialize the system with a transcript
 25    ///
 26    /// This function is optional -- in the case where a ZeroCheck is
 27    /// an building block for a more complex protocol, the transcript
 28    /// may be initialized by this complex protocol, and passed to the
 29    /// ZeroCheck prover/verifier.
 30    fn init_transcript() -> Self::Transcript;
 31
 32    /// initialize the prover to argue for the sum of polynomial over
 33    /// {0,1}^`num_vars` is zero.
 34    fn prove(
 35        poly: &Self::VirtualPolynomial,
 36        transcript: &mut Self::Transcript,
 37    ) -> Result<Self::ZeroCheckProof, PolyIOPErrors>;
 38
 39    /// verify the claimed sum using the proof
 40    fn verify(
 41        proof: &Self::ZeroCheckProof,
 42        aux_info: &Self::VPAuxInfo,
 43        transcript: &mut Self::Transcript,
 44    ) -> Result<Self::ZeroCheckSubClaim, PolyIOPErrors>;
 45}
 46
 47impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
 48    /// A ZeroCheck SubClaim consists of
 49    /// - the SubClaim from the SumCheck
 50    /// - the initial challenge r which is used to build eq(x, r)
 51    type ZeroCheckSubClaim = ZeroCheckSubClaim<F, Self>;
 52    /// A ZeroCheckProof is a SumCheckProof
 53    type ZeroCheckProof = Self::SumCheckProof;
 54
 55    /// Initialize the system with a transcript
 56    ///
 57    /// This function is optional -- in the case where a ZeroCheck is
 58    /// an building block for a more complex protocol, the transcript
 59    /// may be initialized by this complex protocol, and passed to the
 60    /// ZeroCheck prover/verifier.
 61    fn init_transcript() -> Self::Transcript {
 62        IOPTranscript::<F>::new(b"Initializing ZeroCheck transcript")
 63    }
 64
 65    /// Initialize the prover to argue for the sum of polynomial f(x) over
 66    /// {0,1}^`num_vars` is zero.
 67    ///
 68    /// f(x) is zero if \hat f(x) := f(x) * eq(x,r) is also a zero polynomial
 69    /// for a random r sampled from transcript.
 70    ///
 71    /// This function will build the \hat f(x) and then invoke the sumcheck
 72    /// protocol to generate a proof for which the sum of \hat f(x) is zero
 73    fn prove(
 74        poly: &Self::VirtualPolynomial,
 75        transcript: &mut Self::Transcript,
 76    ) -> Result<Self::ZeroCheckProof, PolyIOPErrors> {
 77        let start = start_timer!(|| "zero check prove");
 78
 79        let length = poly.aux_info.num_variables;
 80        let r = transcript.get_and_append_challenge_vectors(b"0check r", length)?;
 81        let f_hat = poly.build_f_hat(r.as_ref())?;
 82        let res = <Self as SumCheck<F>>::prove(&f_hat, transcript);
 83
 84        end_timer!(start);
 85        res
 86    }
 87
 88    /// Verify that the polynomial's sum is zero using the proof.
 89    /// Return a Self::Subclaim that consists of the
 90    ///
 91    /// - a Subclaim that the sum is zero
 92    /// - the initial challenge `r` that is used to build `eq(x, r)`
 93    ///
 94    /// This function will check that \hat f(x)'s sum is zero. It does not check
 95    /// `\hat f(x)` is build correctly. The caller needs to makes sure that
 96    /// `\hat f(x) = f(x) * eq(x, r)`
 97    fn verify(
 98        proof: &Self::ZeroCheckProof,
 99        fx_aux_info: &Self::VPAuxInfo,
100        transcript: &mut Self::Transcript,
101    ) -> Result<Self::ZeroCheckSubClaim, PolyIOPErrors> {
102        let start = start_timer!(|| "zero check verify");
103
104        // check that the sum is zero
105        if proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1] != F::zero() {
106            return Err(PolyIOPErrors::InvalidProof(format!(
107                "zero check: sum {} is not zero",
108                proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1]
109            )));
110        }
111
112        // generate `r` and pass it to the caller for correctness check
113        let length = fx_aux_info.num_variables;
114        let r = transcript.get_and_append_challenge_vectors(b"0check r", length)?;
115
116        // hat_fx's max degree is increased by eq(x, r).degree() which is 1
117        let mut hat_fx_aux_info = fx_aux_info.clone();
118        hat_fx_aux_info.max_degree += 1;
119        let subclaim =
120            <Self as SumCheck<F>>::verify(F::zero(), proof, &hat_fx_aux_info, transcript)?;
121
122        end_timer!(start);
123        Ok(ZeroCheckSubClaim {
124            sum_check_sub_claim: subclaim,
125            init_challenge: r,
126        })
127    }
128}
129
130#[cfg(test)]
131mod test {
132
133    use super::ZeroCheck;
134    use crate::{errors::PolyIOPErrors, PolyIOP};
135    use arithmetic::VirtualPolynomial;
136    use ark_bls12_381::Fr;
137    use ark_std::test_rng;
138
139    fn test_zerocheck(
140        nv: usize,
141        num_multiplicands_range: (usize, usize),
142        num_products: usize,
143    ) -> Result<(), PolyIOPErrors> {
144        let mut rng = test_rng();
145
146        {
147            // good path: zero virtual poly
148            let poly =
149                VirtualPolynomial::rand_zero(nv, num_multiplicands_range, num_products, &mut rng)?;
150
151            let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
152            transcript.append_message(b"testing", b"initializing transcript for testing")?;
153            let proof = <PolyIOP<Fr> as ZeroCheck<Fr>>::prove(&poly, &mut transcript)?;
154
155            let poly_info = poly.aux_info.clone();
156            let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
157            transcript.append_message(b"testing", b"initializing transcript for testing")?;
158            let subclaim =
159                <PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?
160                    .sum_check_sub_claim;
161            assert!(
162                poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
163                "wrong subclaim"
164            );
165        }
166
167        {
168            // bad path: random virtual poly whose sum is not zero
169            let (poly, _sum) =
170                VirtualPolynomial::<Fr>::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
171
172            let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
173            transcript.append_message(b"testing", b"initializing transcript for testing")?;
174            let proof = <PolyIOP<Fr> as ZeroCheck<Fr>>::prove(&poly, &mut transcript)?;
175
176            let poly_info = poly.aux_info.clone();
177            let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
178            transcript.append_message(b"testing", b"initializing transcript for testing")?;
179
180            assert!(
181                <PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)
182                    .is_err()
183            );
184        }
185
186        Ok(())
187    }
188
189    #[test]
190    fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> {
191        let nv = 1;
192        let num_multiplicands_range = (4, 5);
193        let num_products = 1;
194
195        test_zerocheck(nv, num_multiplicands_range, num_products)
196    }
197    #[test]
198    fn test_normal_polynomial() -> Result<(), PolyIOPErrors> {
199        let nv = 5;
200        let num_multiplicands_range = (4, 9);
201        let num_products = 5;
202
203        test_zerocheck(nv, num_multiplicands_range, num_products)
204    }
205
206    #[test]
207    fn zero_polynomial_should_error() -> Result<(), PolyIOPErrors> {
208        let nv = 0;
209        let num_multiplicands_range = (4, 13);
210        let num_products = 5;
211
212        assert!(test_zerocheck(nv, num_multiplicands_range, num_products).is_err());
213        Ok(())
214    }
215}