use std::{hash::Hash, iter};
use ff::{FromUniformBytes, WithSmallOrderMulGroup};
use super::{vanishing, Error, VerifyingKey};
use crate::{
plonk::{evaluate_identities, traces::VerifierTrace},
poly::{commitment::PolynomialCommitmentScheme, CommitmentLabel, VerifierQuery},
transcript::{read_n, Hashable, Sampleable, Transcript},
utils::arithmetic::compute_inner_product,
};
pub fn parse_trace<F, CS, T>(
vk: &VerifyingKey<F, CS>,
#[cfg(feature = "committed-instances")] committed_instances: &[&[CS::Commitment]],
instances: &[&[&[F]]],
transcript: &mut T,
) -> Result<VerifierTrace<F, CS>, Error>
where
CS: PolynomialCommitmentScheme<F>,
T: Transcript,
F: WithSmallOrderMulGroup<3>
+ Hashable<T::Hash>
+ Sampleable<T::Hash>
+ FromUniformBytes<64>
+ Ord,
CS::Commitment: Hashable<T::Hash>,
{
#[cfg(not(feature = "committed-instances"))]
let committed_instances: Vec<Vec<CS::Commitment>> = vec![vec![]; instances.len()];
if committed_instances.is_empty() {
return Err(Error::InvalidInstances);
}
let nb_committed_instances = committed_instances[0].len();
for committed_instances in committed_instances.iter() {
if committed_instances.len() != nb_committed_instances {
return Err(Error::InvalidInstances);
}
}
for (committed_instances, instances) in committed_instances.iter().zip(instances.iter()) {
if committed_instances.len() + instances.len() != vk.cs.num_instance_columns {
return Err(Error::InvalidInstances);
}
}
let num_proofs = instances.len();
vk.hash_into(transcript)?;
for committed_instances in committed_instances.iter() {
for commitment in committed_instances.iter() {
transcript.common(commitment)?
}
}
for instance in instances.iter() {
for instance in instance.iter() {
transcript.common(&F::from_u128(instance.len() as u128))?;
for value in instance.iter() {
transcript.common(value)?;
}
}
}
let (advice_commitments, challenges) = {
let mut advice_commitments =
vec![vec![CS::Commitment::default(); vk.cs.num_advice_columns]; num_proofs];
let mut challenges = vec![F::ZERO; vk.cs.num_challenges];
for current_phase in vk.cs.phases() {
for advice_commitments in advice_commitments.iter_mut() {
for (phase, commitment) in
vk.cs.advice_column_phase.iter().zip(advice_commitments.iter_mut())
{
if current_phase == *phase {
*commitment = transcript.read()?;
}
}
}
for (phase, challenge) in vk.cs.challenge_phase.iter().zip(challenges.iter_mut()) {
if current_phase == *phase {
*challenge = transcript.squeeze_challenge();
}
}
}
(advice_commitments, challenges)
};
let theta: F = transcript.squeeze_challenge();
let lookups_permuted = (0..num_proofs)
.map(|_| -> Result<Vec<_>, _> {
vk.cs
.lookups
.iter()
.map(|argument| argument.read_permuted_commitments(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
let beta: F = transcript.squeeze_challenge();
let gamma: F = transcript.squeeze_challenge();
let permutations_committed = (0..num_proofs)
.map(|_| {
vk.cs.permutation.read_product_commitments(vk, transcript)
})
.collect::<Result<Vec<_>, _>>()?;
let lookups_committed = lookups_permuted
.into_iter()
.map(|lookups| {
lookups
.into_iter()
.map(|lookup| lookup.read_product_commitment(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
let trash_challenge: F = transcript.squeeze_challenge();
let trashcans_committed = (0..num_proofs)
.map(|_| -> Result<Vec<_>, _> {
vk.cs
.trashcans
.iter()
.map(|argument| argument.read_committed::<CS, _>(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
let vanishing = vanishing::Argument::read_commitments_before_y(transcript)?;
let y: F = transcript.squeeze_challenge();
Ok(VerifierTrace {
advice_commitments,
vanishing,
lookups: lookups_committed,
trashcans: trashcans_committed,
permutations: permutations_committed,
challenges,
beta,
gamma,
theta,
trash_challenge,
y,
})
}
pub fn verify_algebraic_constraints<F, CS: PolynomialCommitmentScheme<F>, T: Transcript>(
vk: &VerifyingKey<F, CS>,
trace: VerifierTrace<F, CS>,
#[cfg(feature = "committed-instances")] committed_instances: &[&[CS::Commitment]],
instances: &[&[&[F]]],
transcript: &mut T,
) -> Result<CS::VerificationGuard, Error>
where
F: WithSmallOrderMulGroup<3>
+ Hashable<T::Hash>
+ Sampleable<T::Hash>
+ FromUniformBytes<64>
+ Hash
+ Ord,
CS::Commitment: Hashable<T::Hash>,
{
#[cfg(not(feature = "committed-instances"))]
let committed_instances: Vec<Vec<CS::Commitment>> = vec![vec![]; instances.len()];
if committed_instances.is_empty() {
return Err(Error::InvalidInstances);
}
let nb_committed_instances = committed_instances[0].len();
let num_proofs = instances.len();
let VerifierTrace {
advice_commitments,
vanishing,
lookups,
trashcans,
permutations,
challenges,
beta,
gamma,
theta,
trash_challenge,
y,
} = trace;
let vanishing = vanishing.read_commitments_after_y(vk, transcript)?;
let x: F = transcript.squeeze_challenge();
let xn = x.pow_vartime([vk.n()]);
let instance_evals = {
let (min_rotation, max_rotation) =
vk.cs.instance_queries.iter().fold((0, 0), |(min, max), (_, rotation)| {
if rotation.0 < min {
(rotation.0, max)
} else if rotation.0 > max {
(min, rotation.0)
} else {
(min, max)
}
});
let max_instance_len = instances
.iter()
.flat_map(|instance| instance.iter().map(|instance| instance.len()))
.max_by(Ord::cmp)
.unwrap_or_default();
let l_i_s = &vk.domain.l_i_range(
x,
xn,
-max_rotation..max_instance_len as i32 + min_rotation.abs(),
);
instances
.iter()
.map(|instances| {
vk.cs
.instance_queries
.iter()
.map(|(column, rotation)| {
if column.index() < nb_committed_instances {
transcript.read()
} else {
let instances = instances[column.index() - nb_committed_instances];
let offset = (max_rotation - rotation.0) as usize;
Ok(compute_inner_product(
instances,
&l_i_s[offset..offset + instances.len()],
))
}
})
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?
};
let advice_evals = (0..num_proofs)
.map(|_| -> Result<Vec<_>, _> { read_n(transcript, vk.cs.advice_queries.len()) })
.collect::<Result<Vec<_>, _>>()?;
let fixed_evals = read_n(transcript, vk.cs.fixed_queries.len())?;
let vanishing = vanishing.evaluate_after_x(transcript)?;
let permutations_common = vk.permutation.evaluate(transcript)?;
let permutations_evaluated = permutations
.into_iter()
.map(|permutation| permutation.evaluate(transcript))
.collect::<Result<Vec<_>, _>>()?;
let lookups_evaluated = lookups
.into_iter()
.map(|lookups| -> Result<Vec<_>, _> {
lookups
.into_iter()
.map(|lookup| lookup.evaluate(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
let trashcans_evaluated = trashcans
.into_iter()
.map(|trashcans| -> Result<Vec<_>, _> {
trashcans
.into_iter()
.map(|trash| trash.evaluate(transcript))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<Vec<_>, _>>()?;
let vanishing = evaluate_identities(
vk,
&fixed_evals,
&instance_evals,
&advice_evals,
&permutations_evaluated,
&lookups_evaluated,
&trashcans_evaluated,
&permutations_common,
x,
xn,
beta,
gamma,
theta,
trash_challenge,
&challenges,
y,
vanishing,
);
let queries = committed_instances
.iter()
.zip(instance_evals.iter())
.zip(advice_commitments.iter())
.zip(advice_evals.iter())
.zip(permutations_evaluated.iter())
.zip(lookups_evaluated.iter())
.zip(trashcans_evaluated.iter())
.flat_map(
|(
(
(
(((committed_instances, instance_evals), advice_commitments), advice_evals),
permutation,
),
lookups,
),
trash,
)| {
iter::empty()
.chain(vk.cs.advice_queries.iter().enumerate().map(
move |(query_index, &(column, at))| {
VerifierQuery::new(
vk.domain.rotate_omega(x, at),
CommitmentLabel::Advice(column.index()),
&advice_commitments[column.index()],
advice_evals[query_index],
)
},
))
.chain(vk.cs.instance_queries.iter().enumerate().filter_map(
move |(query_index, &(column, at))| {
if column.index() < nb_committed_instances {
Some(VerifierQuery::new(
vk.domain.rotate_omega(x, at),
CommitmentLabel::Instance(column.index()),
&committed_instances[column.index()],
instance_evals[query_index],
))
} else {
None
}
},
))
.chain(permutation.queries(vk, x))
.chain(lookups.iter().flat_map(move |p| p.queries(vk, x)))
.chain(trash.iter().flat_map(move |p| p.queries(x)))
},
)
.chain(
vk.cs.fixed_queries.iter().enumerate().map(|(query_index, &(column, at))| {
VerifierQuery::new(
vk.domain.rotate_omega(x, at),
CommitmentLabel::Fixed(column.index()),
&vk.fixed_commitments[column.index()],
fixed_evals[query_index],
)
}),
)
.chain(permutations_common.queries(&vk.permutation, x))
.chain(vanishing.queries(x, vk.n()))
.collect::<Vec<_>>();
CS::multi_prepare(&queries, transcript).map_err(|_| Error::Opening)
}
pub fn prepare<F, CS: PolynomialCommitmentScheme<F>, T: Transcript>(
vk: &VerifyingKey<F, CS>,
#[cfg(feature = "committed-instances")] committed_instances: &[&[CS::Commitment]],
instances: &[&[&[F]]],
transcript: &mut T,
) -> Result<CS::VerificationGuard, Error>
where
F: WithSmallOrderMulGroup<3>
+ Hashable<T::Hash>
+ Sampleable<T::Hash>
+ FromUniformBytes<64>
+ Hash
+ Ord,
CS::Commitment: Hashable<T::Hash>,
{
let trace = parse_trace(
vk,
#[cfg(feature = "committed-instances")]
committed_instances,
instances,
transcript,
)?;
verify_algebraic_constraints(
vk,
trace,
#[cfg(feature = "committed-instances")]
committed_instances,
instances,
transcript,
)
}