zinc_piop/sumcheck/
verifier.rs1use crypto_primitives::{FromPrimitiveWithConfig, PrimeField};
4use zinc_poly::{EvaluatablePolynomial, univariate::nat_evaluation::NatEvaluatedPoly};
5use zinc_transcript::traits::{ConstTranscribable, Transcript};
6use zinc_utils::add;
7
8use crate::sumcheck::prover::{NatEvaluatedPolyWithoutConstant, ProverMsg};
9
10use super::SumCheckError;
11
12pub const SQUEEZE_NATIVE_ELEMENTS_NUM: usize = 1;
13
14pub struct VerifierState<F: PrimeField> {
16 pub round: usize,
18 pub nv: usize,
21 pub max_multiplicands: usize,
23 pub finished: bool,
25 pub polynomials_received: Vec<NatEvaluatedPolyWithoutConstant<F>>,
28 pub randomness: Vec<F>,
31 pub config: F::Config,
34}
35
36impl<F: PrimeField> VerifierState<F> {
37 pub fn new(nvars: usize, degree: usize, config: &F::Config) -> Self {
39 Self {
40 round: 1,
41 nv: nvars,
42 max_multiplicands: degree,
43 finished: false,
44 polynomials_received: Vec::with_capacity(nvars),
45 randomness: Vec::with_capacity(nvars),
46 config: config.clone(),
47 }
48 }
49}
50
51#[derive(Clone, Debug)]
53pub struct Subclaim<F> {
54 pub point: Vec<F>,
57 pub expected_evaluation: F,
59}
60
61impl<F: FromPrimitiveWithConfig> VerifierState<F> {
62 pub fn verify_round(&mut self, prover_msg: &ProverMsg<F>, transcript: &mut impl Transcript) -> F
67 where
68 F::Inner: ConstTranscribable,
69 {
70 let challenge: F = transcript.get_field_challenge(&self.config);
71 self.verify_round_with_challenge(prover_msg, challenge.clone());
72 challenge
73 }
74
75 pub fn verify_round_with_challenge(&mut self, prover_msg: &ProverMsg<F>, challenge: F) {
80 if self.finished {
81 panic!("Incorrect verifier state: Verifier is already finished.");
82 }
83
84 self.randomness.push(challenge);
88 self.polynomials_received.push(prover_msg.0.clone());
89
90 if self.round == self.nv {
95 self.finished = true;
97 } else {
98 self.round = add!(self.round, 1);
99 }
100 }
101
102 #[allow(clippy::arithmetic_side_effects)]
109 pub fn check_and_generate_subclaim(
110 self,
111 asserted_sum: F,
112 ) -> Result<Subclaim<F>, SumCheckError<F>> {
113 if !self.finished {
114 panic!("Verifier has not finished.");
115 }
116
117 let mut expected = asserted_sum;
118 if self.polynomials_received.len() != self.nv {
119 panic!("insufficient rounds");
120 }
121 for (i, evaluations_without_constant) in self.polynomials_received.iter().enumerate() {
122 let expected_len = if self.max_multiplicands == 0 {
123 0
124 } else {
125 self.max_multiplicands
126 };
127 if evaluations_without_constant.len() != expected_len {
128 return Err(SumCheckError::MaxDegreeExceeded);
129 }
130
131 let constant_term = if self.max_multiplicands == 0 {
132 expected.clone()
133 } else {
134 let p1 = evaluations_without_constant
135 .first()
136 .expect("degree > 0 implies the polynomial has an evaluation at 1");
137 expected.clone() - p1.clone()
138 };
139 let mut reconstructed_evaluations =
140 Vec::with_capacity(add!(evaluations_without_constant.len(), 1));
141 reconstructed_evaluations.push(constant_term);
142 reconstructed_evaluations.extend_from_slice(evaluations_without_constant);
143
144 let reconstructed_poly = NatEvaluatedPoly::new(reconstructed_evaluations);
145 expected = reconstructed_poly.evaluate_at_point(&self.randomness[i])?;
146 }
147
148 Ok(Subclaim {
149 point: self.randomness,
150 expected_evaluation: expected,
151 })
152 }
153}