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,
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
141 let new_eval_point = {
142 let idx = projected.start_index();
143 let mut new_eval_point = eval_point[0..idx].to_vec();
144 new_eval_point.extend(values.clone());
145 new_eval_point.extend(eval_point[idx..].to_vec());
146 new_eval_point
147 };
148
149 let new_claim = EvalcheckMultilinearClaim {
150 id,
151 eval_point: new_eval_point.into(),
152 eval,
153 };
154
155 self.verify_multilinear(new_claim, evalcheck_proof)?;
156 }
157
158 MultilinearPolyVariant::Shifted(shifted) => {
159 if evalcheck_proof != EvalcheckProof::Shifted {
160 return Err(VerificationError::SubproofMismatch.into());
161 }
162
163 let meta = shifted_sumcheck_meta(self.oracles, &shifted, &eval_point)?;
164 add_bivariate_sumcheck_to_constraints(
165 &meta,
166 &mut self.new_sumcheck_constraints,
167 shifted.block_size(),
168 eval,
169 )
170 }
171
172 MultilinearPolyVariant::Packed(packed) => {
173 if evalcheck_proof != EvalcheckProof::Packed {
174 return Err(VerificationError::SubproofMismatch.into());
175 }
176
177 let meta = packed_sumcheck_meta(self.oracles, &packed, &eval_point)?;
178 add_bivariate_sumcheck_to_constraints(
179 &meta,
180 &mut self.new_sumcheck_constraints,
181 packed.log_degree(),
182 eval,
183 )
184 }
185
186 MultilinearPolyVariant::LinearCombination(linear_combination) => {
187 let subproofs = match evalcheck_proof {
188 EvalcheckProof::LinearCombination { subproofs } => subproofs,
189 _ => return Err(VerificationError::SubproofMismatch.into()),
190 };
191
192 if subproofs.len() != linear_combination.n_polys() {
193 return Err(VerificationError::SubproofMismatch.into());
194 }
195
196 let actual_eval = linear_combination.offset()
198 + inner_product_unchecked::<F, F>(
199 subproofs.iter().map(|(eval, _)| *eval),
200 linear_combination.coefficients(),
201 );
202
203 if actual_eval != eval {
204 return Err(VerificationError::IncorrectEvaluation(multilinear.label()).into());
205 }
206
207 subproofs
208 .into_iter()
209 .zip(linear_combination.polys())
210 .try_for_each(|((eval, subproof), suboracle_id)| {
211 self.verify_multilinear_subclaim(eval, subproof, suboracle_id, &eval_point)
212 })?;
213 }
214 MultilinearPolyVariant::ZeroPadded(inner) => {
215 let (inner_eval, subproof) = match evalcheck_proof {
216 EvalcheckProof::ZeroPadded(eval, subproof) => (eval, subproof),
217 _ => return Err(VerificationError::SubproofMismatch.into()),
218 };
219
220 let inner_n_vars = self.oracles.n_vars(inner);
221
222 let (subclaim_eval_point, zs) = eval_point.split_at(inner_n_vars);
223
224 let mut extrapolate_eval = inner_eval;
225
226 for z in zs {
227 extrapolate_eval =
228 extrapolate_line_scalar::<F, F>(F::ZERO, extrapolate_eval, *z);
229 }
230
231 if extrapolate_eval != eval {
232 return Err(VerificationError::IncorrectEvaluation(multilinear.label()).into());
233 }
234
235 self.verify_multilinear_subclaim(
236 inner_eval,
237 *subproof,
238 inner,
239 subclaim_eval_point,
240 )?;
241 }
242 MultilinearPolyVariant::Composite(composition) => {
243 if evalcheck_proof != EvalcheckProof::CompositeMLE {
244 return Err(VerificationError::SubproofMismatch.into());
245 }
246
247 let meta = composite_sumcheck_meta(self.oracles, &eval_point)?;
248 add_composite_sumcheck_to_constraints(
249 &meta,
250 &mut self.new_sumcheck_constraints,
251 &composition,
252 eval,
253 )
254 }
255 }
256
257 Ok(())
258 }
259
260 fn verify_multilinear_subclaim(
261 &mut self,
262 eval: F,
263 subproof: EvalcheckProof<F>,
264 oracle_id: OracleId,
265 eval_point: &[F],
266 ) -> Result<(), Error> {
267 let subclaim = EvalcheckMultilinearClaim {
268 id: oracle_id,
269 eval_point: eval_point.into(),
270 eval,
271 };
272 self.verify_multilinear(subclaim, subproof)
273 }
274}