binius_core/protocols/gkr_exp/
verifiers.rs

1// Copyright 2025 Irreducible Inc.
2
3use binius_field::{BinaryField, Field, PackedField};
4use binius_math::EvaluationOrder;
5use binius_utils::bail;
6
7use super::{
8	common::{ExpClaim, LayerClaim},
9	compositions::{ExpCompositions, IndexedExpComposition},
10	error::Error,
11	utils::first_layer_inverse,
12};
13use crate::{
14	composition::{FixedDimIndexCompositions, IndexComposition},
15	protocols::sumcheck::CompositeSumClaim,
16};
17
18pub trait ExpVerifier<F: Field> {
19	fn exponent_bit_width(&self) -> usize;
20
21	fn is_last_layer(&self, layer_no: usize) -> bool {
22		self.exponent_bit_width() - 1 - layer_no == 0
23	}
24
25	/// return the `eval_point` of the internal [ExpClaim].
26	fn layer_claim_eval_point(&self) -> &[F];
27
28	/// return [CompositeSumClaim] and multilinears that it contains,
29	/// If the verifier does not participate in the sumcheck for this layer,
30	/// the function returns `None`.
31	fn layer_composite_sum_claim(
32		&self,
33		layer_no: usize,
34		composite_claims_n_multilinears: usize,
35		multilinears_index: usize,
36	) -> Result<Option<CompositeSumClaim<F, IndexedExpComposition<F>>>, Error>;
37
38	/// return a tuple of the number of multilinears used by this verifier for this layer.
39	fn layer_n_multilinears(&self, layer_no: usize) -> usize;
40
41	/// return a tuple of the number of sumcheck claims used by this verifier for this layer.
42	fn layer_n_claims(&self, layer_no: usize) -> usize;
43
44	/// update verifier internal [ExpClaim] and return the [LayerClaim]s of multilinears,
45	/// excluding `this_layer_input`.
46	fn finish_layer(
47		&mut self,
48		evaluation_order: EvaluationOrder,
49		layer_no: usize,
50		multilinear_evals: &[F],
51		r: &[F],
52	) -> Vec<LayerClaim<F>>;
53}
54
55pub struct StaticBaseExpVerifier<F: Field>(ExpClaim<F>);
56
57impl<F: Field> StaticBaseExpVerifier<F> {
58	pub fn new(claim: &ExpClaim<F>) -> Result<Self, Error> {
59		if claim.static_base.is_none() {
60			bail!(Error::IncorrectWitnessType);
61		}
62
63		Ok(Self(claim.clone()))
64	}
65}
66
67impl<F> ExpVerifier<F> for StaticBaseExpVerifier<F>
68where
69	F: BinaryField,
70{
71	fn exponent_bit_width(&self) -> usize {
72		self.0.exponent_bit_width
73	}
74
75	fn layer_claim_eval_point(&self) -> &[F] {
76		&self.0.eval_point
77	}
78
79	fn finish_layer(
80		&mut self,
81		evaluation_order: EvaluationOrder,
82		layer_no: usize,
83		multilinear_evals: &[F],
84		r: &[F],
85	) -> Vec<LayerClaim<F>> {
86		let exponent_bit_claim = if self.is_last_layer(layer_no) {
87			// the evaluation of the last exponent bit can be uniquely calculated from the previous
88			// exponentiation layer claim. a_0(x) = (V_0(x) - 1)/(g - 1)
89
90			let base = self.0.static_base.expect("static_base exist");
91
92			LayerClaim {
93				eval_point: self.0.eval_point.clone(),
94				eval: first_layer_inverse(self.0.eval, base),
95			}
96		} else {
97			let n_vars = self.layer_claim_eval_point().len();
98
99			let layer_eval = multilinear_evals[0];
100
101			let exponent_bit_eval = multilinear_evals[1];
102
103			let eval_point = match evaluation_order {
104				EvaluationOrder::LowToHigh => r[r.len() - n_vars..].to_vec(),
105				EvaluationOrder::HighToLow => r[..n_vars].to_vec(),
106			};
107
108			if !self.is_last_layer(layer_no) {
109				self.0.eval = layer_eval;
110				self.0.eval_point = eval_point.clone();
111			}
112
113			LayerClaim {
114				eval: exponent_bit_eval,
115				eval_point,
116			}
117		};
118
119		vec![exponent_bit_claim]
120	}
121
122	fn layer_composite_sum_claim(
123		&self,
124		layer_no: usize,
125		composite_claims_n_multilinears: usize,
126		multilinears_index: usize,
127	) -> Result<Option<CompositeSumClaim<F, IndexedExpComposition<F>>>, Error> {
128		if self.is_last_layer(layer_no) {
129			Ok(None)
130		} else {
131			let internal_layer_index = self.exponent_bit_width() - 1 - layer_no;
132
133			let base_power_static = self
134				.0
135				.static_base
136				.expect("static_base exist")
137				.pow(1 << internal_layer_index);
138
139			let this_layer_input_index = multilinears_index;
140			let exponent_bit_index = multilinears_index + 1;
141
142			let composition = IndexComposition::new(
143				composite_claims_n_multilinears,
144				[this_layer_input_index, exponent_bit_index],
145				ExpCompositions::StaticBase { base_power_static },
146			)?;
147
148			let this_round_composite_claim = CompositeSumClaim {
149				sum: self.0.eval,
150				composition: FixedDimIndexCompositions::Bivariate(composition),
151			};
152
153			Ok(Some(this_round_composite_claim))
154		}
155	}
156
157	fn layer_n_multilinears(&self, layer_no: usize) -> usize {
158		if self.is_last_layer(layer_no) {
159			0
160		} else {
161			// this_layer_input, exponent_bit
162			2
163		}
164	}
165
166	fn layer_n_claims(&self, layer_no: usize) -> usize {
167		if self.is_last_layer(layer_no) { 0 } else { 1 }
168	}
169}
170
171pub struct DynamicExpVerifier<F: Field>(ExpClaim<F>);
172
173impl<F: Field> DynamicExpVerifier<F> {
174	pub fn new(claim: &ExpClaim<F>) -> Result<Self, Error> {
175		if claim.static_base.is_some() {
176			bail!(Error::IncorrectWitnessType);
177		}
178
179		Ok(Self(claim.clone()))
180	}
181}
182
183impl<F: Field> ExpVerifier<F> for DynamicExpVerifier<F> {
184	fn exponent_bit_width(&self) -> usize {
185		self.0.exponent_bit_width
186	}
187
188	fn layer_claim_eval_point(&self) -> &[F] {
189		&self.0.eval_point
190	}
191
192	fn finish_layer(
193		&mut self,
194		evaluation_order: EvaluationOrder,
195		layer_no: usize,
196		multilinear_evals: &[F],
197		r: &[F],
198	) -> Vec<LayerClaim<F>> {
199		let n_vars = self.layer_claim_eval_point().len();
200
201		let eval_point = match evaluation_order {
202			EvaluationOrder::LowToHigh => r[r.len() - n_vars..].to_vec(),
203			EvaluationOrder::HighToLow => r[..n_vars].to_vec(),
204		};
205
206		let mut claims = Vec::with_capacity(2);
207
208		let exponent_bit_eval = multilinear_evals[1];
209
210		let exponent_bit_claim = LayerClaim {
211			eval: exponent_bit_eval,
212			eval_point: eval_point.clone(),
213		};
214
215		claims.push(exponent_bit_claim);
216
217		if self.is_last_layer(layer_no) {
218			let base_eval = multilinear_evals[0];
219
220			let base_claim = LayerClaim {
221				eval: base_eval,
222				eval_point,
223			};
224			claims.push(base_claim)
225		} else {
226			let layer_eval = multilinear_evals[0];
227
228			self.0.eval = layer_eval;
229			self.0.eval_point = eval_point.clone();
230
231			let base_eval = multilinear_evals[2];
232
233			let base_claim = LayerClaim {
234				eval: base_eval,
235				eval_point,
236			};
237
238			claims.push(base_claim)
239		}
240
241		claims
242	}
243
244	fn layer_composite_sum_claim(
245		&self,
246		layer_no: usize,
247		composite_claims_n_multilinears: usize,
248		multilinears_index: usize,
249	) -> Result<Option<CompositeSumClaim<F, IndexedExpComposition<F>>>, Error> {
250		let composition = if self.is_last_layer(layer_no) {
251			let base_index = multilinears_index;
252			let exponent_bit_index = multilinears_index + 1;
253
254			let composition = IndexComposition::new(
255				composite_claims_n_multilinears,
256				[base_index, exponent_bit_index],
257				ExpCompositions::DynamicBaseLastLayer,
258			)?;
259
260			FixedDimIndexCompositions::Bivariate(composition)
261		} else {
262			let this_layer_input_index = multilinears_index;
263			let exponent_bit_index = multilinears_index + 1;
264			let base_index = multilinears_index + 2;
265
266			let composition = IndexComposition::new(
267				composite_claims_n_multilinears,
268				[this_layer_input_index, exponent_bit_index, base_index],
269				ExpCompositions::DynamicBase,
270			)?;
271
272			FixedDimIndexCompositions::Trivariate(composition)
273		};
274
275		let this_round_composite_claim = CompositeSumClaim {
276			sum: self.0.eval,
277			composition,
278		};
279
280		Ok(Some(this_round_composite_claim))
281	}
282
283	fn layer_n_multilinears(&self, layer_no: usize) -> usize {
284		if self.is_last_layer(layer_no) {
285			// base, exponent_bit
286			2
287		} else {
288			// this_layer_input, exponent_bit, base
289			3
290		}
291	}
292
293	fn layer_n_claims(&self, _layer_no: usize) -> usize {
294		1
295	}
296}