binius_core/protocols/evalcheck/
verify.rs1use std::mem;
4
5use binius_field::{util::inner_product_unchecked, TowerField};
6use binius_math::extrapolate_line_scalar;
7use getset::{Getters, MutGetters};
8use tracing::instrument;
9
10use super::{
11 error::{Error, VerificationError},
12 evalcheck::{EvalcheckMultilinearClaim, EvalcheckProof},
13 subclaims::{
14 add_bivariate_sumcheck_to_constraints, add_composite_sumcheck_to_constraints,
15 composite_sumcheck_meta, packed_sumcheck_meta, shifted_sumcheck_meta,
16 },
17};
18use crate::oracle::{
19 ConstraintSet, ConstraintSetBuilder, Error as OracleError, MultilinearOracleSet,
20 MultilinearPolyVariant, OracleId, ProjectionVariant,
21};
22
23#[derive(Getters, MutGetters)]
29pub struct EvalcheckVerifier<'a, F>
30where
31 F: TowerField,
32{
33 pub(crate) oracles: &'a mut MultilinearOracleSet<F>,
34
35 #[getset(get = "pub", get_mut = "pub")]
36 committed_eval_claims: Vec<EvalcheckMultilinearClaim<F>>,
37
38 new_sumcheck_constraints: Vec<ConstraintSetBuilder<F>>,
39}
40
41impl<'a, F: TowerField> EvalcheckVerifier<'a, F> {
42 pub const fn new(oracles: &'a mut MultilinearOracleSet<F>) -> Self {
46 Self {
47 oracles,
48 committed_eval_claims: Vec::new(),
49 new_sumcheck_constraints: Vec::new(),
50 }
51 }
52
53 pub fn take_new_sumcheck_constraints(&mut self) -> Result<Vec<ConstraintSet<F>>, OracleError> {
55 self.new_sumcheck_constraints
56 .iter_mut()
57 .map(|builder| mem::take(builder).build_one(self.oracles))
58 .filter(|constraint| !matches!(constraint, Err(OracleError::EmptyConstraintSet)))
59 .rev()
60 .collect()
61 }
62
63 #[instrument(skip_all, name = "EvalcheckVerifierState::verify", level = "debug")]
67 pub fn verify(
68 &mut self,
69 evalcheck_claims: Vec<EvalcheckMultilinearClaim<F>>,
70 evalcheck_proofs: Vec<EvalcheckProof<F>>,
71 ) -> Result<(), Error> {
72 for (claim, proof) in evalcheck_claims
73 .into_iter()
74 .zip(evalcheck_proofs.into_iter())
75 {
76 self.verify_multilinear(claim, proof)?;
77 }
78
79 Ok(())
80 }
81
82 fn verify_multilinear(
83 &mut self,
84 evalcheck_claim: EvalcheckMultilinearClaim<F>,
85 evalcheck_proof: EvalcheckProof<F>,
86 ) -> Result<(), Error> {
87 let EvalcheckMultilinearClaim {
88 id,
89 eval_point,
90 eval,
91 } = evalcheck_claim;
92
93 let multilinear = self.oracles.oracle(id);
94 match multilinear.variant.clone() {
95 MultilinearPolyVariant::Transparent(inner) => {
96 if evalcheck_proof != EvalcheckProof::Transparent {
97 return Err(VerificationError::SubproofMismatch.into());
98 }
99
100 let actual_eval = inner.poly().evaluate(&eval_point)?;
101 if actual_eval != eval {
102 return Err(VerificationError::IncorrectEvaluation(
103 self.oracles.oracle(id).label(),
104 )
105 .into());
106 }
107 }
108
109 MultilinearPolyVariant::Committed => {
110 if evalcheck_proof != EvalcheckProof::Committed {
111 return Err(VerificationError::SubproofMismatch.into());
112 }
113
114 let claim = EvalcheckMultilinearClaim {
115 id: multilinear.id(),
116 eval_point,
117 eval,
118 };
119
120 self.committed_eval_claims.push(claim);
121 }
122
123 MultilinearPolyVariant::Repeating { id, .. } => {
124 let subproof = match evalcheck_proof {
125 EvalcheckProof::Repeating(subproof) => subproof,
126 _ => return Err(VerificationError::SubproofMismatch.into()),
127 };
128 let n_vars = self.oracles.n_vars(id);
129 let subclaim = EvalcheckMultilinearClaim {
130 id,
131 eval_point: eval_point[..n_vars].into(),
132 eval,
133 };
134
135 self.verify_multilinear(subclaim, *subproof)?;
136 }
137
138 MultilinearPolyVariant::Projected(projected) => {
139 let (id, values) = (projected.id(), projected.values());
140 let eval_point = match projected.projection_variant() {
141 ProjectionVariant::LastVars => {
142 let mut eval_point = eval_point.to_vec();
143 eval_point.extend(values);
144 eval_point
145 }
146 ProjectionVariant::FirstVars => {
147 values.iter().copied().chain(eval_point.to_vec()).collect()
148 }
149 };
150
151 let new_claim = EvalcheckMultilinearClaim {
152 id,
153 eval_point: eval_point.into(),
154 eval,
155 };
156
157 self.verify_multilinear(new_claim, evalcheck_proof)?;
158 }
159
160 MultilinearPolyVariant::Shifted(shifted) => {
161 if evalcheck_proof != EvalcheckProof::Shifted {
162 return Err(VerificationError::SubproofMismatch.into());
163 }
164
165 let meta = shifted_sumcheck_meta(self.oracles, &shifted, &eval_point)?;
166 add_bivariate_sumcheck_to_constraints(
167 meta,
168 &mut self.new_sumcheck_constraints,
169 shifted.block_size(),
170 eval,
171 )
172 }
173
174 MultilinearPolyVariant::Packed(packed) => {
175 if evalcheck_proof != EvalcheckProof::Packed {
176 return Err(VerificationError::SubproofMismatch.into());
177 }
178
179 let meta = packed_sumcheck_meta(self.oracles, &packed, &eval_point)?;
180 add_bivariate_sumcheck_to_constraints(
181 meta,
182 &mut self.new_sumcheck_constraints,
183 packed.log_degree(),
184 eval,
185 )
186 }
187
188 MultilinearPolyVariant::LinearCombination(linear_combination) => {
189 let subproofs = match evalcheck_proof {
190 EvalcheckProof::LinearCombination { subproofs } => subproofs,
191 _ => return Err(VerificationError::SubproofMismatch.into()),
192 };
193
194 if subproofs.len() != linear_combination.n_polys() {
195 return Err(VerificationError::SubproofMismatch.into());
196 }
197
198 let actual_eval = linear_combination.offset()
200 + inner_product_unchecked::<F, F>(
201 subproofs.iter().map(|(eval, _)| *eval),
202 linear_combination.coefficients(),
203 );
204
205 if actual_eval != eval {
206 return Err(VerificationError::IncorrectEvaluation(multilinear.label()).into());
207 }
208
209 subproofs
210 .into_iter()
211 .zip(linear_combination.polys())
212 .try_for_each(|((eval, subproof), suboracle_id)| {
213 self.verify_multilinear_subclaim(eval, subproof, suboracle_id, &eval_point)
214 })?;
215 }
216 MultilinearPolyVariant::ZeroPadded(inner) => {
217 let (inner_eval, subproof) = match evalcheck_proof {
218 EvalcheckProof::ZeroPadded(eval, subproof) => (eval, subproof),
219 _ => return Err(VerificationError::SubproofMismatch.into()),
220 };
221
222 let inner_n_vars = self.oracles.n_vars(inner);
223
224 let (subclaim_eval_point, zs) = eval_point.split_at(inner_n_vars);
225
226 let mut extrapolate_eval = inner_eval;
227
228 for z in zs {
229 extrapolate_eval =
230 extrapolate_line_scalar::<F, F>(F::ZERO, extrapolate_eval, *z);
231 }
232
233 if extrapolate_eval != eval {
234 return Err(VerificationError::IncorrectEvaluation(multilinear.label()).into());
235 }
236
237 self.verify_multilinear_subclaim(
238 inner_eval,
239 *subproof,
240 inner,
241 subclaim_eval_point,
242 )?;
243 }
244 MultilinearPolyVariant::Composite(composition) => {
245 if evalcheck_proof != EvalcheckProof::CompositeMLE {
246 return Err(VerificationError::SubproofMismatch.into());
247 }
248
249 let meta = composite_sumcheck_meta(self.oracles, &eval_point)?;
250 add_composite_sumcheck_to_constraints(
251 meta,
252 &mut self.new_sumcheck_constraints,
253 &composition,
254 eval,
255 )
256 }
257 }
258
259 Ok(())
260 }
261
262 fn verify_multilinear_subclaim(
263 &mut self,
264 eval: F,
265 subproof: EvalcheckProof<F>,
266 oracle_id: OracleId,
267 eval_point: &[F],
268 ) -> Result<(), Error> {
269 let subclaim = EvalcheckMultilinearClaim {
270 id: oracle_id,
271 eval_point: eval_point.into(),
272 eval,
273 };
274 self.verify_multilinear(subclaim, subproof)
275 }
276}