binius_core/protocols/sumcheck/
verify_zerocheck.rs1use binius_field::{TowerField, util::inner_product_unchecked};
4use binius_math::{BinarySubspace, CompositionPoly, EvaluationDomain};
5use binius_utils::{bail, checked_arithmetics::log2_ceil_usize, sorting::is_sorted_ascending};
6use tracing::instrument;
7
8use super::{
9 BatchSumcheckOutput,
10 eq_ind::{self, ClaimsSortingOrder},
11 error::{Error, VerificationError},
12 front_loaded,
13 zerocheck::{self, BatchZerocheckOutput, ZerocheckClaim, univariatizing_reduction_claim},
14};
15use crate::{
16 fiat_shamir::{CanSample, Challenger},
17 transcript::VerifierTranscript,
18};
19
20pub const fn domain_size(composition_degree: usize, skip_rounds: usize) -> usize {
27 composition_degree << skip_rounds
28}
29
30pub const fn extrapolated_scalars_count(composition_degree: usize, skip_rounds: usize) -> usize {
32 composition_degree.saturating_sub(1) << skip_rounds
33}
34
35#[instrument(skip_all, level = "debug")]
53pub fn batch_verify<F, Composition, Challenger_>(
54 claims: &[ZerocheckClaim<F, Composition>],
55 skip_rounds: usize,
56 transcript: &mut VerifierTranscript<Challenger_>,
57) -> Result<BatchZerocheckOutput<F>, Error>
58where
59 F: TowerField,
60 Composition: CompositionPoly<F> + Clone,
61 Challenger_: Challenger,
62{
63 if !is_sorted_ascending(claims.iter().map(|claim| claim.n_vars())) {
65 bail!(Error::ClaimsOutOfOrder);
66 }
67
68 let max_n_vars = claims.last().map(|claim| claim.n_vars()).unwrap_or(0);
69
70 if max_n_vars < skip_rounds {
71 bail!(VerificationError::IncorrectSkippedRoundsCount);
72 }
73
74 let eq_ind_challenges = transcript.sample_vec(max_n_vars - skip_rounds);
76
77 let max_domain_size = claims
79 .iter()
80 .map(|claim| domain_size(claim.max_individual_degree(), skip_rounds))
81 .max()
82 .unwrap_or(0);
83 let zeros_prefix_len = (1 << skip_rounds).min(max_domain_size);
84
85 let mut batch_coeffs = Vec::with_capacity(claims.len());
87 for _claim in claims {
88 let next_batch_coeff = transcript.sample();
89 batch_coeffs.push(next_batch_coeff);
90 }
91
92 let round_evals = transcript
95 .message()
96 .read_scalar_slice(max_domain_size - zeros_prefix_len)?;
97 let univariate_challenge = transcript.sample();
98
99 let max_dim = log2_ceil_usize(max_domain_size);
102 let subspace = BinarySubspace::<F::Canonical>::with_dim(max_dim)?.isomorphic::<F>();
103 let evaluation_domain = EvaluationDomain::from_points(
104 subspace.iter().take(max_domain_size).collect::<Vec<_>>(),
105 false,
106 )?;
107
108 let lagrange_coeffs = evaluation_domain.lagrange_evals(univariate_challenge);
109 let sum = inner_product_unchecked::<F, F>(
110 round_evals,
111 lagrange_coeffs[zeros_prefix_len..].iter().copied(),
112 );
113
114 let eq_ind_sumcheck_claims = zerocheck::reduce_to_eq_ind_sumchecks(skip_rounds, claims)?;
116 let sumcheck_claims = eq_ind::reduce_to_regular_sumchecks(&eq_ind_sumcheck_claims)?;
117
118 let batch_sumcheck_verifier =
119 front_loaded::BatchVerifier::new_prebatched(batch_coeffs, sum, &sumcheck_claims)?;
120
121 let mut sumcheck_output = batch_sumcheck_verifier.run(transcript)?;
122
123 sumcheck_output.challenges.reverse();
125
126 let eq_ind_output = eq_ind::verify_sumcheck_outputs(
127 ClaimsSortingOrder::AscendingVars,
128 &eq_ind_sumcheck_claims,
129 &eq_ind_challenges,
130 sumcheck_output,
131 )?;
132
133 let reduction_claim =
135 univariatizing_reduction_claim(skip_rounds, &eq_ind_output.multilinear_evals)?;
136
137 let univariatize_verifier =
138 front_loaded::BatchVerifier::new(&[reduction_claim.clone()], transcript)?;
139 let mut reduction_sumcheck_output = univariatize_verifier.run(transcript)?;
140
141 reduction_sumcheck_output.challenges.reverse();
143
144 let BatchSumcheckOutput {
145 challenges: skipped_challenges,
146 multilinear_evals: mut concat_multilinear_evals,
147 } = zerocheck::verify_reduction_sumcheck_output(
148 &reduction_claim,
149 skip_rounds,
150 univariate_challenge,
151 reduction_sumcheck_output,
152 )?;
153
154 let concat_multilinear_evals = concat_multilinear_evals
155 .pop()
156 .expect("multilinear_evals.len() == 1");
157
158 let output = BatchZerocheckOutput {
160 skipped_challenges,
161 unskipped_challenges: eq_ind_output.challenges,
162 concat_multilinear_evals,
163 };
164
165 Ok(output)
166}