use ff::Field; use group::Curve; use super::{ circuit::{Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed}, permutation, Error, LagrangeCoeff, Permutation, Polynomial, ProvingKey, VerifyingKey, }; use crate::arithmetic::CurveAffine; use crate::poly::{ commitment::{Blind, Params}, EvaluationDomain, Rotation, }; pub(crate) fn create_domain( params: &Params, ) -> ( EvaluationDomain, ConstraintSystem, ConcreteCircuit::Config, ) where C: CurveAffine, ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); // The permutation argument will serve alongside the gates, so must be // accounted for. let mut degree = cs .permutations .iter() .map(|p| p.required_degree()) .max() .unwrap_or(1); // The lookup argument also serves alongside the gates and must be accounted // for. degree = std::cmp::max( degree, cs.lookups .iter() .map(|l| l.required_degree()) .max() .unwrap_or(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); (domain, cs, config) } /// Assembly to be used in circuit synthesis. #[derive(Debug)] struct Assembly { fixed: Vec>, permutations: Vec, _marker: std::marker::PhantomData, } impl Assignment for Assembly { fn enter_region(&mut self, _: N) where NR: Into, N: FnOnce() -> NR, { // Do nothing; we don't care about regions in this context. } fn exit_region(&mut self) { // Do nothing; we don't care about regions in this context. } fn assign_advice( &mut self, _: A, _: Column, _: usize, _: V, ) -> Result<(), Error> where V: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { // We only care about fixed columns here Ok(()) } fn assign_fixed( &mut self, _: A, column: Column, row: usize, to: V, ) -> Result<(), Error> where V: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { *self .fixed .get_mut(column.index()) .and_then(|v| v.get_mut(row)) .ok_or(Error::BoundsFailure)? = to()?; Ok(()) } fn copy( &mut self, permutation: &Permutation, left_column: Column, left_row: usize, right_column: Column, right_row: usize, ) -> Result<(), Error> { // Check bounds first if permutation.index() >= self.permutations.len() { return Err(Error::BoundsFailure); } let left_column_index = permutation .mapping() .iter() .position(|c| c == &left_column) .ok_or(Error::SynthesisError)?; let right_column_index = permutation .mapping() .iter() .position(|c| c == &right_column) .ok_or(Error::SynthesisError)?; self.permutations[permutation.index()].copy( left_column_index, left_row, right_column_index, right_row, ) } fn push_namespace(&mut self, _: N) where NR: Into, N: FnOnce() -> NR, { // Do nothing; we don't care about namespaces in this context. } fn pop_namespace(&mut self, _: Option) { // Do nothing; we don't care about namespaces in this context. } } /// Generate a `VerifyingKey` from an instance of `Circuit`. pub fn keygen_vk( params: &Params, circuit: &ConcreteCircuit, ) -> Result, Error> where C: CurveAffine, ConcreteCircuit: Circuit, { let (domain, cs, config) = create_domain::(params); let mut assembly: Assembly = Assembly { fixed: vec![domain.empty_lagrange(); cs.num_fixed_columns], permutations: cs .permutations .iter() .map(|p| permutation::keygen::Assembly::new(params.n as usize, p)) .collect(), _marker: std::marker::PhantomData, }; // Synthesize the circuit to obtain URS circuit.synthesize(&mut assembly, config)?; let permutation_helper = permutation::keygen::Assembly::build_helper(params, &cs, &domain); let permutation_vks = cs .permutations .iter() .zip(assembly.permutations.into_iter()) .map(|(p, assembly)| assembly.build_vk(params, &domain, &permutation_helper, p)) .collect(); let fixed_commitments = assembly .fixed .iter() .map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine()) .collect(); Ok(VerifyingKey { domain, fixed_commitments, permutations: permutation_vks, cs, }) } /// Generate a `ProvingKey` from a `VerifyingKey` and an instance of `Circuit`. pub fn keygen_pk( params: &Params, vk: VerifyingKey, circuit: &ConcreteCircuit, ) -> Result, Error> where C: CurveAffine, ConcreteCircuit: Circuit, { let mut cs = ConstraintSystem::default(); let config = ConcreteCircuit::configure(&mut cs); let mut assembly: Assembly = Assembly { fixed: vec![vk.domain.empty_lagrange(); vk.cs.num_fixed_columns], permutations: vk .cs .permutations .iter() .map(|p| permutation::keygen::Assembly::new(params.n as usize, p)) .collect(), _marker: std::marker::PhantomData, }; // Synthesize the circuit to obtain URS circuit.synthesize(&mut assembly, config)?; let fixed_polys: Vec<_> = assembly .fixed .iter() .map(|poly| vk.domain.lagrange_to_coeff(poly.clone())) .collect(); let fixed_cosets = vk .cs .fixed_queries .iter() .map(|&(column, at)| { let poly = fixed_polys[column.index()].clone(); vk.domain.coeff_to_extended(poly, at) }) .collect(); let permutation_helper = permutation::keygen::Assembly::build_helper(params, &vk.cs, &vk.domain); let permutation_pks = vk .cs .permutations .iter() .zip(assembly.permutations.into_iter()) .map(|(p, assembly)| assembly.build_pk(&vk.domain, &permutation_helper, p)) .collect(); // Compute l_0(X) // TODO: this can be done more efficiently let mut l0 = vk.domain.empty_lagrange(); l0[0] = C::Scalar::one(); let l0 = vk.domain.lagrange_to_coeff(l0); let l0 = vk.domain.coeff_to_extended(l0, Rotation::cur()); Ok(ProvingKey { vk, l0, fixed_values: assembly.fixed, fixed_polys, fixed_cosets, permutations: permutation_pks, }) }