arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
hyperplonk/poly-iop/src/sum_check/prover.rs
poly-iop/src/sum_check/prover.rsRaw
  1//! Prover subroutines for a SumCheck protocol.
  2
  3use super::SumCheckProver;
  4use crate::{
  5    errors::PolyIOPErrors,
  6    structs::{IOPProverMessage, IOPProverState},
  7};
  8use arithmetic::VirtualPolynomial;
  9use ark_ff::PrimeField;
 10use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
 11use ark_std::{end_timer, start_timer, vec::Vec};
 12use std::rc::Rc;
 13
 14#[cfg(feature = "parallel")]
 15use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
 16
 17impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
 18    type VirtualPolynomial = VirtualPolynomial<F>;
 19    type ProverMessage = IOPProverMessage<F>;
 20
 21    /// Initialize the prover state to argue for the sum of the input polynomial
 22    /// over {0,1}^`num_vars`.
 23    fn prover_init(polynomial: &Self::VirtualPolynomial) -> Result<Self, PolyIOPErrors> {
 24        let start = start_timer!(|| "sum check prover init");
 25        if polynomial.aux_info.num_variables == 0 {
 26            return Err(PolyIOPErrors::InvalidParameters(
 27                "Attempt to prove a constant.".to_string(),
 28            ));
 29        }
 30        end_timer!(start);
 31
 32        Ok(Self {
 33            challenges: Vec::with_capacity(polynomial.aux_info.num_variables),
 34            round: 0,
 35            poly: polynomial.clone(),
 36        })
 37    }
 38
 39    /// Receive message from verifier, generate prover message, and proceed to
 40    /// next round.
 41    ///
 42    /// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).
 43    fn prove_round_and_update_state(
 44        &mut self,
 45        challenge: &Option<F>,
 46    ) -> Result<Self::ProverMessage, PolyIOPErrors> {
 47        let start =
 48            start_timer!(|| format!("sum check prove {}-th round and update state", self.round));
 49
 50        if self.round >= self.poly.aux_info.num_variables {
 51            return Err(PolyIOPErrors::InvalidProver(
 52                "Prover is not active".to_string(),
 53            ));
 54        }
 55
 56        let fix_argument = start_timer!(|| "fix argument");
 57
 58        // Step 1:
 59        // fix argument and evaluate f(x) over x_m = r; where r is the challenge
 60        // for the current round, and m is the round number, indexed from 1
 61        //
 62        // i.e.:
 63        // at round m <= n, for each mle g(x_1, ... x_n) within the flattened_mle
 64        // which has already been evaluated to
 65        //
 66        //    g(r_1, ..., r_{m-1}, x_m ... x_n)
 67        //
 68        // eval g over r_m, and mutate g to g(r_1, ... r_m,, x_{m+1}... x_n)
 69        let mut flattened_ml_extensions: Vec<DenseMultilinearExtension<F>> = self
 70            .poly
 71            .flattened_ml_extensions
 72            .iter()
 73            .map(|x| x.as_ref().clone())
 74            .collect();
 75
 76        if let Some(chal) = challenge {
 77            if self.round == 0 {
 78                return Err(PolyIOPErrors::InvalidProver(
 79                    "first round should be prover first.".to_string(),
 80                ));
 81            }
 82            self.challenges.push(*chal);
 83
 84            let r = self.challenges[self.round - 1];
 85            #[cfg(feature = "parallel")]
 86            flattened_ml_extensions
 87                .par_iter_mut()
 88                .for_each(|mle| *mle = mle.fix_variables(&[r]));
 89
 90            #[cfg(not(feature = "parallel"))]
 91            flattened_ml_extensions
 92                .iter_mut()
 93                .for_each(|mle| *mle = mle.fix_variables(&[r]));
 94        } else if self.round > 0 {
 95            return Err(PolyIOPErrors::InvalidProver(
 96                "verifier message is empty".to_string(),
 97            ));
 98        }
 99        end_timer!(fix_argument);
100
101        self.round += 1;
102
103        let products_list = self.poly.products.clone();
104        let mut products_sum = Vec::with_capacity(self.poly.aux_info.max_degree + 1);
105        products_sum.resize(self.poly.aux_info.max_degree + 1, F::zero());
106
107        let compute_sum = start_timer!(|| "compute sum");
108        // Step 2: generate sum for the partial evaluated polynomial:
109        // f(r_1, ... r_m,, x_{m+1}... x_n)
110
111        #[cfg(feature = "parallel")]
112        products_sum.par_iter_mut().enumerate().for_each(|(t, e)| {
113            for b in 0..1 << (self.poly.aux_info.num_variables - self.round) {
114                // evaluate P_round(t)
115                for (coefficient, products) in products_list.iter() {
116                    let num_mles = products.len();
117                    let mut product = *coefficient;
118                    for &f in products.iter().take(num_mles) {
119                        let table = &flattened_ml_extensions[f]; // f's range is checked in init
120                        product *= table[b << 1] * (F::one() - F::from(t as u64))
121                            + table[(b << 1) + 1] * F::from(t as u64);
122                    }
123                    *e += product;
124                }
125            }
126        });
127
128        #[cfg(not(feature = "parallel"))]
129        products_sum.iter_mut().enumerate().for_each(|(t, e)| {
130            for b in 0..1 << (self.poly.aux_info.num_variables - self.round) {
131                // evaluate P_round(t)
132                for (coefficient, products) in products_list.iter() {
133                    let num_mles = products.len();
134                    let mut product = *coefficient;
135                    for &f in products.iter().take(num_mles) {
136                        let table = &flattened_ml_extensions[f]; // f's range is checked in init
137                        product *= table[b << 1] * (F::one() - F::from(t as u64))
138                            + table[(b << 1) + 1] * F::from(t as u64);
139                    }
140                    *e += product;
141                }
142            }
143        });
144
145        // update prover's state to the partial evaluated polynomial
146        self.poly.flattened_ml_extensions = flattened_ml_extensions
147            .iter()
148            .map(|x| Rc::new(x.clone()))
149            .collect();
150
151        end_timer!(compute_sum);
152        end_timer!(start);
153        Ok(IOPProverMessage {
154            evaluations: products_sum,
155        })
156    }
157}