Remove SRS and replace with ProvingKey/VerifyingKey abstractions

Co-authored-by: therealyingtong <yingtong@electriccoin.co>
This commit is contained in:
Sean Bowe 2020-09-29 08:25:04 -06:00
parent 865160ca88
commit c97da352ee
No known key found for this signature in database
GPG key ID: 95684257D8F8B031
5 changed files with 337 additions and 313 deletions

View file

@ -12,30 +12,37 @@ use crate::poly::{
use crate::transcript::Hasher; use crate::transcript::Hasher;
mod circuit; mod circuit;
mod keygen;
mod prover; mod prover;
mod srs;
mod verifier; mod verifier;
pub use circuit::*; pub use circuit::*;
pub use keygen::*;
pub use prover::*; pub use prover::*;
pub use srs::*;
pub use verifier::*; pub use verifier::*;
/// This is a structured reference string (SRS) that is (deterministically) /// This is a verifying key which allows for the verification of proofs for a
/// computed from a specific circuit and parameters for the polynomial /// particular circuit.
/// commitment scheme.
#[derive(Debug)] #[derive(Debug)]
pub struct SRS<C: CurveAffine> { pub struct VerifyingKey<C: CurveAffine> {
domain: EvaluationDomain<C::Scalar>, domain: EvaluationDomain<C::Scalar>,
l0: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
fixed_commitments: Vec<C>, fixed_commitments: Vec<C>,
permutation_commitments: Vec<Vec<C>>,
cs: ConstraintSystem<C::Scalar>,
}
/// This is a proving key which allows for the creation of proofs for a
/// particular circuit.
#[derive(Debug)]
pub struct ProvingKey<C: CurveAffine> {
vk: VerifyingKey<C>,
// TODO: get rid of this?
l0: Polynomial<C::Scalar, ExtendedLagrangeCoeff>,
fixed_polys: Vec<Polynomial<C::Scalar, Coeff>>, fixed_polys: Vec<Polynomial<C::Scalar, Coeff>>,
fixed_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>, fixed_cosets: Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>,
permutation_commitments: Vec<Vec<C>>,
permutations: Vec<Vec<Polynomial<C::Scalar, LagrangeCoeff>>>, permutations: Vec<Vec<Polynomial<C::Scalar, LagrangeCoeff>>>,
permutation_polys: Vec<Vec<Polynomial<C::Scalar, Coeff>>>, permutation_polys: Vec<Vec<Polynomial<C::Scalar, Coeff>>>,
permutation_cosets: Vec<Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>>, permutation_cosets: Vec<Vec<Polynomial<C::Scalar, ExtendedLagrangeCoeff>>>,
cs: ConstraintSystem<C::Scalar>,
} }
/// This is an object which represents a (Turbo)PLONK proof. /// This is an object which represents a (Turbo)PLONK proof.
@ -75,6 +82,20 @@ pub enum Error {
OpeningError, OpeningError,
} }
impl<C: CurveAffine> ProvingKey<C> {
/// Get the underlying `VerifyingKey`
pub fn get_vk(&self) -> &VerifyingKey<C> {
&self.vk
}
}
impl<C: CurveAffine> VerifyingKey<C> {
/// Get the underlying `VerifyingKey`
pub fn get_domain(&self) -> &EvaluationDomain<C::Scalar> {
&self.domain
}
}
fn hash_point<C: CurveAffine, H: Hasher<C::Base>>( fn hash_point<C: CurveAffine, H: Hasher<C::Base>>(
transcript: &mut H, transcript: &mut H,
point: &C, point: &C,
@ -368,10 +389,10 @@ fn test_proving() {
let empty_circuit: MyCircuit<Fp> = MyCircuit { a: None }; let empty_circuit: MyCircuit<Fp> = MyCircuit { a: None };
// Initialize the SRS // Initialize the proving key
let srs = SRS::generate(&params, &empty_circuit).expect("SRS generation should not fail"); let pk = keygen(&params, &empty_circuit).expect("keygen should not fail");
let mut pubinputs = srs.domain.empty_lagrange(); let mut pubinputs = pk.get_vk().get_domain().empty_lagrange();
pubinputs[0] = Fp::one(); pubinputs[0] = Fp::one();
pubinputs[0] += Fp::one(); pubinputs[0] += Fp::one();
let pubinput = params let pubinput = params
@ -382,7 +403,7 @@ fn test_proving() {
// Create a proof // Create a proof
let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>( let proof = Proof::create::<DummyHash<Fq>, DummyHash<Fp>, _>(
&params, &params,
&srs, &pk,
&circuit, &circuit,
&[pubinputs.clone()], &[pubinputs.clone()],
) )
@ -390,7 +411,7 @@ fn test_proving() {
let msm = params.empty_msm(); let msm = params.empty_msm();
let guard = proof let guard = proof
.verify::<DummyHash<Fq>, DummyHash<Fp>>(&params, &srs, msm, &[pubinput]) .verify::<DummyHash<Fq>, DummyHash<Fp>>(&params, pk.get_vk(), msm, &[pubinput])
.unwrap(); .unwrap();
{ {
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
@ -404,7 +425,7 @@ fn test_proving() {
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();
assert!(msm.clone().eval()); assert!(msm.clone().eval());
let guard = proof let guard = proof
.verify::<DummyHash<Fq>, DummyHash<Fp>>(&params, &srs, msm, &[pubinput]) .verify::<DummyHash<Fq>, DummyHash<Fp>>(&params, pk.get_vk(), msm, &[pubinput])
.unwrap(); .unwrap();
{ {
let msm = guard.clone().use_challenges(); let msm = guard.clone().use_challenges();

259
src/plonk/keygen.rs Normal file
View file

@ -0,0 +1,259 @@
use super::{
circuit::{AdviceWire, Assignment, Circuit, ConstraintSystem, FixedWire},
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,
_: AdviceWire,
_: usize,
_: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
// We only care about fixed wires here
Ok(())
}
fn assign_fixed(
&mut self,
wire: FixedWire,
row: usize,
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
*self
.fixed
.get_mut(wire.0)
.and_then(|v| v.get_mut(row))
.ok_or(Error::BoundsFailure)? = to()?;
Ok(())
}
fn copy(
&mut self,
permutation: usize,
left_wire: usize,
left_row: usize,
right_wire: usize,
right_row: usize,
) -> Result<(), Error> {
// Check bounds first
if permutation >= self.mapping.len()
|| left_wire >= self.mapping[permutation].len()
|| left_row >= self.mapping[permutation][left_wire].len()
|| right_wire >= self.mapping[permutation].len()
|| right_row >= self.mapping[permutation][right_wire].len()
{
return Err(Error::BoundsFailure);
}
let mut left_cycle = self.aux[permutation][left_wire][left_row];
let mut right_cycle = self.aux[permutation][right_wire][right_row];
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;
}
}
let tmp = self.mapping[permutation][left_wire][left_row];
self.mapping[permutation][left_wire][left_row] =
self.mapping[permutation][right_wire][right_row];
self.mapping[permutation][right_wire][right_row] = tmp;
Ok(())
}
}
let mut cs = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut cs);
// Get the largest permutation argument length in terms of the number of
// advice wires involved.
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 {
fixed: vec![domain.empty_lagrange(); cs.num_fixed_wires],
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 {
let mut wires = vec![];
for i in 0..permutation.len() {
// Computes [(i, 0), (i, 1), ..., (i, n - 1)]
wires.push((0..params.n).map(|j| (i, j as usize)).collect());
}
assembly.mapping.push(wires.clone());
assembly.aux.push(wires);
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()
.map(|&(wire, at)| {
let poly = fixed_polys[wire.0].clone();
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,
})
}

View file

@ -1,6 +1,6 @@
use super::{ use super::{
circuit::{AdviceWire, Assignment, Circuit, ConstraintSystem, FixedWire}, circuit::{AdviceWire, Assignment, Circuit, ConstraintSystem, FixedWire},
hash_point, Error, Proof, SRS, hash_point, Error, Proof, ProvingKey,
}; };
use crate::arithmetic::{ use crate::arithmetic::{
eval_polynomial, get_challenge_scalar, kate_division, parallelize, BatchInvert, Challenge, eval_polynomial, get_challenge_scalar, kate_division, parallelize, BatchInvert, Challenge,
@ -14,19 +14,19 @@ use crate::transcript::Hasher;
impl<C: CurveAffine> Proof<C> { impl<C: CurveAffine> Proof<C> {
/// This creates a proof for the provided `circuit` when given the public /// This creates a proof for the provided `circuit` when given the public
/// parameters `params` and the structured reference string `srs` that was /// parameters `params` and the proving key [`ProvingKey`] that was
/// previously computed for the same circuit. /// generated previously for the same circuit.
pub fn create< pub fn create<
HBase: Hasher<C::Base>, HBase: Hasher<C::Base>,
HScalar: Hasher<C::Scalar>, HScalar: Hasher<C::Scalar>,
ConcreteCircuit: Circuit<C::Scalar>, ConcreteCircuit: Circuit<C::Scalar>,
>( >(
params: &Params<C>, params: &Params<C>,
srs: &SRS<C>, pk: &ProvingKey<C>,
circuit: &ConcreteCircuit, circuit: &ConcreteCircuit,
aux: &[Polynomial<C::Scalar, LagrangeCoeff>], aux: &[Polynomial<C::Scalar, LagrangeCoeff>],
) -> Result<Self, Error> { ) -> Result<Self, Error> {
if aux.len() != srs.cs.num_aux_wires { if aux.len() != pk.vk.cs.num_aux_wires {
return Err(Error::IncompatibleParams); return Err(Error::IncompatibleParams);
} }
@ -76,7 +76,7 @@ impl<C: CurveAffine> Proof<C> {
} }
} }
let domain = &srs.domain; let domain = &pk.vk.domain;
let mut meta = ConstraintSystem::default(); let mut meta = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut meta); let config = ConcreteCircuit::configure(&mut meta);
@ -176,7 +176,7 @@ impl<C: CurveAffine> Proof<C> {
// Iterate over each permutation // Iterate over each permutation
let mut permutation_modified_advice = vec![]; let mut permutation_modified_advice = vec![];
for (wires, permuted_values) in srs.cs.permutations.iter().zip(srs.permutations.iter()) { for (wires, permuted_values) in pk.vk.cs.permutations.iter().zip(pk.permutations.iter()) {
// Goal is to compute the products of fractions // Goal is to compute the products of fractions
// //
// (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) / // (p_j(\omega^i) + \delta^j \omega^i \beta + \gamma) /
@ -209,7 +209,8 @@ impl<C: CurveAffine> Proof<C> {
.flat_map(|v| v.iter_mut()) .flat_map(|v| v.iter_mut())
.batch_invert(); .batch_invert();
for (wires, mut modified_advice) in srs for (wires, mut modified_advice) in pk
.vk
.cs .cs
.permutations .permutations
.iter() .iter()
@ -287,7 +288,7 @@ impl<C: CurveAffine> Proof<C> {
h_poly = h_poly * x_2; h_poly = h_poly * x_2;
let evaluation = poly.evaluate( let evaluation = poly.evaluate(
&|index| srs.fixed_cosets[index].clone(), &|index| pk.fixed_cosets[index].clone(),
&|index| advice_cosets[index].clone(), &|index| advice_cosets[index].clone(),
&|index| aux_cosets[index].clone(), &|index| aux_cosets[index].clone(),
&|a, b| a + &b, &|a, b| a + &b,
@ -304,7 +305,7 @@ impl<C: CurveAffine> Proof<C> {
for ((h, c), l0) in h for ((h, c), l0) in h
.iter_mut() .iter_mut()
.zip(coset[start..].iter()) .zip(coset[start..].iter())
.zip(srs.l0[start..].iter()) .zip(pk.l0[start..].iter())
{ {
*h *= &x_2; *h *= &x_2;
*h += &(*l0 * &(C::Scalar::one() - c)); *h += &(*l0 * &(C::Scalar::one() - c));
@ -313,14 +314,14 @@ impl<C: CurveAffine> Proof<C> {
} }
// z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma) // z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma)
for (permutation_index, wires) in srs.cs.permutations.iter().enumerate() { for (permutation_index, wires) in pk.vk.cs.permutations.iter().enumerate() {
h_poly = h_poly * x_2; h_poly = h_poly * x_2;
let mut left = permutation_product_cosets[permutation_index].clone(); let mut left = permutation_product_cosets[permutation_index].clone();
for (advice, permutation) in wires for (advice, permutation) in wires
.iter() .iter()
.map(|&(_, index)| &advice_cosets[index]) .map(|&(_, index)| &advice_cosets[index])
.zip(srs.permutation_cosets[permutation_index].iter()) .zip(pk.permutation_cosets[permutation_index].iter())
{ {
parallelize(&mut left, |left, start| { parallelize(&mut left, |left, start| {
for ((left, advice), permutation) in left for ((left, advice), permutation) in left
@ -402,7 +403,7 @@ impl<C: CurveAffine> Proof<C> {
.fixed_queries .fixed_queries
.iter() .iter()
.map(|&(wire, at)| { .map(|&(wire, at)| {
eval_polynomial(&srs.fixed_polys[wire.0], domain.rotate_omega(x_3, at)) eval_polynomial(&pk.fixed_polys[wire.0], domain.rotate_omega(x_3, at))
}) })
.collect(); .collect();
@ -416,7 +417,7 @@ impl<C: CurveAffine> Proof<C> {
.map(|poly| eval_polynomial(poly, domain.rotate_omega(x_3, Rotation(-1)))) .map(|poly| eval_polynomial(poly, domain.rotate_omega(x_3, Rotation(-1))))
.collect(); .collect();
let permutation_evals: Vec<Vec<C::Scalar>> = srs let permutation_evals: Vec<Vec<C::Scalar>> = pk
.permutation_polys .permutation_polys
.iter() .iter()
.map(|polys| { .map(|polys| {
@ -511,7 +512,7 @@ impl<C: CurveAffine> Proof<C> {
accumulate( accumulate(
point_index, point_index,
&srs.fixed_polys[wire.0], &pk.fixed_polys[wire.0],
Blind::default(), Blind::default(),
fixed_evals[query_index], fixed_evals[query_index],
); );
@ -528,7 +529,7 @@ impl<C: CurveAffine> Proof<C> {
} }
// Handle permutation arguments, if any exist // Handle permutation arguments, if any exist
if !srs.cs.permutations.is_empty() { if !pk.vk.cs.permutations.is_empty() {
// Open permutation product commitments at x_3 // Open permutation product commitments at x_3
for ((poly, blind), eval) in permutation_product_polys for ((poly, blind), eval) in permutation_product_polys
.iter() .iter()
@ -539,7 +540,7 @@ impl<C: CurveAffine> Proof<C> {
} }
// Open permutation polynomial commitments at x_3 // Open permutation polynomial commitments at x_3
for (poly, eval) in srs for (poly, eval) in pk
.permutation_polys .permutation_polys
.iter() .iter()
.zip(permutation_evals.iter()) .zip(permutation_evals.iter())
@ -548,7 +549,7 @@ impl<C: CurveAffine> Proof<C> {
accumulate(current_index, poly, Blind::default(), *eval); accumulate(current_index, poly, Blind::default(), *eval);
} }
let current_index = (*srs.cs.rotations.get(&Rotation(-1)).unwrap()).0; let current_index = (*pk.vk.cs.rotations.get(&Rotation(-1)).unwrap()).0;
// Open permutation product commitments at \omega^{-1} x_3 // Open permutation product commitments at \omega^{-1} x_3
for ((poly, blind), eval) in permutation_product_polys for ((poly, blind), eval) in permutation_product_polys
.iter() .iter()

View file

@ -1,257 +0,0 @@
use super::{
circuit::{AdviceWire, Assignment, Circuit, ConstraintSystem, FixedWire},
Error, SRS,
};
use crate::arithmetic::{Curve, CurveAffine, Field};
use crate::poly::{
commitment::{Blind, Params},
EvaluationDomain, LagrangeCoeff, Polynomial, Rotation,
};
impl<C: CurveAffine> SRS<C> {
/// This generates a structured reference string for the provided `circuit`
/// and `params`.
pub fn generate<ConcreteCircuit: Circuit<C::Scalar>>(
params: &Params<C>,
circuit: &ConcreteCircuit,
) -> Result<Self, Error> {
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,
_: AdviceWire,
_: usize,
_: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
// We only care about fixed wires here
Ok(())
}
fn assign_fixed(
&mut self,
wire: FixedWire,
row: usize,
to: impl FnOnce() -> Result<F, Error>,
) -> Result<(), Error> {
*self
.fixed
.get_mut(wire.0)
.and_then(|v| v.get_mut(row))
.ok_or(Error::BoundsFailure)? = to()?;
Ok(())
}
fn copy(
&mut self,
permutation: usize,
left_wire: usize,
left_row: usize,
right_wire: usize,
right_row: usize,
) -> Result<(), Error> {
// Check bounds first
if permutation >= self.mapping.len()
|| left_wire >= self.mapping[permutation].len()
|| left_row >= self.mapping[permutation][left_wire].len()
|| right_wire >= self.mapping[permutation].len()
|| right_row >= self.mapping[permutation][right_wire].len()
{
return Err(Error::BoundsFailure);
}
let mut left_cycle = self.aux[permutation][left_wire][left_row];
let mut right_cycle = self.aux[permutation][right_wire][right_row];
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;
}
}
let tmp = self.mapping[permutation][left_wire][left_row];
self.mapping[permutation][left_wire][left_row] =
self.mapping[permutation][right_wire][right_row];
self.mapping[permutation][right_wire][right_row] = tmp;
Ok(())
}
}
let mut cs = ConstraintSystem::default();
let config = ConcreteCircuit::configure(&mut cs);
// Get the largest permutation argument length in terms of the number of
// advice wires involved.
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 {
fixed: vec![domain.empty_lagrange(); cs.num_fixed_wires],
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 {
let mut wires = vec![];
for i in 0..permutation.len() {
// Computes [(i, 0), (i, 1), ..., (i, n - 1)]
wires.push((0..params.n).map(|j| (i, j as usize)).collect());
}
assembly.mapping.push(wires.clone());
assembly.aux.push(wires);
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()
.map(|&(wire, at)| {
let poly = fixed_polys[wire.0].clone();
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(SRS {
domain,
l0,
fixed_commitments,
fixed_polys,
fixed_cosets,
permutation_commitments,
permutations,
permutation_polys,
permutation_cosets,
cs,
})
}
}

View file

@ -1,4 +1,4 @@
use super::{hash_point, Error, Proof, SRS}; use super::{hash_point, Error, Proof, VerifyingKey};
use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field}; use crate::arithmetic::{get_challenge_scalar, Challenge, CurveAffine, Field};
use crate::poly::{ use crate::poly::{
commitment::{Guard, Params, MSM}, commitment::{Guard, Params, MSM},
@ -11,14 +11,14 @@ impl<'a, C: CurveAffine> Proof<C> {
pub fn verify<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>( pub fn verify<HBase: Hasher<C::Base>, HScalar: Hasher<C::Scalar>>(
&self, &self,
params: &'a Params<C>, params: &'a Params<C>,
srs: &SRS<C>, vk: &VerifyingKey<C>,
mut msm: MSM<'a, C>, mut msm: MSM<'a, C>,
aux_commitments: &[C], aux_commitments: &[C],
) -> Result<Guard<'a, C>, Error> { ) -> Result<Guard<'a, C>, Error> {
// Check that aux_commitments matches the expected number of aux_wires // Check that aux_commitments matches the expected number of aux_wires
// and self.aux_evals // and self.aux_evals
if aux_commitments.len() != srs.cs.num_aux_wires if aux_commitments.len() != vk.cs.num_aux_wires
|| self.aux_evals.len() != srs.cs.num_aux_wires || self.aux_evals.len() != vk.cs.num_aux_wires
{ {
return Err(Error::IncompatibleParams); return Err(Error::IncompatibleParams);
} }
@ -88,7 +88,7 @@ impl<'a, C: CurveAffine> Proof<C> {
// Evaluate the circuit using the custom gates provided // Evaluate the circuit using the custom gates provided
let mut h_eval = C::Scalar::zero(); let mut h_eval = C::Scalar::zero();
for poly in srs.cs.gates.iter() { for poly in vk.cs.gates.iter() {
h_eval *= &x_2; h_eval *= &x_2;
let evaluation: C::Scalar = poly.evaluate( let evaluation: C::Scalar = poly.evaluate(
@ -114,7 +114,7 @@ impl<'a, C: CurveAffine> Proof<C> {
let mut tmp = denominator; // 1 / (x_3 - 1) let mut tmp = denominator; // 1 / (x_3 - 1)
tmp *= &(x_3n - &C::Scalar::one()); // (x_3^n - 1) / (x_3 - 1) tmp *= &(x_3n - &C::Scalar::one()); // (x_3^n - 1) / (x_3 - 1)
tmp *= &srs.domain.get_barycentric_weight(); // l_0(x_3) tmp *= &vk.domain.get_barycentric_weight(); // l_0(x_3)
tmp *= &(C::Scalar::one() - &eval); // l_0(X) * (1 - z(X)) tmp *= &(C::Scalar::one() - &eval); // l_0(X) * (1 - z(X))
h_eval += &tmp; h_eval += &tmp;
@ -122,7 +122,7 @@ impl<'a, C: CurveAffine> Proof<C> {
} }
// z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma) // z(X) \prod (p(X) + \beta s_i(X) + \gamma) - z(omega^{-1} X) \prod (p(X) + \delta^i \beta X + \gamma)
for (permutation_index, wires) in srs.cs.permutations.iter().enumerate() { for (permutation_index, wires) in vk.cs.permutations.iter().enumerate() {
h_eval *= &x_2; h_eval *= &x_2;
let mut left = self.permutation_product_evals[permutation_index]; let mut left = self.permutation_product_evals[permutation_index];
@ -168,8 +168,8 @@ impl<'a, C: CurveAffine> Proof<C> {
// Compress the commitments and expected evaluations at x_3 together // Compress the commitments and expected evaluations at x_3 together
// using the challenge x_4 // using the challenge x_4
let mut q_commitments: Vec<_> = vec![params.empty_msm(); srs.cs.rotations.len()]; let mut q_commitments: Vec<_> = vec![params.empty_msm(); vk.cs.rotations.len()];
let mut q_evals: Vec<_> = vec![C::Scalar::zero(); srs.cs.rotations.len()]; let mut q_evals: Vec<_> = vec![C::Scalar::zero(); vk.cs.rotations.len()];
{ {
let mut accumulate = |point_index: usize, new_commitment, eval| { let mut accumulate = |point_index: usize, new_commitment, eval| {
q_commitments[point_index].scale(x_4); q_commitments[point_index].scale(x_4);
@ -178,8 +178,8 @@ impl<'a, C: CurveAffine> Proof<C> {
q_evals[point_index] += &eval; q_evals[point_index] += &eval;
}; };
for (query_index, &(wire, ref at)) in srs.cs.advice_queries.iter().enumerate() { for (query_index, &(wire, ref at)) in vk.cs.advice_queries.iter().enumerate() {
let point_index = (*srs.cs.rotations.get(at).unwrap()).0; let point_index = (*vk.cs.rotations.get(at).unwrap()).0;
accumulate( accumulate(
point_index, point_index,
self.advice_commitments[wire.0], self.advice_commitments[wire.0],
@ -187,8 +187,8 @@ impl<'a, C: CurveAffine> Proof<C> {
); );
} }
for (query_index, &(wire, ref at)) in srs.cs.aux_queries.iter().enumerate() { for (query_index, &(wire, ref at)) in vk.cs.aux_queries.iter().enumerate() {
let point_index = (*srs.cs.rotations.get(at).unwrap()).0; let point_index = (*vk.cs.rotations.get(at).unwrap()).0;
accumulate( accumulate(
point_index, point_index,
aux_commitments[wire.0], aux_commitments[wire.0],
@ -196,22 +196,22 @@ impl<'a, C: CurveAffine> Proof<C> {
); );
} }
for (query_index, &(wire, ref at)) in srs.cs.fixed_queries.iter().enumerate() { for (query_index, &(wire, ref at)) in vk.cs.fixed_queries.iter().enumerate() {
let point_index = (*srs.cs.rotations.get(at).unwrap()).0; let point_index = (*vk.cs.rotations.get(at).unwrap()).0;
accumulate( accumulate(
point_index, point_index,
srs.fixed_commitments[wire.0], vk.fixed_commitments[wire.0],
self.fixed_evals[query_index], self.fixed_evals[query_index],
); );
} }
let current_index = (*srs.cs.rotations.get(&Rotation::default()).unwrap()).0; let current_index = (*vk.cs.rotations.get(&Rotation::default()).unwrap()).0;
for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals.iter()) { for (commitment, eval) in self.h_commitments.iter().zip(self.h_evals.iter()) {
accumulate(current_index, *commitment, *eval); accumulate(current_index, *commitment, *eval);
} }
// Handle permutation arguments, if any exist // Handle permutation arguments, if any exist
if !srs.cs.permutations.is_empty() { if !vk.cs.permutations.is_empty() {
// Open permutation product commitments at x_3 // Open permutation product commitments at x_3
for (commitment, eval) in self for (commitment, eval) in self
.permutation_product_commitments .permutation_product_commitments
@ -221,7 +221,7 @@ impl<'a, C: CurveAffine> Proof<C> {
accumulate(current_index, *commitment, *eval); accumulate(current_index, *commitment, *eval);
} }
// Open permutation commitments for each permutation argument at x_3 // Open permutation commitments for each permutation argument at x_3
for (commitment, eval) in srs for (commitment, eval) in vk
.permutation_commitments .permutation_commitments
.iter() .iter()
.zip(self.permutation_evals.iter()) .zip(self.permutation_evals.iter())
@ -229,7 +229,7 @@ impl<'a, C: CurveAffine> Proof<C> {
{ {
accumulate(current_index, *commitment, *eval); accumulate(current_index, *commitment, *eval);
} }
let current_index = (*srs.cs.rotations.get(&Rotation(-1)).unwrap()).0; let current_index = (*vk.cs.rotations.get(&Rotation(-1)).unwrap()).0;
// Open permutation product commitments at \omega^{-1} x_3 // Open permutation product commitments at \omega^{-1} x_3
for (commitment, eval) in self for (commitment, eval) in self
.permutation_product_commitments .permutation_product_commitments
@ -263,10 +263,10 @@ impl<'a, C: CurveAffine> Proof<C> {
// We can compute the expected msm_eval at x_6 using the q_evals provided // We can compute the expected msm_eval at x_6 using the q_evals provided
// by the prover and from x_5 // by the prover and from x_5
let mut msm_eval = C::Scalar::zero(); let mut msm_eval = C::Scalar::zero();
for (&row, point_index) in srs.cs.rotations.iter() { for (&row, point_index) in vk.cs.rotations.iter() {
let mut eval = self.q_evals[point_index.0]; let mut eval = self.q_evals[point_index.0];
let point = srs.domain.rotate_omega(x_3, row); let point = vk.domain.rotate_omega(x_3, row);
eval = eval - &q_evals[point_index.0]; eval = eval - &q_evals[point_index.0];
eval = eval * &(x_6 - &point).invert().unwrap(); eval = eval * &(x_6 - &point).invert().unwrap();
@ -281,7 +281,7 @@ impl<'a, C: CurveAffine> Proof<C> {
// Compute the final commitment that has to be opened // Compute the final commitment that has to be opened
let mut commitment_msm = params.empty_msm(); let mut commitment_msm = params.empty_msm();
commitment_msm.add_term(C::Scalar::one(), self.f_commitment); commitment_msm.add_term(C::Scalar::one(), self.f_commitment);
for (_, &point_index) in srs.cs.rotations.iter() { for (_, &point_index) in vk.cs.rotations.iter() {
commitment_msm.scale(x_7); commitment_msm.scale(x_7);
commitment_msm.add_msm(&q_commitments[point_index.0]); commitment_msm.add_msm(&q_commitments[point_index.0]);
msm_eval *= &x_7; msm_eval *= &x_7;