mirror of https://github.com/zcash/halo2.git
850 lines
32 KiB
Rust
850 lines
32 KiB
Rust
//! Generate a proof
|
|
|
|
use group::prime::PrimeCurveAffine;
|
|
use group::Curve;
|
|
use rand_core::RngCore;
|
|
use std::collections::{BTreeSet, HashSet};
|
|
use std::{collections::HashMap, iter};
|
|
|
|
use crate::arithmetic::{eval_polynomial, CurveAffine};
|
|
use crate::plonk::{
|
|
lookup, lookup::prover::lookup_commit_permuted, permutation,
|
|
permutation::prover::permutation_commit, shuffle, shuffle::prover::shuffle_commit_product,
|
|
vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX, ChallengeY, Error,
|
|
ProvingKey,
|
|
};
|
|
use crate::poly::{
|
|
commitment::{Blind, CommitmentScheme, Params, Prover},
|
|
Basis, Coeff, LagrangeCoeff, Polynomial, ProverQuery,
|
|
};
|
|
use crate::transcript::{EncodedChallenge, TranscriptWrite};
|
|
use halo2_middleware::ff::{Field, FromUniformBytes, WithSmallOrderMulGroup};
|
|
|
|
/// Collection of instance data used during proving for a single circuit proof.
|
|
#[derive(Debug)]
|
|
struct InstanceSingle<C: CurveAffine> {
|
|
pub instance_values: Vec<Polynomial<C::Scalar, LagrangeCoeff>>,
|
|
pub instance_polys: Vec<Polynomial<C::Scalar, Coeff>>,
|
|
}
|
|
|
|
/// Collection of advice data used during proving for a single circuit proof.
|
|
#[derive(Debug, Clone)]
|
|
struct AdviceSingle<C: CurveAffine, B: Basis> {
|
|
pub advice_polys: Vec<Polynomial<C::Scalar, B>>,
|
|
pub advice_blinds: Vec<Blind<C::Scalar>>,
|
|
}
|
|
|
|
/// The prover object used to create proofs interactively by passing the witnesses to commit at
|
|
/// each phase. This works for a single proof. This is a wrapper over ProverV2.
|
|
#[derive(Debug)]
|
|
pub struct ProverV2Single<
|
|
'a,
|
|
'params,
|
|
Scheme: CommitmentScheme,
|
|
P: Prover<'params, Scheme>,
|
|
E: EncodedChallenge<Scheme::Curve>,
|
|
R: RngCore,
|
|
T: TranscriptWrite<Scheme::Curve, E>,
|
|
>(ProverV2<'a, 'params, Scheme, P, E, R, T>);
|
|
|
|
impl<
|
|
'a,
|
|
'params,
|
|
Scheme: CommitmentScheme,
|
|
P: Prover<'params, Scheme>,
|
|
E: EncodedChallenge<Scheme::Curve>,
|
|
R: RngCore,
|
|
T: TranscriptWrite<Scheme::Curve, E>,
|
|
> ProverV2Single<'a, 'params, Scheme, P, E, R, T>
|
|
{
|
|
/// Create a new prover object
|
|
pub fn new(
|
|
params: &'params Scheme::ParamsProver,
|
|
pk: &'a ProvingKey<Scheme::Curve>,
|
|
// TODO: If this was a vector the usage would be simpler
|
|
// https://github.com/privacy-scaling-explorations/halo2/issues/265
|
|
instance: &[&[Scheme::Scalar]],
|
|
rng: R,
|
|
transcript: &'a mut T,
|
|
) -> Result<Self, Error>
|
|
where
|
|
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
|
{
|
|
Ok(Self(ProverV2::new(
|
|
params,
|
|
pk,
|
|
&[instance],
|
|
rng,
|
|
transcript,
|
|
)?))
|
|
}
|
|
|
|
/// Commit the `witness` at `phase` and return the challenges after `phase`.
|
|
pub fn commit_phase(
|
|
&mut self,
|
|
phase: u8,
|
|
witness: Vec<Option<Vec<Scheme::Scalar>>>,
|
|
) -> Result<HashMap<usize, Scheme::Scalar>, Error>
|
|
where
|
|
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
|
{
|
|
self.0.commit_phase(phase, vec![witness])
|
|
}
|
|
|
|
/// Finalizes the proof creation.
|
|
pub fn create_proof(self) -> Result<(), Error>
|
|
where
|
|
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
|
{
|
|
self.0.create_proof()
|
|
}
|
|
}
|
|
|
|
/// The prover object used to create proofs interactively by passing the witnesses to commit at
|
|
/// each phase. This supports batch proving.
|
|
#[derive(Debug)]
|
|
pub struct ProverV2<
|
|
'a,
|
|
'params,
|
|
Scheme: CommitmentScheme,
|
|
P: Prover<'params, Scheme>,
|
|
E: EncodedChallenge<Scheme::Curve>,
|
|
R: RngCore,
|
|
T: TranscriptWrite<Scheme::Curve, E>,
|
|
> {
|
|
// Circuit and setup fields
|
|
params: &'params Scheme::ParamsProver,
|
|
// Plonk proving key
|
|
pk: &'a ProvingKey<Scheme::Curve>,
|
|
// Phases
|
|
phases: Vec<u8>,
|
|
// Polynomials (Lagrange and Coeff) for all circuits instances
|
|
instances: Vec<InstanceSingle<Scheme::Curve>>,
|
|
// Advice polynomials with its blindings
|
|
advices: Vec<AdviceSingle<Scheme::Curve, LagrangeCoeff>>,
|
|
// The phase challenges by challenge index
|
|
challenges: HashMap<usize, Scheme::Scalar>,
|
|
// The next phase to be committed
|
|
next_phase_index: usize,
|
|
// Transcript to be updated
|
|
transcript: &'a mut T,
|
|
// Randomness
|
|
rng: R,
|
|
_marker: std::marker::PhantomData<(P, E)>,
|
|
}
|
|
|
|
impl<
|
|
'a,
|
|
'params,
|
|
Scheme: CommitmentScheme,
|
|
P: Prover<'params, Scheme>,
|
|
E: EncodedChallenge<Scheme::Curve>,
|
|
R: RngCore,
|
|
T: TranscriptWrite<Scheme::Curve, E>,
|
|
> ProverV2<'a, 'params, Scheme, P, E, R, T>
|
|
{
|
|
/// Create a new prover object
|
|
pub fn new(
|
|
params: &'params Scheme::ParamsProver,
|
|
pk: &'a ProvingKey<Scheme::Curve>,
|
|
// TODO: If this was a vector the usage would be simpler.
|
|
// https://github.com/privacy-scaling-explorations/halo2/issues/265
|
|
circuits_instances: &[&[&[Scheme::Scalar]]],
|
|
rng: R,
|
|
transcript: &'a mut T,
|
|
) -> Result<Self, Error>
|
|
where
|
|
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
|
{
|
|
for instance in circuits_instances.iter() {
|
|
if instance.len() != pk.vk.cs.num_instance_columns {
|
|
return Err(Error::InvalidInstances);
|
|
}
|
|
}
|
|
|
|
// Hash verification key into transcript [TRANSCRIPT-1]
|
|
pk.vk.hash_into(transcript)?;
|
|
|
|
let meta = &pk.vk.cs;
|
|
let phases = meta.phases().collect();
|
|
|
|
let domain = &pk.vk.domain;
|
|
|
|
// commit_instance_fn is a helper function to return the polynomials (and its commitments) of
|
|
// instance columns while updating the transcript.
|
|
let mut commit_instance_fn =
|
|
|instance: &[&[Scheme::Scalar]]| -> Result<InstanceSingle<Scheme::Curve>, Error> {
|
|
// Create a lagrange polynomial for each instance column
|
|
|
|
let instance_values = instance
|
|
.iter()
|
|
.map(|values| {
|
|
let mut poly = domain.empty_lagrange();
|
|
assert_eq!(poly.len(), params.n() as usize);
|
|
if values.len() > (poly.len() - (meta.blinding_factors() + 1)) {
|
|
return Err(Error::InstanceTooLarge);
|
|
}
|
|
for (poly, value) in poly.iter_mut().zip(values.iter()) {
|
|
if !P::QUERY_INSTANCE {
|
|
// Add to the transcript the instance polynomials lagrange value.
|
|
transcript.common_scalar(*value)?;
|
|
}
|
|
*poly = *value;
|
|
}
|
|
Ok(poly)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
if P::QUERY_INSTANCE {
|
|
// Add to the transcript the commitments of the instance lagrange polynomials
|
|
|
|
let instance_commitments_projective: Vec<_> = instance_values
|
|
.iter()
|
|
.map(|poly| params.commit_lagrange(poly, Blind::default()))
|
|
.collect();
|
|
let mut instance_commitments =
|
|
vec![Scheme::Curve::identity(); instance_commitments_projective.len()];
|
|
<Scheme::Curve as CurveAffine>::CurveExt::batch_normalize(
|
|
&instance_commitments_projective,
|
|
&mut instance_commitments,
|
|
);
|
|
let instance_commitments = instance_commitments;
|
|
drop(instance_commitments_projective);
|
|
|
|
for commitment in &instance_commitments {
|
|
transcript.common_point(*commitment)?;
|
|
}
|
|
}
|
|
|
|
// Convert from evaluation to coefficient form.
|
|
|
|
let instance_polys: Vec<_> = instance_values
|
|
.iter()
|
|
.map(|poly| {
|
|
let lagrange_vec = domain.lagrange_from_vec(poly.to_vec());
|
|
domain.lagrange_to_coeff(lagrange_vec)
|
|
})
|
|
.collect();
|
|
|
|
Ok(InstanceSingle {
|
|
instance_values,
|
|
instance_polys,
|
|
})
|
|
};
|
|
|
|
// Commit the polynomials of all circuits instances
|
|
// [TRANSCRIPT-2]
|
|
|
|
let instances: Vec<InstanceSingle<Scheme::Curve>> = circuits_instances
|
|
.iter()
|
|
.map(|instance| commit_instance_fn(instance))
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// Create an structure to hold the advice polynomials and its blinds, it will be filled later in the
|
|
// [`commit_phase`].
|
|
|
|
let advices = vec![
|
|
AdviceSingle::<Scheme::Curve, LagrangeCoeff> {
|
|
// Create vectors with empty polynomials to free space while they are not being used
|
|
advice_polys: vec![
|
|
Polynomial::new_empty(0, Scheme::Scalar::ZERO);
|
|
meta.num_advice_columns
|
|
],
|
|
advice_blinds: vec![Blind::default(); meta.num_advice_columns],
|
|
};
|
|
circuits_instances.len()
|
|
];
|
|
|
|
// Challenges will be also filled later in the [`commit_phase`].
|
|
|
|
let challenges = HashMap::<usize, Scheme::Scalar>::with_capacity(meta.num_challenges);
|
|
|
|
Ok(ProverV2 {
|
|
params,
|
|
pk,
|
|
phases,
|
|
instances,
|
|
rng,
|
|
transcript,
|
|
advices,
|
|
challenges,
|
|
next_phase_index: 0,
|
|
_marker: std::marker::PhantomData {},
|
|
})
|
|
}
|
|
|
|
/// Commit the `witness` at `phase` and return the challenges after `phase`.
|
|
#[allow(clippy::type_complexity)]
|
|
pub fn commit_phase(
|
|
&mut self,
|
|
phase: u8,
|
|
witness: Vec<Vec<Option<Vec<Scheme::Scalar>>>>,
|
|
) -> Result<HashMap<usize, Scheme::Scalar>, Error>
|
|
where
|
|
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
|
{
|
|
// Check if the phase is valid.
|
|
|
|
let current_phase = match self.phases.get(self.next_phase_index) {
|
|
Some(phase) => phase,
|
|
None => {
|
|
return Err(Error::Other("All phases already committed".to_string()));
|
|
}
|
|
};
|
|
if phase != *current_phase {
|
|
return Err(Error::Other(format!(
|
|
"Committing invalid phase. Expected {current_phase}, got {phase}",
|
|
)));
|
|
}
|
|
|
|
let params = self.params;
|
|
let meta = &self.pk.vk.cs;
|
|
|
|
let mut rng = &mut self.rng;
|
|
|
|
let advices = &mut self.advices;
|
|
let challenges = &mut self.challenges;
|
|
|
|
// Get the indices of the advice columns that are in the current phase.
|
|
|
|
let column_indices = meta
|
|
.advice_column_phase
|
|
.iter()
|
|
.enumerate()
|
|
.filter_map(|(column_index, phase)| {
|
|
if current_phase == phase {
|
|
Some(column_index)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect::<BTreeSet<_>>();
|
|
|
|
if witness.len() != advices.len() {
|
|
return Err(Error::Other("witness.len() != advice.len()".to_string()));
|
|
}
|
|
|
|
// Check all witness are consistent with the current phase.
|
|
|
|
for witness_circuit in &witness {
|
|
// Check contains all columns.
|
|
if witness_circuit.len() != meta.num_advice_columns {
|
|
return Err(Error::Other(format!(
|
|
"unexpected length in witness_circuitk. Got {}, expected {}",
|
|
witness_circuit.len(),
|
|
meta.num_advice_columns,
|
|
)));
|
|
}
|
|
// Check that all current_phase advice columns are Some, and their length is correct
|
|
for (column_index, advice_column) in witness_circuit.iter().enumerate() {
|
|
if column_indices.contains(&column_index) {
|
|
match advice_column {
|
|
None => {
|
|
return Err(Error::Other(format!(
|
|
"expected advice column with index {column_index} at phase {current_phase}",
|
|
)))
|
|
}
|
|
Some(advice_column) => {
|
|
if advice_column.len() != params.n() as usize {
|
|
return Err(Error::Other(format!(
|
|
"expected advice column with index {} to have length {}",
|
|
column_index,
|
|
params.n(),
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
} else if advice_column.is_some() {
|
|
return Err(Error::Other(format!(
|
|
"expected no advice column with index {column_index} at phase {current_phase}",
|
|
)));
|
|
};
|
|
}
|
|
}
|
|
|
|
// commit_phase_fn fills advice columns (no defined as unblinded) with binding factors,
|
|
// adding to the transcript its blinded affine commitments.
|
|
// Also sets advice_polys with the (blinding) updated advice columns and advice_blinds with
|
|
// the blinding factor used for each advice column.
|
|
|
|
let mut commit_phase_fn =
|
|
|advice: &mut AdviceSingle<Scheme::Curve, LagrangeCoeff>,
|
|
witness: Vec<Option<Polynomial<Scheme::Scalar, LagrangeCoeff>>>|
|
|
-> Result<(), Error> {
|
|
let unusable_rows_start = params.n() as usize - (meta.blinding_factors() + 1);
|
|
let mut advice_values: Vec<_> = witness.into_iter().flatten().collect();
|
|
let unblinded_advice: HashSet<usize> =
|
|
HashSet::from_iter(meta.unblinded_advice_columns.clone());
|
|
|
|
// Add blinding factors to advice columns.
|
|
for (column_index, advice_values) in column_indices.iter().zip(&mut advice_values) {
|
|
if !unblinded_advice.contains(column_index) {
|
|
for cell in &mut advice_values[unusable_rows_start..] {
|
|
*cell = Scheme::Scalar::random(&mut rng);
|
|
}
|
|
} else {
|
|
#[cfg(feature = "sanity-checks")]
|
|
for cell in &advice_values[unusable_rows_start..] {
|
|
assert_eq!(*cell, Scheme::Scalar::ZERO);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute commitments to advice column polynomials
|
|
let blinds: Vec<_> = column_indices
|
|
.iter()
|
|
.map(|i| {
|
|
if unblinded_advice.contains(i) {
|
|
Blind::default()
|
|
} else {
|
|
Blind(Scheme::Scalar::random(&mut rng))
|
|
}
|
|
})
|
|
.collect();
|
|
let advice_commitments_projective: Vec<_> = advice_values
|
|
.iter()
|
|
.zip(blinds.iter())
|
|
.map(|(poly, blind)| params.commit_lagrange(poly, *blind))
|
|
.collect();
|
|
let mut advice_commitments_affine =
|
|
vec![Scheme::Curve::identity(); advice_commitments_projective.len()];
|
|
<Scheme::Curve as CurveAffine>::CurveExt::batch_normalize(
|
|
&advice_commitments_projective,
|
|
&mut advice_commitments_affine,
|
|
);
|
|
let advice_commitments_affine = advice_commitments_affine;
|
|
drop(advice_commitments_projective);
|
|
|
|
// Update transcript.
|
|
// [TRANSCRIPT-3]
|
|
for commitment in &advice_commitments_affine {
|
|
self.transcript.write_point(*commitment)?;
|
|
}
|
|
|
|
// Set advice_polys & advice_blinds
|
|
for ((column_index, advice_values), blind) in
|
|
column_indices.iter().zip(advice_values).zip(blinds)
|
|
{
|
|
advice.advice_polys[*column_index] = advice_values;
|
|
advice.advice_blinds[*column_index] = blind;
|
|
}
|
|
Ok(())
|
|
};
|
|
|
|
// Update blindings for each advice column
|
|
// [TRANSCRIPT-3]
|
|
|
|
for (witness, advice) in witness.into_iter().zip(advices.iter_mut()) {
|
|
commit_phase_fn(
|
|
advice,
|
|
witness
|
|
.into_iter()
|
|
.map(|v| v.map(Polynomial::new_lagrange_from_vec))
|
|
.collect(),
|
|
)?;
|
|
}
|
|
|
|
// Squeeze the current transcript and get an new fresh challenge from the current phase.
|
|
// [TRANSCRIPT-4]
|
|
|
|
for (index, phase) in meta.challenge_phase.iter().enumerate() {
|
|
if current_phase == phase {
|
|
let existing =
|
|
challenges.insert(index, *self.transcript.squeeze_challenge_scalar::<()>());
|
|
assert!(existing.is_none());
|
|
}
|
|
}
|
|
|
|
// Move on
|
|
|
|
self.next_phase_index += 1;
|
|
Ok(challenges.clone())
|
|
}
|
|
|
|
/// Finalizes the proof creation.
|
|
/// The following steps are performed:
|
|
/// - 1. Generate commited lookup polys
|
|
/// - 2. Generate commited permutation polys
|
|
/// - 3. Generate commited lookup polys
|
|
/// - 4. Generate commited shuffle polys
|
|
/// - 5. Commit to the vanishing argument's random polynomial
|
|
/// - 6. Generate the advice polys
|
|
/// - 7. Evaluate the h(X) polynomial
|
|
/// - 8. Construct the vanishing argument's h(X) commitments
|
|
/// - 9. Compute x
|
|
/// - 10. Compute and hash instance evals for the circuit instance
|
|
/// - 11. Compute and hash fixed evals
|
|
/// - 12. Evaluate permutation, lookups and shuffles at x
|
|
/// - 13. Generate all queries ([`PowerQuery`])
|
|
/// - 14. Send the queries to the [`Prover`]
|
|
pub fn create_proof(mut self) -> Result<(), Error>
|
|
where
|
|
Scheme::Scalar: WithSmallOrderMulGroup<3> + FromUniformBytes<64>,
|
|
{
|
|
let params = self.params;
|
|
let cs = &self.pk.vk.cs;
|
|
let pk = self.pk;
|
|
let domain = &self.pk.vk.domain;
|
|
|
|
let mut rng = self.rng;
|
|
|
|
let instances = std::mem::take(&mut self.instances);
|
|
let advices = std::mem::take(&mut self.advices);
|
|
let mut challenges = self.challenges;
|
|
|
|
assert_eq!(challenges.len(), cs.num_challenges);
|
|
let challenges = (0..cs.num_challenges)
|
|
.map(|index| challenges.remove(&index).unwrap())
|
|
.collect::<Vec<_>>();
|
|
|
|
// 1. Generate commited ( added to transcript ) lookup polys ---------------------------------------
|
|
|
|
// Sample theta challenge for keeping lookup columns linearly independent
|
|
// [TRANSCRIPT-5]
|
|
|
|
let theta: ChallengeTheta<_> = self.transcript.squeeze_challenge_scalar();
|
|
|
|
// 2. Get permuted lookup polys
|
|
// [TRANSCRIPT-6]
|
|
|
|
let mut lookups_fn =
|
|
|instance: &InstanceSingle<Scheme::Curve>,
|
|
advice: &AdviceSingle<Scheme::Curve, LagrangeCoeff>|
|
|
-> Result<Vec<lookup::prover::Permuted<Scheme::Curve>>, Error> {
|
|
cs.lookups
|
|
.iter()
|
|
.map(|lookup| {
|
|
lookup_commit_permuted(
|
|
lookup,
|
|
pk,
|
|
params,
|
|
domain,
|
|
theta,
|
|
&advice.advice_polys,
|
|
&pk.fixed_values,
|
|
&instance.instance_values,
|
|
&challenges,
|
|
&mut rng,
|
|
self.transcript,
|
|
)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()
|
|
};
|
|
let permuted_lookups: Vec<Vec<lookup::prover::Permuted<Scheme::Curve>>> = instances
|
|
.iter()
|
|
.zip(advices.iter())
|
|
.map(|(instance, advice)| -> Result<Vec<_>, Error> {
|
|
// Construct and commit to permuted values for each lookup
|
|
lookups_fn(instance, advice)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// Sample beta challenge
|
|
// [TRANSCRIPT-7]
|
|
let beta: ChallengeBeta<_> = self.transcript.squeeze_challenge_scalar();
|
|
|
|
// Sample gamma challenge
|
|
// [TRANSCRIPT-8]
|
|
let gamma: ChallengeGamma<_> = self.transcript.squeeze_challenge_scalar();
|
|
|
|
// 2. Generate commited permutation polys -----------------------------------------
|
|
// [TRANSCRIPT-9]
|
|
let permutations_commited: Vec<permutation::prover::Committed<Scheme::Curve>> = instances
|
|
.iter()
|
|
.zip(advices.iter())
|
|
.map(|(instance, advice)| {
|
|
permutation_commit(
|
|
&cs.permutation,
|
|
params,
|
|
pk,
|
|
&pk.permutation,
|
|
&advice.advice_polys,
|
|
&pk.fixed_values,
|
|
&instance.instance_values,
|
|
beta,
|
|
gamma,
|
|
&mut rng,
|
|
self.transcript,
|
|
)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// 3. Generate commited lookup polys ----------------------------------------------------------
|
|
|
|
// [TRANSCRIPT-10]
|
|
let lookups_commited: Vec<Vec<lookup::prover::Committed<Scheme::Curve>>> = permuted_lookups
|
|
.into_iter()
|
|
.map(|lookups| -> Result<Vec<_>, _> {
|
|
// Construct and commit to products for each lookup
|
|
lookups
|
|
.into_iter()
|
|
.map(|lookup| {
|
|
lookup.commit_product(pk, params, beta, gamma, &mut rng, self.transcript)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// 4. Generate commited shuffle polys -------------------------------------------------------
|
|
|
|
// [TRANSCRIPT-11]
|
|
let shuffles_commited: Vec<Vec<shuffle::prover::Committed<Scheme::Curve>>> = instances
|
|
.iter()
|
|
.zip(advices.iter())
|
|
.map(|(instance, advice)| -> Result<Vec<_>, _> {
|
|
// Compress expressions for each shuffle
|
|
cs.shuffles
|
|
.iter()
|
|
.map(|shuffle| {
|
|
shuffle_commit_product(
|
|
shuffle,
|
|
pk,
|
|
params,
|
|
domain,
|
|
theta,
|
|
gamma,
|
|
&advice.advice_polys,
|
|
&pk.fixed_values,
|
|
&instance.instance_values,
|
|
&challenges,
|
|
&mut rng,
|
|
self.transcript,
|
|
)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// 5. Commit to the vanishing argument's random polynomial for blinding h(x_3) -------------------
|
|
// [TRANSCRIPT-12]
|
|
let vanishing = vanishing::Argument::commit(params, domain, &mut rng, self.transcript)?;
|
|
|
|
// 6. Generate the advice polys ------------------------------------------------------------------
|
|
|
|
let advice: Vec<AdviceSingle<Scheme::Curve, Coeff>> = advices
|
|
.into_iter()
|
|
.map(
|
|
|AdviceSingle {
|
|
advice_polys,
|
|
advice_blinds,
|
|
}| {
|
|
AdviceSingle {
|
|
advice_polys: advice_polys
|
|
.into_iter()
|
|
.map(|poly| domain.lagrange_to_coeff(poly))
|
|
.collect::<Vec<_>>(),
|
|
advice_blinds,
|
|
}
|
|
},
|
|
)
|
|
.collect();
|
|
|
|
// 7. Evaluate the h(X) polynomial -----------------------------------------------------------
|
|
|
|
// Obtain challenge for keeping all separate gates linearly independent
|
|
// [TRANSCRIPT-13]
|
|
let y: ChallengeY<_> = self.transcript.squeeze_challenge_scalar();
|
|
|
|
let h_poly = pk.ev.evaluate_h(
|
|
pk,
|
|
&advice
|
|
.iter()
|
|
.map(|a| a.advice_polys.as_slice())
|
|
.collect::<Vec<_>>(),
|
|
&instances
|
|
.iter()
|
|
.map(|i| i.instance_polys.as_slice())
|
|
.collect::<Vec<_>>(),
|
|
&challenges,
|
|
*y,
|
|
*beta,
|
|
*gamma,
|
|
*theta,
|
|
&lookups_commited,
|
|
&shuffles_commited,
|
|
&permutations_commited,
|
|
);
|
|
|
|
// 8. Construct the vanishing argument's h(X) commitments --------------------------------------
|
|
// [TRANSCRIPT-14]
|
|
let vanishing = vanishing.construct(params, domain, h_poly, &mut rng, self.transcript)?;
|
|
|
|
// 9. Compute x --------------------------------------------------------------------------------
|
|
// [TRANSCRIPT-15]
|
|
let x: ChallengeX<_> = self.transcript.squeeze_challenge_scalar();
|
|
|
|
let x_pow_n = x.pow([params.n()]);
|
|
|
|
// [TRANSCRIPT-16]
|
|
if P::QUERY_INSTANCE {
|
|
// Compute and hash instance evals for the circuit instance
|
|
for instance in instances.iter() {
|
|
// Evaluate polynomials at omega^i x
|
|
let instance_evals: Vec<_> = cs
|
|
.instance_queries
|
|
.iter()
|
|
.map(|&(column, at)| {
|
|
eval_polynomial(
|
|
&instance.instance_polys[column.index],
|
|
domain.rotate_omega(*x, at),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
// Hash each instance column evaluation
|
|
for eval in instance_evals.iter() {
|
|
self.transcript.write_scalar(*eval)?;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 10. Compute and hash advice evals for the circuit instance ------------------------------------
|
|
// [TRANSCRIPT-17]
|
|
for advice in advice.iter() {
|
|
// Evaluate polynomials at omega^i x
|
|
let advice_evals: Vec<_> = cs
|
|
.advice_queries
|
|
.iter()
|
|
.map(|&(column, at)| {
|
|
eval_polynomial(
|
|
&advice.advice_polys[column.index],
|
|
domain.rotate_omega(*x, at),
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
// Hash each advice column evaluation
|
|
for eval in advice_evals.iter() {
|
|
self.transcript.write_scalar(*eval)?;
|
|
}
|
|
}
|
|
|
|
// 11. Compute and hash fixed evals -----------------------------------------------------------
|
|
let fixed_evals: Vec<_> = cs
|
|
.fixed_queries
|
|
.iter()
|
|
.map(|&(column, at)| {
|
|
eval_polynomial(&pk.fixed_polys[column.index], domain.rotate_omega(*x, at))
|
|
})
|
|
.collect();
|
|
|
|
// Hash each fixed column evaluation
|
|
// [TRANSCRIPT-18]
|
|
for eval in fixed_evals.iter() {
|
|
self.transcript.write_scalar(*eval)?;
|
|
}
|
|
|
|
// [TRANSCRIPT-19]
|
|
let vanishing = vanishing.evaluate(x, x_pow_n, domain, self.transcript)?;
|
|
|
|
// 12. Evaluate permutation, lookups and shuffles at x -----------------------------------
|
|
|
|
// Evaluate common permutation data
|
|
// [TRANSCRIPT-20]
|
|
pk.permutation.evaluate(x, self.transcript)?;
|
|
|
|
// Evaluate the permutations, if any, at omega^i x.
|
|
// [TRANSCRIPT-21]
|
|
let permutations_evaluated: Vec<permutation::prover::Evaluated<Scheme::Curve>> =
|
|
permutations_commited
|
|
.into_iter()
|
|
.map(|permutation| -> Result<_, _> {
|
|
permutation.construct().evaluate(pk, x, self.transcript)
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// Evaluate the lookups, if any, at omega^i x.
|
|
// [TRANSCRIPT-22]
|
|
let lookups_evaluated: Vec<Vec<lookup::prover::Evaluated<Scheme::Curve>>> =
|
|
lookups_commited
|
|
.into_iter()
|
|
.map(|lookups| -> Result<Vec<_>, _> {
|
|
lookups
|
|
.into_iter()
|
|
.map(|p| p.evaluate(pk, x, self.transcript))
|
|
.collect::<Result<Vec<_>, _>>()
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// Evaluate the shuffles, if any, at omega^i x.
|
|
// [TRANSCRIPT-23]
|
|
let shuffles_evaluated: Vec<Vec<shuffle::prover::Evaluated<Scheme::Curve>>> =
|
|
shuffles_commited
|
|
.into_iter()
|
|
.map(|shuffles| -> Result<Vec<_>, _> {
|
|
shuffles
|
|
.into_iter()
|
|
.map(|p| p.evaluate(pk, x, self.transcript))
|
|
.collect::<Result<Vec<_>, _>>()
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
|
|
// 13. Generate all queries ([`PowerQuery`]) that needs to be sent to prover --------------------
|
|
|
|
let queries = instances
|
|
// group the instance, advice, permutation, lookups and shuffles
|
|
.iter()
|
|
.zip(advice.iter())
|
|
.zip(permutations_evaluated.iter())
|
|
.zip(lookups_evaluated.iter())
|
|
.zip(shuffles_evaluated.iter())
|
|
.flat_map(|((((instance, advice), permutation), lookups), shuffles)| {
|
|
// Build a (an iterator) over a set of ProverQueries for each instance, advice, permutatiom, lookup and shuffle
|
|
iter::empty()
|
|
// Instances
|
|
.chain(
|
|
P::QUERY_INSTANCE
|
|
.then_some(cs.instance_queries.iter().map(move |&(column, at)| {
|
|
ProverQuery {
|
|
point: domain.rotate_omega(*x, at),
|
|
poly: &instance.instance_polys[column.index],
|
|
blind: Blind::default(),
|
|
}
|
|
}))
|
|
.into_iter()
|
|
.flatten(),
|
|
)
|
|
// Advices
|
|
.chain(
|
|
cs.advice_queries
|
|
.iter()
|
|
.map(move |&(column, at)| ProverQuery {
|
|
point: domain.rotate_omega(*x, at),
|
|
poly: &advice.advice_polys[column.index],
|
|
blind: advice.advice_blinds[column.index],
|
|
}),
|
|
)
|
|
// Permutations
|
|
.chain(permutation.open(pk, x))
|
|
// Lookups
|
|
.chain(lookups.iter().flat_map(move |p| p.open(pk, x)))
|
|
// Shuffles
|
|
.chain(shuffles.iter().flat_map(move |p| p.open(pk, x)))
|
|
})
|
|
// Queries to fixed columns
|
|
.chain(cs.fixed_queries.iter().map(|&(column, at)| ProverQuery {
|
|
point: domain.rotate_omega(*x, at),
|
|
poly: &pk.fixed_polys[column.index],
|
|
blind: Blind::default(),
|
|
}))
|
|
// Copy constraints
|
|
.chain(pk.permutation.open(x))
|
|
// We query the h(X) polynomial at x
|
|
.chain(vanishing.open(x));
|
|
|
|
// 14. Send the queries to the [`Prover`] ------------------------------------------------
|
|
|
|
let prover = P::new(params);
|
|
prover
|
|
.create_proof(rng, self.transcript, queries)
|
|
.map_err(|_| Error::ConstraintSystemFailure)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Returns the phases of the circuit
|
|
pub fn phases(&self) -> &[u8] {
|
|
self.phases.as_slice()
|
|
}
|
|
}
|