#![allow(clippy::int_plus_one)] use std::ops::Range; use ff::{Field, FromUniformBytes}; use group::Curve; use super::{ circuit::{ Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Instance, Selector, }, permutation, Assigned, Error, LagrangeCoeff, Polynomial, ProvingKey, VerifyingKey, }; use crate::{ arithmetic::CurveAffine, circuit::Value, poly::{ batch_invert_assigned, commitment::{Blind, Params}, EvaluationDomain, }, }; 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); let degree = cs.degree(); let domain = EvaluationDomain::new(degree as u32, params.k); (domain, cs, config) } /// Assembly to be used in circuit synthesis. #[derive(Debug)] struct Assembly { k: u32, fixed: Vec, LagrangeCoeff>>, permutation: permutation::keygen::Assembly, selectors: Vec>, // A range of available rows for assignment and copies. usable_rows: Range, _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 enable_selector(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error> where A: FnOnce() -> AR, AR: Into, { if !self.usable_rows.contains(&row) { return Err(Error::not_enough_rows_available(self.k)); } self.selectors[selector.0][row] = true; Ok(()) } fn query_instance(&self, _: Column, row: usize) -> Result, Error> { if !self.usable_rows.contains(&row) { return Err(Error::not_enough_rows_available(self.k)); } // There is no instance in this context. Ok(Value::unknown()) } fn assign_advice( &mut self, _: A, _: Column, _: usize, _: V, ) -> Result<(), Error> where V: FnOnce() -> Value, VR: Into>, 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() -> Value, VR: Into>, A: FnOnce() -> AR, AR: Into, { if !self.usable_rows.contains(&row) { return Err(Error::not_enough_rows_available(self.k)); } *self .fixed .get_mut(column.index()) .and_then(|v| v.get_mut(row)) .ok_or(Error::BoundsFailure)? = to().into_field().assign()?; Ok(()) } fn copy( &mut self, left_column: Column, left_row: usize, right_column: Column, right_row: usize, ) -> Result<(), Error> { if !self.usable_rows.contains(&left_row) || !self.usable_rows.contains(&right_row) { return Err(Error::not_enough_rows_available(self.k)); } self.permutation .copy(left_column, left_row, right_column, right_row) } fn fill_from_row( &mut self, column: Column, from_row: usize, to: Value>, ) -> Result<(), Error> { if !self.usable_rows.contains(&from_row) { return Err(Error::not_enough_rows_available(self.k)); } let col = self .fixed .get_mut(column.index()) .ok_or(Error::BoundsFailure)?; let filler = to.assign()?; for row in self.usable_rows.clone().skip(from_row) { col[row] = filler; } Ok(()) } 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, C::Scalar: FromUniformBytes<64>, ConcreteCircuit: Circuit, { let (domain, cs, config) = create_domain::(params); if (params.n as usize) < cs.minimum_rows() { return Err(Error::not_enough_rows_available(params.k)); } let mut assembly: Assembly = Assembly { k: params.k, fixed: vec![domain.empty_lagrange_assigned(); cs.num_fixed_columns], permutation: permutation::keygen::Assembly::new(params.n as usize, &cs.permutation), selectors: vec![vec![false; params.n as usize]; cs.num_selectors], usable_rows: 0..params.n as usize - (cs.blinding_factors() + 1), _marker: std::marker::PhantomData, }; // Synthesize the circuit to obtain URS ConcreteCircuit::FloorPlanner::synthesize( &mut assembly, circuit, config, cs.constants.clone(), )?; let mut fixed = batch_invert_assigned(assembly.fixed); let (cs, selector_polys) = cs.compress_selectors(assembly.selectors); fixed.extend( selector_polys .into_iter() .map(|poly| domain.lagrange_from_vec(poly)), ); let permutation_vk = assembly .permutation .build_vk(params, &domain, &cs.permutation); let fixed_commitments = fixed .iter() .map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine()) .collect(); Ok(VerifyingKey::from_parts( domain, fixed_commitments, permutation_vk, 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 cs = cs; if (params.n as usize) < cs.minimum_rows() { return Err(Error::not_enough_rows_available(params.k)); } let mut assembly: Assembly = Assembly { k: params.k, fixed: vec![vk.domain.empty_lagrange_assigned(); cs.num_fixed_columns], permutation: permutation::keygen::Assembly::new(params.n as usize, &cs.permutation), selectors: vec![vec![false; params.n as usize]; cs.num_selectors], usable_rows: 0..params.n as usize - (cs.blinding_factors() + 1), _marker: std::marker::PhantomData, }; // Synthesize the circuit to obtain URS ConcreteCircuit::FloorPlanner::synthesize( &mut assembly, circuit, config, cs.constants.clone(), )?; let mut fixed = batch_invert_assigned(assembly.fixed); let (cs, selector_polys) = cs.compress_selectors(assembly.selectors); fixed.extend( selector_polys .into_iter() .map(|poly| vk.domain.lagrange_from_vec(poly)), ); let fixed_polys: Vec<_> = fixed .iter() .map(|poly| vk.domain.lagrange_to_coeff(poly.clone())) .collect(); let fixed_cosets = fixed_polys .iter() .map(|poly| vk.domain.coeff_to_extended(poly.clone())) .collect(); let permutation_pk = assembly .permutation .build_pk(params, &vk.domain, &cs.permutation); // 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); // Compute l_blind(X) which evaluates to 1 for each blinding factor row // and 0 otherwise over the domain. let mut l_blind = vk.domain.empty_lagrange(); for evaluation in l_blind[..].iter_mut().rev().take(cs.blinding_factors()) { *evaluation = C::Scalar::ONE; } let l_blind = vk.domain.lagrange_to_coeff(l_blind); let l_blind = vk.domain.coeff_to_extended(l_blind); // Compute l_last(X) which evaluates to 1 on the first inactive row (just // before the blinding factors) and 0 otherwise over the domain let mut l_last = vk.domain.empty_lagrange(); l_last[params.n as usize - cs.blinding_factors() - 1] = C::Scalar::ONE; let l_last = vk.domain.lagrange_to_coeff(l_last); let l_last = vk.domain.coeff_to_extended(l_last); Ok(ProvingKey { vk, l0, l_blind, l_last, fixed_values: fixed, fixed_polys, fixed_cosets, permutation: permutation_pk, }) }