ark_marlin/ahp/
prover.rs

1#![allow(non_snake_case)]
2
3use crate::ahp::indexer::*;
4use crate::ahp::verifier::*;
5use crate::ahp::*;
6
7use crate::ahp::constraint_systems::{
8    make_matrices_square_for_prover, pad_input_for_indexer_and_prover, unformat_public_input,
9};
10use crate::{ToString, Vec};
11use ark_ff::{Field, PrimeField, Zero};
12use ark_poly::{
13    univariate::DensePolynomial, EvaluationDomain, Evaluations as EvaluationsOnDomain,
14    GeneralEvaluationDomain, Polynomial, UVPolynomial,
15};
16use ark_relations::r1cs::{
17    ConstraintSynthesizer, ConstraintSystem, OptimizationGoal, SynthesisError,
18};
19use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError};
20use ark_std::rand::RngCore;
21use ark_std::{
22    cfg_into_iter, cfg_iter, cfg_iter_mut,
23    io::{Read, Write},
24};
25
26/// State for the AHP prover.
27pub struct ProverState<'a, F: PrimeField> {
28    formatted_input_assignment: Vec<F>,
29    witness_assignment: Vec<F>,
30    /// Az
31    z_a: Option<Vec<F>>,
32    /// Bz
33    z_b: Option<Vec<F>>,
34    /// query bound b
35    zk_bound: usize,
36
37    w_poly: Option<LabeledPolynomial<F>>,
38    mz_polys: Option<(LabeledPolynomial<F>, LabeledPolynomial<F>)>,
39
40    index: &'a Index<F>,
41
42    /// the random values sent by the verifier in the first round
43    verifier_first_msg: Option<VerifierFirstMsg<F>>,
44
45    /// the blinding polynomial for the first round
46    mask_poly: Option<LabeledPolynomial<F>>,
47
48    /// domain X, sized for the public input
49    domain_x: GeneralEvaluationDomain<F>,
50
51    /// domain H, sized for constraints
52    domain_h: GeneralEvaluationDomain<F>,
53
54    /// domain K, sized for matrix nonzero elements
55    domain_k: GeneralEvaluationDomain<F>,
56}
57
58impl<'a, F: PrimeField> ProverState<'a, F> {
59    /// Get the public input.
60    pub fn public_input(&self) -> Vec<F> {
61        unformat_public_input(&self.formatted_input_assignment)
62    }
63}
64
65/// Each prover message that is not a list of oracles is a list of field elements.
66#[derive(Clone)]
67pub enum ProverMsg<F: Field> {
68    /// Some rounds, the prover sends only oracles. (This is actually the case for all
69    /// rounds in Marlin.)
70    EmptyMessage,
71    /// Otherwise, it's one or more field elements.
72    FieldElements(Vec<F>),
73}
74
75impl<F: Field> ark_ff::ToBytes for ProverMsg<F> {
76    fn write<W: Write>(&self, w: W) -> ark_std::io::Result<()> {
77        match self {
78            ProverMsg::EmptyMessage => Ok(()),
79            ProverMsg::FieldElements(field_elems) => field_elems.write(w),
80        }
81    }
82}
83
84impl<F: Field> CanonicalSerialize for ProverMsg<F> {
85    fn serialize<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> {
86        let res: Option<Vec<F>> = match self {
87            ProverMsg::EmptyMessage => None,
88            ProverMsg::FieldElements(v) => Some(v.clone()),
89        };
90        res.serialize(&mut writer)
91    }
92
93    fn serialized_size(&self) -> usize {
94        let res: Option<Vec<F>> = match self {
95            ProverMsg::EmptyMessage => None,
96            ProverMsg::FieldElements(v) => Some(v.clone()),
97        };
98        res.serialized_size()
99    }
100
101    fn serialize_unchecked<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> {
102        let res: Option<Vec<F>> = match self {
103            ProverMsg::EmptyMessage => None,
104            ProverMsg::FieldElements(v) => Some(v.clone()),
105        };
106        res.serialize_unchecked(&mut writer)
107    }
108
109    fn serialize_uncompressed<W: Write>(&self, mut writer: W) -> Result<(), SerializationError> {
110        let res: Option<Vec<F>> = match self {
111            ProverMsg::EmptyMessage => None,
112            ProverMsg::FieldElements(v) => Some(v.clone()),
113        };
114        res.serialize_uncompressed(&mut writer)
115    }
116
117    fn uncompressed_size(&self) -> usize {
118        let res: Option<Vec<F>> = match self {
119            ProverMsg::EmptyMessage => None,
120            ProverMsg::FieldElements(v) => Some(v.clone()),
121        };
122        res.uncompressed_size()
123    }
124}
125
126impl<F: Field> CanonicalDeserialize for ProverMsg<F> {
127    fn deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
128        let res = Option::<Vec<F>>::deserialize(&mut reader)?;
129
130        if let Some(res) = res {
131            Ok(ProverMsg::FieldElements(res))
132        } else {
133            Ok(ProverMsg::EmptyMessage)
134        }
135    }
136
137    fn deserialize_unchecked<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
138        let res = Option::<Vec<F>>::deserialize_unchecked(&mut reader)?;
139
140        if let Some(res) = res {
141            Ok(ProverMsg::FieldElements(res))
142        } else {
143            Ok(ProverMsg::EmptyMessage)
144        }
145    }
146
147    fn deserialize_uncompressed<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
148        let res = Option::<Vec<F>>::deserialize_uncompressed(&mut reader)?;
149
150        if let Some(res) = res {
151            Ok(ProverMsg::FieldElements(res))
152        } else {
153            Ok(ProverMsg::EmptyMessage)
154        }
155    }
156}
157
158/// The first set of prover oracles.
159pub struct ProverFirstOracles<F: Field> {
160    /// The LDE of `w`.
161    pub w: LabeledPolynomial<F>,
162    /// The LDE of `Az`.
163    pub z_a: LabeledPolynomial<F>,
164    /// The LDE of `Bz`.
165    pub z_b: LabeledPolynomial<F>,
166    /// The sum-check hiding polynomial.
167    pub mask_poly: LabeledPolynomial<F>,
168}
169
170impl<F: Field> ProverFirstOracles<F> {
171    /// Iterate over the polynomials output by the prover in the first round.
172    pub fn iter(&self) -> impl Iterator<Item = &LabeledPolynomial<F>> {
173        vec![&self.w, &self.z_a, &self.z_b, &self.mask_poly].into_iter()
174    }
175}
176
177/// The second set of prover oracles.
178pub struct ProverSecondOracles<F: Field> {
179    /// The polynomial `t` that is produced in the first round.
180    pub t: LabeledPolynomial<F>,
181    /// The polynomial `g` resulting from the first sumcheck.
182    pub g_1: LabeledPolynomial<F>,
183    /// The polynomial `h` resulting from the first sumcheck.
184    pub h_1: LabeledPolynomial<F>,
185}
186
187impl<F: Field> ProverSecondOracles<F> {
188    /// Iterate over the polynomials output by the prover in the second round.
189    pub fn iter(&self) -> impl Iterator<Item = &LabeledPolynomial<F>> {
190        vec![&self.t, &self.g_1, &self.h_1].into_iter()
191    }
192}
193
194/// The third set of prover oracles.
195pub struct ProverThirdOracles<F: Field> {
196    /// The polynomial `g` resulting from the second sumcheck.
197    pub g_2: LabeledPolynomial<F>,
198    /// The polynomial `h` resulting from the second sumcheck.
199    pub h_2: LabeledPolynomial<F>,
200}
201
202impl<F: Field> ProverThirdOracles<F> {
203    /// Iterate over the polynomials output by the prover in the third round.
204    pub fn iter(&self) -> impl Iterator<Item = &LabeledPolynomial<F>> {
205        vec![&self.g_2, &self.h_2].into_iter()
206    }
207}
208
209impl<F: PrimeField> AHPForR1CS<F> {
210    /// Initialize the AHP prover.
211    pub fn prover_init<'a, C: ConstraintSynthesizer<F>>(
212        index: &'a Index<F>,
213        c: C,
214    ) -> Result<ProverState<'a, F>, Error> {
215        let init_time = start_timer!(|| "AHP::Prover::Init");
216
217        let constraint_time = start_timer!(|| "Generating constraints and witnesses");
218        let pcs = ConstraintSystem::new_ref();
219        pcs.set_optimization_goal(OptimizationGoal::Weight);
220        pcs.set_mode(ark_relations::r1cs::SynthesisMode::Prove {
221            construct_matrices: true,
222        });
223        c.generate_constraints(pcs.clone())?;
224        end_timer!(constraint_time);
225
226        let padding_time = start_timer!(|| "Padding matrices to make them square");
227        pad_input_for_indexer_and_prover(pcs.clone());
228        pcs.finalize();
229        make_matrices_square_for_prover(pcs.clone());
230        end_timer!(padding_time);
231
232        let num_non_zero = index.index_info.num_non_zero;
233
234        let (formatted_input_assignment, witness_assignment, num_constraints) = {
235            let pcs = pcs.borrow().unwrap();
236            (
237                pcs.instance_assignment.as_slice().to_vec(),
238                pcs.witness_assignment.as_slice().to_vec(),
239                pcs.num_constraints,
240            )
241        };
242
243        let num_input_variables = formatted_input_assignment.len();
244        let num_witness_variables = witness_assignment.len();
245        if index.index_info.num_constraints != num_constraints
246            || num_input_variables + num_witness_variables != index.index_info.num_variables
247        {
248            return Err(Error::InstanceDoesNotMatchIndex);
249        }
250
251        if !Self::formatted_public_input_is_admissible(&formatted_input_assignment) {
252            return Err(Error::InvalidPublicInputLength);
253        }
254
255        // Perform matrix multiplications
256        let inner_prod_fn = |row: &[(F, usize)]| {
257            let mut acc = F::zero();
258            for &(ref coeff, i) in row {
259                let tmp = if i < num_input_variables {
260                    formatted_input_assignment[i]
261                } else {
262                    witness_assignment[i - num_input_variables]
263                };
264
265                acc += &(if coeff.is_one() { tmp } else { tmp * coeff });
266            }
267            acc
268        };
269
270        let eval_z_a_time = start_timer!(|| "Evaluating z_A");
271        let z_a = index.a.iter().map(|row| inner_prod_fn(row)).collect();
272        end_timer!(eval_z_a_time);
273
274        let eval_z_b_time = start_timer!(|| "Evaluating z_B");
275        let z_b = index.b.iter().map(|row| inner_prod_fn(row)).collect();
276        end_timer!(eval_z_b_time);
277
278        let zk_bound = 1; // One query is sufficient for our desired soundness
279
280        let domain_h = GeneralEvaluationDomain::new(num_constraints)
281            .ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
282
283        let domain_k = GeneralEvaluationDomain::new(num_non_zero)
284            .ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
285
286        let domain_x = GeneralEvaluationDomain::new(num_input_variables)
287            .ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
288
289        end_timer!(init_time);
290
291        Ok(ProverState {
292            formatted_input_assignment,
293            witness_assignment,
294            z_a: Some(z_a),
295            z_b: Some(z_b),
296            w_poly: None,
297            mz_polys: None,
298            zk_bound,
299            index,
300            verifier_first_msg: None,
301            mask_poly: None,
302            domain_h,
303            domain_k,
304            domain_x,
305        })
306    }
307
308    /// Output the first round message and the next state.
309    pub fn prover_first_round<'a, R: RngCore>(
310        mut state: ProverState<'a, F>,
311        rng: &mut R,
312    ) -> Result<(ProverMsg<F>, ProverFirstOracles<F>, ProverState<'a, F>), Error> {
313        let round_time = start_timer!(|| "AHP::Prover::FirstRound");
314        let domain_h = state.domain_h;
315        let zk_bound = state.zk_bound;
316
317        let v_H = domain_h.vanishing_polynomial().into();
318
319        let x_time = start_timer!(|| "Computing x polynomial and evals");
320        let domain_x = state.domain_x;
321        let x_poly = EvaluationsOnDomain::from_vec_and_domain(
322            state.formatted_input_assignment.clone(),
323            domain_x,
324        )
325        .interpolate();
326        let x_evals = domain_h.fft(&x_poly);
327        end_timer!(x_time);
328
329        let ratio = domain_h.size() / domain_x.size();
330
331        let mut w_extended = state.witness_assignment.clone();
332        w_extended.extend(vec![
333            F::zero();
334            domain_h.size()
335                - domain_x.size()
336                - state.witness_assignment.len()
337        ]);
338
339        let w_poly_time = start_timer!(|| "Computing w polynomial");
340        let w_poly_evals = cfg_into_iter!(0..domain_h.size())
341            .map(|k| {
342                if k % ratio == 0 {
343                    F::zero()
344                } else {
345                    w_extended[k - (k / ratio) - 1] - &x_evals[k]
346                }
347            })
348            .collect();
349
350        let w_poly = &EvaluationsOnDomain::from_vec_and_domain(w_poly_evals, domain_h)
351            .interpolate()
352            + &(&DensePolynomial::from_coefficients_slice(&[F::rand(rng)]) * &v_H);
353        let (w_poly, remainder) = w_poly.divide_by_vanishing_poly(domain_x).unwrap();
354        assert!(remainder.is_zero());
355        end_timer!(w_poly_time);
356
357        let z_a_poly_time = start_timer!(|| "Computing z_A polynomial");
358        let z_a = state.z_a.clone().unwrap();
359        let z_a_poly = &EvaluationsOnDomain::from_vec_and_domain(z_a, domain_h).interpolate()
360            + &(&DensePolynomial::from_coefficients_slice(&[F::rand(rng)]) * &v_H);
361        end_timer!(z_a_poly_time);
362
363        let z_b_poly_time = start_timer!(|| "Computing z_B polynomial");
364        let z_b = state.z_b.clone().unwrap();
365        let z_b_poly = &EvaluationsOnDomain::from_vec_and_domain(z_b, domain_h).interpolate()
366            + &(&DensePolynomial::from_coefficients_slice(&[F::rand(rng)]) * &v_H);
367        end_timer!(z_b_poly_time);
368
369        let mask_poly_time = start_timer!(|| "Computing mask polynomial");
370        let mask_poly_degree = 3 * domain_h.size() + 2 * zk_bound - 3;
371        let mut mask_poly = DensePolynomial::rand(mask_poly_degree, rng);
372        let scaled_sigma_1 = (mask_poly.divide_by_vanishing_poly(domain_h).unwrap().1)[0];
373        mask_poly[0] -= &scaled_sigma_1;
374        end_timer!(mask_poly_time);
375
376        let msg = ProverMsg::EmptyMessage;
377
378        assert!(w_poly.degree() < domain_h.size() - domain_x.size() + zk_bound);
379        assert!(z_a_poly.degree() < domain_h.size() + zk_bound);
380        assert!(z_b_poly.degree() < domain_h.size() + zk_bound);
381        assert!(mask_poly.degree() <= 3 * domain_h.size() + 2 * zk_bound - 3);
382
383        let w = LabeledPolynomial::new("w".to_string(), w_poly, None, Some(1));
384        let z_a = LabeledPolynomial::new("z_a".to_string(), z_a_poly, None, Some(1));
385        let z_b = LabeledPolynomial::new("z_b".to_string(), z_b_poly, None, Some(1));
386        let mask_poly =
387            LabeledPolynomial::new("mask_poly".to_string(), mask_poly.clone(), None, None);
388
389        let oracles = ProverFirstOracles {
390            w: w.clone(),
391            z_a: z_a.clone(),
392            z_b: z_b.clone(),
393            mask_poly: mask_poly.clone(),
394        };
395
396        state.w_poly = Some(w);
397        state.mz_polys = Some((z_a, z_b));
398        state.mask_poly = Some(mask_poly);
399        end_timer!(round_time);
400
401        Ok((msg, oracles, state))
402    }
403
404    fn calculate_t<'a>(
405        matrices: impl Iterator<Item = &'a Matrix<F>>,
406        matrix_randomizers: &[F],
407        input_domain: GeneralEvaluationDomain<F>,
408        domain_h: GeneralEvaluationDomain<F>,
409        r_alpha_x_on_h: Vec<F>,
410    ) -> DensePolynomial<F> {
411        let mut t_evals_on_h = vec![F::zero(); domain_h.size()];
412        for (matrix, eta) in matrices.zip(matrix_randomizers) {
413            for (r, row) in matrix.iter().enumerate() {
414                for (coeff, c) in row.iter() {
415                    let index = domain_h.reindex_by_subdomain(input_domain, *c);
416                    t_evals_on_h[index] += *eta * coeff * r_alpha_x_on_h[r];
417                }
418            }
419        }
420        EvaluationsOnDomain::from_vec_and_domain(t_evals_on_h, domain_h).interpolate()
421    }
422
423    /// Output the number of oracles sent by the prover in the first round.
424    pub fn prover_num_first_round_oracles() -> usize {
425        4
426    }
427
428    /// Output the degree bounds of oracles in the first round.
429    pub fn prover_first_round_degree_bounds(
430        _info: &IndexInfo<F>,
431    ) -> impl Iterator<Item = Option<usize>> {
432        vec![None; 4].into_iter()
433    }
434
435    /// Output the second round message and the next state.
436    pub fn prover_second_round<'a, R: RngCore>(
437        ver_message: &VerifierFirstMsg<F>,
438        mut state: ProverState<'a, F>,
439        _r: &mut R,
440    ) -> (ProverMsg<F>, ProverSecondOracles<F>, ProverState<'a, F>) {
441        let round_time = start_timer!(|| "AHP::Prover::SecondRound");
442
443        let domain_h = state.domain_h;
444        let zk_bound = state.zk_bound;
445
446        let mask_poly = state
447            .mask_poly
448            .as_ref()
449            .expect("ProverState should include mask_poly when prover_second_round is called");
450
451        let VerifierFirstMsg {
452            alpha,
453            eta_a,
454            eta_b,
455            eta_c,
456        } = *ver_message;
457
458        let summed_z_m_poly_time = start_timer!(|| "Compute z_m poly");
459        let (z_a_poly, z_b_poly) = state.mz_polys.as_ref().unwrap();
460        let z_c_poly = z_a_poly.polynomial() * z_b_poly.polynomial();
461
462        let mut summed_z_m_coeffs = z_c_poly.coeffs;
463        // Note: Can't combine these two loops, because z_c_poly has 2x the degree
464        // of z_a_poly and z_b_poly, so the second loop gets truncated due to
465        // the `zip`s.
466        cfg_iter_mut!(summed_z_m_coeffs).for_each(|c| *c *= &eta_c);
467        cfg_iter_mut!(summed_z_m_coeffs)
468            .zip(&z_a_poly.polynomial().coeffs)
469            .zip(&z_b_poly.polynomial().coeffs)
470            .for_each(|((c, a), b)| *c += &(eta_a * a + &(eta_b * b)));
471
472        let summed_z_m = DensePolynomial::from_coefficients_vec(summed_z_m_coeffs);
473        end_timer!(summed_z_m_poly_time);
474
475        let r_alpha_x_evals_time = start_timer!(|| "Compute r_alpha_x evals");
476        let r_alpha_x_evals =
477            domain_h.batch_eval_unnormalized_bivariate_lagrange_poly_with_diff_inputs(alpha);
478        end_timer!(r_alpha_x_evals_time);
479
480        let r_alpha_poly_time = start_timer!(|| "Compute r_alpha_x poly");
481        let r_alpha_poly = DensePolynomial::from_coefficients_vec(domain_h.ifft(&r_alpha_x_evals));
482        end_timer!(r_alpha_poly_time);
483
484        let t_poly_time = start_timer!(|| "Compute t poly");
485        let t_poly = Self::calculate_t(
486            vec![&state.index.a, &state.index.b, &state.index.c].into_iter(),
487            &[eta_a, eta_b, eta_c],
488            state.domain_x,
489            state.domain_h,
490            r_alpha_x_evals.to_vec(),
491        );
492        end_timer!(t_poly_time);
493
494        let z_poly_time = start_timer!(|| "Compute z poly");
495
496        let domain_x = GeneralEvaluationDomain::new(state.formatted_input_assignment.len())
497            .ok_or(SynthesisError::PolynomialDegreeTooLarge)
498            .unwrap();
499        let x_poly = EvaluationsOnDomain::from_vec_and_domain(
500            state.formatted_input_assignment.clone(),
501            domain_x,
502        )
503        .interpolate();
504        let w_poly = state.w_poly.as_ref().unwrap();
505        let mut z_poly = w_poly.polynomial().mul_by_vanishing_poly(domain_x);
506        cfg_iter_mut!(z_poly.coeffs)
507            .zip(&x_poly.coeffs)
508            .for_each(|(z, x)| *z += x);
509        assert!(z_poly.degree() < domain_h.size() + zk_bound);
510
511        end_timer!(z_poly_time);
512
513        let q_1_time = start_timer!(|| "Compute q_1 poly");
514
515        let mul_domain_size = *[
516            mask_poly.len(),
517            r_alpha_poly.coeffs.len() + summed_z_m.coeffs.len(),
518            t_poly.coeffs.len() + z_poly.len(),
519        ]
520        .iter()
521        .max()
522        .unwrap();
523        let mul_domain = GeneralEvaluationDomain::new(mul_domain_size)
524            .expect("field is not smooth enough to construct domain");
525        let mut r_alpha_evals = r_alpha_poly.evaluate_over_domain_by_ref(mul_domain);
526        let summed_z_m_evals = summed_z_m.evaluate_over_domain_by_ref(mul_domain);
527        let z_poly_evals = z_poly.evaluate_over_domain_by_ref(mul_domain);
528        let t_poly_m_evals = t_poly.evaluate_over_domain_by_ref(mul_domain);
529
530        cfg_iter_mut!(r_alpha_evals.evals)
531            .zip(&summed_z_m_evals.evals)
532            .zip(&z_poly_evals.evals)
533            .zip(&t_poly_m_evals.evals)
534            .for_each(|(((a, b), &c), d)| {
535                *a *= b;
536                *a -= c * d;
537            });
538        let rhs = r_alpha_evals.interpolate();
539        let q_1 = mask_poly.polynomial() + &rhs;
540        end_timer!(q_1_time);
541
542        let sumcheck_time = start_timer!(|| "Compute sumcheck h and g polys");
543        let (h_1, x_g_1) = q_1.divide_by_vanishing_poly(domain_h).unwrap();
544        let g_1 = DensePolynomial::from_coefficients_slice(&x_g_1.coeffs[1..]);
545        end_timer!(sumcheck_time);
546
547        let msg = ProverMsg::EmptyMessage;
548
549        assert!(g_1.degree() <= domain_h.size() - 2);
550        assert!(h_1.degree() <= 2 * domain_h.size() + 2 * zk_bound - 2);
551
552        let oracles = ProverSecondOracles {
553            t: LabeledPolynomial::new("t".into(), t_poly, None, None),
554            g_1: LabeledPolynomial::new("g_1".into(), g_1, Some(domain_h.size() - 2), Some(1)),
555            h_1: LabeledPolynomial::new("h_1".into(), h_1, None, None),
556        };
557
558        state.w_poly = None;
559        state.verifier_first_msg = Some(*ver_message);
560        end_timer!(round_time);
561
562        (msg, oracles, state)
563    }
564
565    /// Output the number of oracles sent by the prover in the second round.
566    pub fn prover_num_second_round_oracles() -> usize {
567        3
568    }
569
570    /// Output the degree bounds of oracles in the second round.
571    pub fn prover_second_round_degree_bounds(
572        info: &IndexInfo<F>,
573    ) -> impl Iterator<Item = Option<usize>> {
574        let h_domain_size =
575            GeneralEvaluationDomain::<F>::compute_size_of_domain(info.num_constraints).unwrap();
576
577        vec![None, Some(h_domain_size - 2), None].into_iter()
578    }
579
580    /// Output the third round message and the next state.
581    pub fn prover_third_round<'a, R: RngCore>(
582        ver_message: &VerifierSecondMsg<F>,
583        prover_state: ProverState<'a, F>,
584        _r: &mut R,
585    ) -> Result<(ProverMsg<F>, ProverThirdOracles<F>), Error> {
586        let round_time = start_timer!(|| "AHP::Prover::ThirdRound");
587
588        let ProverState {
589            index,
590            verifier_first_msg,
591            domain_h,
592            domain_k,
593            ..
594        } = prover_state;
595
596        let VerifierFirstMsg {
597            eta_a,
598            eta_b,
599            eta_c,
600            alpha,
601        } = verifier_first_msg.expect(
602            "ProverState should include verifier_first_msg when prover_third_round is called",
603        );
604
605        let beta = ver_message.beta;
606
607        let v_H_at_alpha = domain_h.evaluate_vanishing_polynomial(alpha);
608        let v_H_at_beta = domain_h.evaluate_vanishing_polynomial(beta);
609
610        let (a_star, b_star, c_star) = (
611            &index.a_star_arith,
612            &index.b_star_arith,
613            &index.c_star_arith,
614        );
615
616        let f_evals_time = start_timer!(|| "Computing f evals on K");
617        let mut f_vals_on_K = Vec::with_capacity(domain_k.size());
618        let mut inverses_a = Vec::with_capacity(domain_k.size());
619        let mut inverses_b = Vec::with_capacity(domain_k.size());
620        let mut inverses_c = Vec::with_capacity(domain_k.size());
621
622        for i in 0..domain_k.size() {
623            inverses_a.push((beta - a_star.evals_on_K.row[i]) * (alpha - a_star.evals_on_K.col[i]));
624            inverses_b.push((beta - b_star.evals_on_K.row[i]) * (alpha - b_star.evals_on_K.col[i]));
625            inverses_c.push((beta - c_star.evals_on_K.row[i]) * (alpha - c_star.evals_on_K.col[i]));
626        }
627        ark_ff::batch_inversion(&mut inverses_a);
628        ark_ff::batch_inversion(&mut inverses_b);
629        ark_ff::batch_inversion(&mut inverses_c);
630
631        for i in 0..domain_k.size() {
632            let t = eta_a * a_star.evals_on_K.val[i] * inverses_a[i]
633                + eta_b * b_star.evals_on_K.val[i] * inverses_b[i]
634                + eta_c * c_star.evals_on_K.val[i] * inverses_c[i];
635            let f_at_kappa = v_H_at_beta * v_H_at_alpha * t;
636            f_vals_on_K.push(f_at_kappa);
637        }
638        end_timer!(f_evals_time);
639
640        let f_poly_time = start_timer!(|| "Computing f poly");
641        let f = EvaluationsOnDomain::from_vec_and_domain(f_vals_on_K, domain_k).interpolate();
642        end_timer!(f_poly_time);
643
644        let g_2 = DensePolynomial::from_coefficients_slice(&f.coeffs[1..]);
645
646        let domain_b = GeneralEvaluationDomain::<F>::new(3 * domain_k.size() - 3)
647            .ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
648
649        let denom_eval_time = start_timer!(|| "Computing denominator evals on B");
650        let a_denom: Vec<_> = cfg_iter!(a_star.evals_on_B.row.evals)
651            .zip(&a_star.evals_on_B.col.evals)
652            .zip(&a_star.row_col_evals_on_B.evals)
653            .map(|((&r, c), r_c)| beta * alpha - (r * alpha) - (beta * c) + r_c)
654            .collect();
655
656        let b_denom: Vec<_> = cfg_iter!(b_star.evals_on_B.row.evals)
657            .zip(&b_star.evals_on_B.col.evals)
658            .zip(&b_star.row_col_evals_on_B.evals)
659            .map(|((&r, c), r_c)| beta * alpha - (r * alpha) - (beta * c) + r_c)
660            .collect();
661
662        let c_denom: Vec<_> = cfg_iter!(c_star.evals_on_B.row.evals)
663            .zip(&c_star.evals_on_B.col.evals)
664            .zip(&c_star.row_col_evals_on_B.evals)
665            .map(|((&r, c), r_c)| beta * alpha - (r * alpha) - (beta * c) + r_c)
666            .collect();
667        end_timer!(denom_eval_time);
668
669        let a_evals_time = start_timer!(|| "Computing a evals on B");
670        let a_star_evals_on_B = &a_star.evals_on_B;
671        let b_star_evals_on_B = &b_star.evals_on_B;
672        let c_star_evals_on_B = &c_star.evals_on_B;
673        let a_poly_on_B = cfg_into_iter!(0..domain_b.size())
674            .map(|i| {
675                let t = eta_a * a_star_evals_on_B.val.evals[i] * b_denom[i] * c_denom[i]
676                    + eta_b * b_star_evals_on_B.val.evals[i] * a_denom[i] * c_denom[i]
677                    + eta_c * c_star_evals_on_B.val.evals[i] * a_denom[i] * b_denom[i];
678                v_H_at_beta * v_H_at_alpha * t
679            })
680            .collect();
681        end_timer!(a_evals_time);
682
683        let a_poly_time = start_timer!(|| "Computing a poly");
684        let a_poly = EvaluationsOnDomain::from_vec_and_domain(a_poly_on_B, domain_b).interpolate();
685        end_timer!(a_poly_time);
686
687        let b_evals_time = start_timer!(|| "Computing b evals on B");
688        let b_poly_on_B = cfg_into_iter!(0..domain_b.size())
689            .map(|i| a_denom[i] * b_denom[i] * c_denom[i])
690            .collect();
691        end_timer!(b_evals_time);
692
693        let b_poly_time = start_timer!(|| "Computing b poly");
694        let b_poly = EvaluationsOnDomain::from_vec_and_domain(b_poly_on_B, domain_b).interpolate();
695        end_timer!(b_poly_time);
696
697        let h_2_poly_time = start_timer!(|| "Computing sumcheck h poly");
698        let h_2 = (&a_poly - &(&b_poly * &f))
699            .divide_by_vanishing_poly(domain_k)
700            .unwrap()
701            .0;
702        end_timer!(h_2_poly_time);
703
704        let msg = ProverMsg::EmptyMessage;
705
706        assert!(g_2.degree() <= domain_k.size() - 2);
707        let oracles = ProverThirdOracles {
708            g_2: LabeledPolynomial::new("g_2".to_string(), g_2, Some(domain_k.size() - 2), None),
709            h_2: LabeledPolynomial::new("h_2".to_string(), h_2, None, None),
710        };
711        end_timer!(round_time);
712
713        Ok((msg, oracles))
714    }
715
716    /// Output the number of oracles sent by the prover in the third round.
717    pub fn prover_num_third_round_oracles() -> usize {
718        3
719    }
720
721    /// Output the degree bounds of oracles in the third round.
722    pub fn prover_third_round_degree_bounds(
723        info: &IndexInfo<F>,
724    ) -> impl Iterator<Item = Option<usize>> {
725        let num_non_zero = info.num_non_zero;
726        let k_size = GeneralEvaluationDomain::<F>::compute_size_of_domain(num_non_zero).unwrap();
727
728        vec![Some(k_size - 2), None].into_iter()
729    }
730}