zinc_piop/sumcheck/
verifier.rs1use crypto_primitives::{BaseFieldConfig, ProjectPrimitiveIntegersWithConfig, SetConfig};
4use zinc_poly::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<C: SetConfig> {
16 pub round: usize,
18 pub nv: usize,
21 pub max_multiplicands: usize,
23 pub finished: bool,
25 pub polynomials_received: Vec<NatEvaluatedPolyWithoutConstant<C::Element>>,
28 pub randomness: Vec<C::Element>,
31 pub config: C,
34}
35
36impl<C: SetConfig> VerifierState<C> {
37 pub fn new(nvars: usize, degree: usize, config: &C) -> 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<C: BaseFieldConfig + ProjectPrimitiveIntegersWithConfig> VerifierState<C> {
62 pub fn verify_round(
67 &mut self,
68 prover_msg: &ProverMsg<C::Element>,
69 transcript: &mut impl Transcript,
70 ) -> C::Element
71 where
72 C::Integer: ConstTranscribable,
73 {
74 let challenge: C::Element = transcript.get_field_challenge(&self.config);
75 self.verify_round_with_challenge(prover_msg, challenge.clone());
76 challenge
77 }
78
79 pub fn verify_round_with_challenge(
84 &mut self,
85 prover_msg: &ProverMsg<C::Element>,
86 challenge: C::Element,
87 ) {
88 if self.finished {
89 panic!("Incorrect verifier state: Verifier is already finished.");
90 }
91
92 self.randomness.push(challenge);
96 self.polynomials_received.push(prover_msg.0.clone());
97
98 if self.round == self.nv {
103 self.finished = true;
105 } else {
106 self.round = add!(self.round, 1);
107 }
108 }
109
110 #[allow(clippy::arithmetic_side_effects)]
117 pub fn check_and_generate_subclaim(
118 self,
119 asserted_sum: C::Element,
120 ) -> Result<Subclaim<C::Element>, SumCheckError<C::Element>> {
121 if !self.finished {
122 panic!("Verifier has not finished.");
123 }
124
125 let mut expected = asserted_sum;
126 if self.polynomials_received.len() != self.nv {
127 panic!("insufficient rounds");
128 }
129 for (i, evaluations_without_constant) in self.polynomials_received.iter().enumerate() {
130 let expected_len = if self.max_multiplicands == 0 {
131 0
132 } else {
133 self.max_multiplicands
134 };
135 if evaluations_without_constant.len() != expected_len {
136 return Err(SumCheckError::MaxDegreeExceeded);
137 }
138
139 let constant_term = if self.max_multiplicands == 0 {
140 expected.clone()
141 } else {
142 let p1 = evaluations_without_constant
143 .first()
144 .expect("degree > 0 implies the polynomial has an evaluation at 1");
145 self.config.sub(&expected, p1)
146 };
147 let mut reconstructed_evaluations =
148 Vec::with_capacity(add!(evaluations_without_constant.len(), 1));
149 reconstructed_evaluations.push(constant_term);
150 reconstructed_evaluations.extend_from_slice(evaluations_without_constant);
151
152 let reconstructed_poly = NatEvaluatedPoly::new(reconstructed_evaluations);
153 expected = reconstructed_poly.evaluate_at_point(&self.config, &self.randomness[i])?;
154 }
155
156 Ok(Subclaim {
157 point: self.randomness,
158 expected_evaluation: expected,
159 })
160 }
161}