binius_core/protocols/sumcheck/
univariate_zerocheck.rsuse binius_field::{util::inner_product_unchecked, Field, TowerField};
use binius_math::{CompositionPolyOS, EvaluationDomainFactory, IsomorphicEvaluationDomainFactory};
use binius_utils::{bail, sorting::is_sorted_ascending};
use tracing::instrument;
use super::{
error::{Error, VerificationError},
verify::BatchVerifyStart,
zerocheck::ZerocheckClaim,
};
use crate::{fiat_shamir::CanSample, transcript::CanRead};
#[derive(Debug)]
pub struct BatchZerocheckUnivariateOutput<F: Field> {
pub univariate_challenge: F,
pub batch_verify_start: BatchVerifyStart<F>,
}
pub fn domain_size(composition_degree: usize, skip_rounds: usize) -> usize {
composition_degree << skip_rounds
}
pub fn extrapolated_scalars_count(composition_degree: usize, skip_rounds: usize) -> usize {
composition_degree.saturating_sub(1) << skip_rounds
}
#[instrument(skip_all, level = "debug")]
pub fn batch_verify_zerocheck_univariate_round<F, Composition, Transcript>(
claims: &[ZerocheckClaim<F, Composition>],
skip_rounds: usize,
mut transcript: Transcript,
) -> Result<BatchZerocheckUnivariateOutput<F>, Error>
where
F: TowerField,
Composition: CompositionPolyOS<F>,
Transcript: CanRead + CanSample<F>,
{
if !is_sorted_ascending(claims.iter().map(|claim| claim.n_vars()).rev()) {
bail!(Error::ClaimsOutOfOrder);
}
let max_n_vars = claims.first().map(|claim| claim.n_vars()).unwrap_or(0);
let min_n_vars = claims.last().map(|claim| claim.n_vars()).unwrap_or(0);
if max_n_vars - min_n_vars > skip_rounds {
bail!(VerificationError::IncorrectSkippedRoundsCount);
}
let max_domain_size = claims
.iter()
.map(|claim| {
domain_size(claim.max_individual_degree(), skip_rounds + claim.n_vars() - max_n_vars)
})
.max()
.unwrap_or(0);
let zeros_prefix_len = (1 << (skip_rounds + min_n_vars - max_n_vars)).min(max_domain_size);
let mut batch_coeffs = Vec::with_capacity(claims.len());
let mut max_degree = 0;
for claim in claims {
let next_batch_coeff = transcript.sample();
batch_coeffs.push(next_batch_coeff);
max_degree = max_degree.max(claim.max_individual_degree() + 1);
}
let round_evals = transcript.read_scalar_slice(max_domain_size - zeros_prefix_len)?;
let univariate_challenge = transcript.sample();
let evaluation_domain = EvaluationDomainFactory::<F>::create(
&IsomorphicEvaluationDomainFactory::<F::Canonical>::default(),
max_domain_size,
)?;
let lagrange_coeffs = evaluation_domain.lagrange_evals(univariate_challenge);
let sum = inner_product_unchecked::<F, F>(
round_evals,
lagrange_coeffs[zeros_prefix_len..].iter().copied(),
);
let batch_verify_start = BatchVerifyStart {
batch_coeffs,
sum,
max_degree,
skip_rounds,
};
let output = BatchZerocheckUnivariateOutput {
univariate_challenge,
batch_verify_start,
};
Ok(output)
}