2020-09-29 07:25:04 -07:00
|
|
|
use super::{
|
2020-11-05 19:25:50 -08:00
|
|
|
circuit::{Advice, Assignment, Circuit, Column, ConstraintSystem, Fixed},
|
2020-09-29 07:25:04 -07:00
|
|
|
Error, ProvingKey, VerifyingKey,
|
|
|
|
};
|
|
|
|
use crate::arithmetic::{Curve, CurveAffine, Field};
|
|
|
|
use crate::poly::{
|
|
|
|
commitment::{Blind, Params},
|
|
|
|
EvaluationDomain, LagrangeCoeff, Polynomial, Rotation,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Generate a `ProvingKey` from an instance of `Circuit`.
|
|
|
|
pub fn keygen<C, ConcreteCircuit>(
|
|
|
|
params: &Params<C>,
|
|
|
|
circuit: &ConcreteCircuit,
|
|
|
|
) -> Result<ProvingKey<C>, Error>
|
|
|
|
where
|
|
|
|
C: CurveAffine,
|
|
|
|
ConcreteCircuit: Circuit<C::Scalar>,
|
|
|
|
{
|
|
|
|
struct Assembly<F: Field> {
|
|
|
|
fixed: Vec<Polynomial<F, LagrangeCoeff>>,
|
|
|
|
mapping: Vec<Vec<Vec<(usize, usize)>>>,
|
|
|
|
aux: Vec<Vec<Vec<(usize, usize)>>>,
|
|
|
|
sizes: Vec<Vec<Vec<usize>>>,
|
|
|
|
_marker: std::marker::PhantomData<F>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<F: Field> Assignment<F> for Assembly<F> {
|
|
|
|
fn assign_advice(
|
|
|
|
&mut self,
|
2020-11-05 19:25:50 -08:00
|
|
|
_: Column<Advice>,
|
2020-09-29 07:25:04 -07:00
|
|
|
_: usize,
|
|
|
|
_: impl FnOnce() -> Result<F, Error>,
|
|
|
|
) -> Result<(), Error> {
|
2020-11-05 19:13:54 -08:00
|
|
|
// We only care about fixed columns here
|
2020-09-29 07:25:04 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn assign_fixed(
|
|
|
|
&mut self,
|
2020-11-05 19:25:50 -08:00
|
|
|
column: Column<Fixed>,
|
2020-09-29 07:25:04 -07:00
|
|
|
row: usize,
|
|
|
|
to: impl FnOnce() -> Result<F, Error>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
*self
|
|
|
|
.fixed
|
2020-11-09 08:45:52 -08:00
|
|
|
.get_mut(column.index())
|
2020-09-29 07:25:04 -07:00
|
|
|
.and_then(|v| v.get_mut(row))
|
|
|
|
.ok_or(Error::BoundsFailure)? = to()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn copy(
|
|
|
|
&mut self,
|
|
|
|
permutation: usize,
|
2020-11-05 19:13:54 -08:00
|
|
|
left_column: usize,
|
2020-09-29 07:25:04 -07:00
|
|
|
left_row: usize,
|
2020-11-05 19:13:54 -08:00
|
|
|
right_column: usize,
|
2020-09-29 07:25:04 -07:00
|
|
|
right_row: usize,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
// Check bounds first
|
|
|
|
if permutation >= self.mapping.len()
|
2020-11-05 19:13:54 -08:00
|
|
|
|| left_column >= self.mapping[permutation].len()
|
|
|
|
|| left_row >= self.mapping[permutation][left_column].len()
|
|
|
|
|| right_column >= self.mapping[permutation].len()
|
|
|
|
|| right_row >= self.mapping[permutation][right_column].len()
|
2020-09-29 07:25:04 -07:00
|
|
|
{
|
|
|
|
return Err(Error::BoundsFailure);
|
|
|
|
}
|
|
|
|
|
2020-11-05 19:13:54 -08:00
|
|
|
let mut left_cycle = self.aux[permutation][left_column][left_row];
|
|
|
|
let mut right_cycle = self.aux[permutation][right_column][right_row];
|
2020-09-29 07:25:04 -07:00
|
|
|
|
|
|
|
if left_cycle == right_cycle {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.sizes[permutation][left_cycle.0][left_cycle.1]
|
|
|
|
< self.sizes[permutation][right_cycle.0][right_cycle.1]
|
|
|
|
{
|
|
|
|
std::mem::swap(&mut left_cycle, &mut right_cycle);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.sizes[permutation][left_cycle.0][left_cycle.1] +=
|
|
|
|
self.sizes[permutation][right_cycle.0][right_cycle.1];
|
|
|
|
let mut i = right_cycle;
|
|
|
|
loop {
|
|
|
|
self.aux[permutation][i.0][i.1] = left_cycle;
|
|
|
|
i = self.mapping[permutation][i.0][i.1];
|
|
|
|
if i == right_cycle {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-05 19:13:54 -08:00
|
|
|
let tmp = self.mapping[permutation][left_column][left_row];
|
|
|
|
self.mapping[permutation][left_column][left_row] =
|
|
|
|
self.mapping[permutation][right_column][right_row];
|
|
|
|
self.mapping[permutation][right_column][right_row] = tmp;
|
2020-09-29 07:25:04 -07:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut cs = ConstraintSystem::default();
|
|
|
|
let config = ConcreteCircuit::configure(&mut cs);
|
|
|
|
|
|
|
|
// Get the largest permutation argument length in terms of the number of
|
2020-11-05 19:13:54 -08:00
|
|
|
// advice columns involved.
|
2020-09-29 07:25:04 -07:00
|
|
|
let mut largest_permutation_length = 0;
|
|
|
|
for permutation in &cs.permutations {
|
|
|
|
largest_permutation_length = std::cmp::max(permutation.len(), largest_permutation_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The permutation argument will serve alongside the gates, so must be
|
|
|
|
// accounted for.
|
|
|
|
let mut degree = largest_permutation_length + 1;
|
|
|
|
|
|
|
|
// Account for each gate to ensure our quotient polynomial is the
|
|
|
|
// correct degree and that our extended domain is the right size.
|
|
|
|
for poly in cs.gates.iter() {
|
|
|
|
degree = std::cmp::max(degree, poly.degree());
|
|
|
|
}
|
|
|
|
|
|
|
|
let domain = EvaluationDomain::new(degree as u32, params.k);
|
|
|
|
|
|
|
|
// Compute [omega^0, omega^1, ..., omega^{params.n - 1}]
|
|
|
|
let mut omega_powers = Vec::with_capacity(params.n as usize);
|
|
|
|
{
|
|
|
|
let mut cur = C::Scalar::one();
|
|
|
|
for _ in 0..params.n {
|
|
|
|
omega_powers.push(cur);
|
|
|
|
cur *= &domain.get_omega();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute [omega_powers * \delta^0, omega_powers * \delta^1, ..., omega_powers * \delta^m]
|
|
|
|
let mut deltaomega = Vec::with_capacity(largest_permutation_length);
|
|
|
|
{
|
|
|
|
let mut cur = C::Scalar::one();
|
|
|
|
for _ in 0..largest_permutation_length {
|
|
|
|
let mut omega_powers = omega_powers.clone();
|
|
|
|
for o in &mut omega_powers {
|
|
|
|
*o *= &cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
deltaomega.push(omega_powers);
|
|
|
|
|
|
|
|
cur *= &C::Scalar::DELTA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut assembly: Assembly<C::Scalar> = Assembly {
|
2020-11-05 19:13:54 -08:00
|
|
|
fixed: vec![domain.empty_lagrange(); cs.num_fixed_columns],
|
2020-09-29 07:25:04 -07:00
|
|
|
mapping: vec![],
|
|
|
|
aux: vec![],
|
|
|
|
sizes: vec![],
|
|
|
|
_marker: std::marker::PhantomData,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Initialize the copy vector to keep track of copy constraints in all
|
|
|
|
// the permutation arguments.
|
|
|
|
for permutation in &cs.permutations {
|
2020-11-05 19:13:54 -08:00
|
|
|
let mut columns = vec![];
|
2020-09-29 07:25:04 -07:00
|
|
|
for i in 0..permutation.len() {
|
|
|
|
// Computes [(i, 0), (i, 1), ..., (i, n - 1)]
|
2020-11-05 19:13:54 -08:00
|
|
|
columns.push((0..params.n).map(|j| (i, j as usize)).collect());
|
2020-09-29 07:25:04 -07:00
|
|
|
}
|
2020-11-05 19:13:54 -08:00
|
|
|
assembly.mapping.push(columns.clone());
|
|
|
|
assembly.aux.push(columns);
|
2020-09-29 07:25:04 -07:00
|
|
|
assembly
|
|
|
|
.sizes
|
|
|
|
.push(vec![vec![1usize; params.n as usize]; permutation.len()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Synthesize the circuit to obtain SRS
|
|
|
|
circuit.synthesize(&mut assembly, config)?;
|
|
|
|
|
|
|
|
// Compute permutation polynomials, convert to coset form and
|
|
|
|
// pre-compute commitments for the SRS.
|
|
|
|
let mut permutation_commitments = vec![];
|
|
|
|
let mut permutations = vec![];
|
|
|
|
let mut permutation_polys = vec![];
|
|
|
|
let mut permutation_cosets = vec![];
|
|
|
|
for (permutation_index, permutation) in cs.permutations.iter().enumerate() {
|
|
|
|
let mut commitments = vec![];
|
|
|
|
let mut inner_permutations = vec![];
|
|
|
|
let mut polys = vec![];
|
|
|
|
let mut cosets = vec![];
|
|
|
|
for i in 0..permutation.len() {
|
|
|
|
// Computes the permutation polynomial based on the permutation
|
|
|
|
// description in the assembly.
|
|
|
|
let mut permutation_poly = domain.empty_lagrange();
|
|
|
|
for (j, p) in permutation_poly.iter_mut().enumerate() {
|
|
|
|
let (permuted_i, permuted_j) = assembly.mapping[permutation_index][i][j];
|
|
|
|
*p = deltaomega[permuted_i][permuted_j];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute commitment to permutation polynomial
|
|
|
|
commitments.push(
|
|
|
|
params
|
|
|
|
.commit_lagrange(&permutation_poly, Blind::default())
|
|
|
|
.to_affine(),
|
|
|
|
);
|
|
|
|
// Store permutation polynomial and precompute its coset evaluation
|
|
|
|
inner_permutations.push(permutation_poly.clone());
|
|
|
|
let poly = domain.lagrange_to_coeff(permutation_poly);
|
|
|
|
polys.push(poly.clone());
|
|
|
|
cosets.push(domain.coeff_to_extended(poly, Rotation::default()));
|
|
|
|
}
|
|
|
|
permutation_commitments.push(commitments);
|
|
|
|
permutations.push(inner_permutations);
|
|
|
|
permutation_polys.push(polys);
|
|
|
|
permutation_cosets.push(cosets);
|
|
|
|
}
|
|
|
|
|
|
|
|
let fixed_commitments = assembly
|
|
|
|
.fixed
|
|
|
|
.iter()
|
|
|
|
.map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let fixed_polys: Vec<_> = assembly
|
|
|
|
.fixed
|
|
|
|
.into_iter()
|
|
|
|
.map(|poly| domain.lagrange_to_coeff(poly))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let fixed_cosets = cs
|
|
|
|
.fixed_queries
|
|
|
|
.iter()
|
2020-11-05 19:13:54 -08:00
|
|
|
.map(|&(column, at)| {
|
2020-11-09 08:45:52 -08:00
|
|
|
let poly = fixed_polys[column.index()].clone();
|
2020-09-29 07:25:04 -07:00
|
|
|
domain.coeff_to_extended(poly, at)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
// Compute l_0(X)
|
|
|
|
// TODO: this can be done more efficiently
|
|
|
|
let mut l0 = domain.empty_lagrange();
|
|
|
|
l0[0] = C::Scalar::one();
|
|
|
|
let l0 = domain.lagrange_to_coeff(l0);
|
|
|
|
let l0 = domain.coeff_to_extended(l0, Rotation::default());
|
|
|
|
|
|
|
|
Ok(ProvingKey {
|
|
|
|
vk: VerifyingKey {
|
|
|
|
domain,
|
|
|
|
fixed_commitments,
|
|
|
|
permutation_commitments,
|
|
|
|
cs,
|
|
|
|
},
|
|
|
|
l0,
|
|
|
|
fixed_polys,
|
|
|
|
fixed_cosets,
|
|
|
|
permutations,
|
|
|
|
permutation_polys,
|
|
|
|
permutation_cosets,
|
|
|
|
})
|
|
|
|
}
|