halo2_proofs/plonk/
keygen.rs

1#![allow(clippy::int_plus_one)]
2
3use std::ops::Range;
4
5use ff::Field;
6use group::Curve;
7
8use super::{
9    circuit::{
10        Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Instance,
11        Selector,
12    },
13    permutation, Assigned, Error, LagrangeCoeff, Polynomial, ProvingKey, VerifyingKey,
14};
15use crate::poly::{
16    commitment::{Blind, Params},
17    EvaluationDomain,
18};
19use crate::{arithmetic::CurveAffine, poly::batch_invert_assigned};
20
21pub(crate) fn create_domain<C, ConcreteCircuit>(
22    params: &Params<C>,
23) -> (
24    EvaluationDomain<C::Scalar>,
25    ConstraintSystem<C::Scalar>,
26    ConcreteCircuit::Config,
27)
28where
29    C: CurveAffine,
30    ConcreteCircuit: Circuit<C::Scalar>,
31{
32    let mut cs = ConstraintSystem::default();
33    let config = ConcreteCircuit::configure(&mut cs);
34
35    let degree = cs.degree();
36
37    let domain = EvaluationDomain::new(degree as u32, params.k);
38
39    (domain, cs, config)
40}
41
42/// Assembly to be used in circuit synthesis.
43#[derive(Debug)]
44struct Assembly<F: Field> {
45    k: u32,
46    fixed: Vec<Polynomial<Assigned<F>, LagrangeCoeff>>,
47    permutation: permutation::keygen::Assembly,
48    selectors: Vec<Vec<bool>>,
49    // A range of available rows for assignment and copies.
50    usable_rows: Range<usize>,
51    _marker: std::marker::PhantomData<F>,
52}
53
54impl<F: Field> Assignment<F> for Assembly<F> {
55    fn enter_region<NR, N>(&mut self, _: N)
56    where
57        NR: Into<String>,
58        N: FnOnce() -> NR,
59    {
60        // Do nothing; we don't care about regions in this context.
61    }
62
63    fn exit_region(&mut self) {
64        // Do nothing; we don't care about regions in this context.
65    }
66
67    fn enable_selector<A, AR>(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error>
68    where
69        A: FnOnce() -> AR,
70        AR: Into<String>,
71    {
72        if !self.usable_rows.contains(&row) {
73            return Err(Error::not_enough_rows_available(self.k));
74        }
75
76        self.selectors[selector.0][row] = true;
77
78        Ok(())
79    }
80
81    fn query_instance(&self, _: Column<Instance>, row: usize) -> Result<Option<F>, Error> {
82        if !self.usable_rows.contains(&row) {
83            return Err(Error::not_enough_rows_available(self.k));
84        }
85
86        // There is no instance in this context.
87        Ok(None)
88    }
89
90    fn assign_advice<V, VR, A, AR>(
91        &mut self,
92        _: A,
93        _: Column<Advice>,
94        _: usize,
95        _: V,
96    ) -> Result<(), Error>
97    where
98        V: FnOnce() -> Result<VR, Error>,
99        VR: Into<Assigned<F>>,
100        A: FnOnce() -> AR,
101        AR: Into<String>,
102    {
103        // We only care about fixed columns here
104        Ok(())
105    }
106
107    fn assign_fixed<V, VR, A, AR>(
108        &mut self,
109        _: A,
110        column: Column<Fixed>,
111        row: usize,
112        to: V,
113    ) -> Result<(), Error>
114    where
115        V: FnOnce() -> Result<VR, Error>,
116        VR: Into<Assigned<F>>,
117        A: FnOnce() -> AR,
118        AR: Into<String>,
119    {
120        if !self.usable_rows.contains(&row) {
121            return Err(Error::not_enough_rows_available(self.k));
122        }
123
124        *self
125            .fixed
126            .get_mut(column.index())
127            .and_then(|v| v.get_mut(row))
128            .ok_or(Error::BoundsFailure)? = to()?.into();
129
130        Ok(())
131    }
132
133    fn copy(
134        &mut self,
135        left_column: Column<Any>,
136        left_row: usize,
137        right_column: Column<Any>,
138        right_row: usize,
139    ) -> Result<(), Error> {
140        if !self.usable_rows.contains(&left_row) || !self.usable_rows.contains(&right_row) {
141            return Err(Error::not_enough_rows_available(self.k));
142        }
143
144        self.permutation
145            .copy(left_column, left_row, right_column, right_row)
146    }
147
148    fn fill_from_row(
149        &mut self,
150        column: Column<Fixed>,
151        from_row: usize,
152        to: Option<Assigned<F>>,
153    ) -> Result<(), Error> {
154        if !self.usable_rows.contains(&from_row) {
155            return Err(Error::not_enough_rows_available(self.k));
156        }
157
158        let col = self
159            .fixed
160            .get_mut(column.index())
161            .ok_or(Error::BoundsFailure)?;
162
163        for row in self.usable_rows.clone().skip(from_row) {
164            col[row] = to.ok_or(Error::Synthesis)?;
165        }
166
167        Ok(())
168    }
169
170    fn push_namespace<NR, N>(&mut self, _: N)
171    where
172        NR: Into<String>,
173        N: FnOnce() -> NR,
174    {
175        // Do nothing; we don't care about namespaces in this context.
176    }
177
178    fn pop_namespace(&mut self, _: Option<String>) {
179        // Do nothing; we don't care about namespaces in this context.
180    }
181}
182
183/// Generate a `VerifyingKey` from an instance of `Circuit`.
184pub fn keygen_vk<C, ConcreteCircuit>(
185    params: &Params<C>,
186    circuit: &ConcreteCircuit,
187) -> Result<VerifyingKey<C>, Error>
188where
189    C: CurveAffine,
190    ConcreteCircuit: Circuit<C::Scalar>,
191{
192    let (domain, cs, config) = create_domain::<C, ConcreteCircuit>(params);
193
194    if (params.n as usize) < cs.minimum_rows() {
195        return Err(Error::not_enough_rows_available(params.k));
196    }
197
198    let mut assembly: Assembly<C::Scalar> = Assembly {
199        k: params.k,
200        fixed: vec![domain.empty_lagrange_assigned(); cs.num_fixed_columns],
201        permutation: permutation::keygen::Assembly::new(params.n as usize, &cs.permutation),
202        selectors: vec![vec![false; params.n as usize]; cs.num_selectors],
203        usable_rows: 0..params.n as usize - (cs.blinding_factors() + 1),
204        _marker: std::marker::PhantomData,
205    };
206
207    // Synthesize the circuit to obtain URS
208    ConcreteCircuit::FloorPlanner::synthesize(
209        &mut assembly,
210        circuit,
211        config,
212        cs.constants.clone(),
213    )?;
214
215    let mut fixed = batch_invert_assigned(assembly.fixed);
216    let (cs, selector_polys) = cs.compress_selectors(assembly.selectors);
217    fixed.extend(
218        selector_polys
219            .into_iter()
220            .map(|poly| domain.lagrange_from_vec(poly)),
221    );
222
223    let permutation_vk = assembly
224        .permutation
225        .build_vk(params, &domain, &cs.permutation);
226
227    let fixed_commitments = fixed
228        .iter()
229        .map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine())
230        .collect();
231
232    Ok(VerifyingKey {
233        domain,
234        fixed_commitments,
235        permutation: permutation_vk,
236        cs,
237    })
238}
239
240/// Generate a `ProvingKey` from a `VerifyingKey` and an instance of `Circuit`.
241pub fn keygen_pk<C, ConcreteCircuit>(
242    params: &Params<C>,
243    vk: VerifyingKey<C>,
244    circuit: &ConcreteCircuit,
245) -> Result<ProvingKey<C>, Error>
246where
247    C: CurveAffine,
248    ConcreteCircuit: Circuit<C::Scalar>,
249{
250    let mut cs = ConstraintSystem::default();
251    let config = ConcreteCircuit::configure(&mut cs);
252
253    let cs = cs;
254
255    if (params.n as usize) < cs.minimum_rows() {
256        return Err(Error::not_enough_rows_available(params.k));
257    }
258
259    let mut assembly: Assembly<C::Scalar> = Assembly {
260        k: params.k,
261        fixed: vec![vk.domain.empty_lagrange_assigned(); cs.num_fixed_columns],
262        permutation: permutation::keygen::Assembly::new(params.n as usize, &cs.permutation),
263        selectors: vec![vec![false; params.n as usize]; cs.num_selectors],
264        usable_rows: 0..params.n as usize - (cs.blinding_factors() + 1),
265        _marker: std::marker::PhantomData,
266    };
267
268    // Synthesize the circuit to obtain URS
269    ConcreteCircuit::FloorPlanner::synthesize(
270        &mut assembly,
271        circuit,
272        config,
273        cs.constants.clone(),
274    )?;
275
276    let mut fixed = batch_invert_assigned(assembly.fixed);
277    let (cs, selector_polys) = cs.compress_selectors(assembly.selectors);
278    fixed.extend(
279        selector_polys
280            .into_iter()
281            .map(|poly| vk.domain.lagrange_from_vec(poly)),
282    );
283
284    let fixed_polys: Vec<_> = fixed
285        .iter()
286        .map(|poly| vk.domain.lagrange_to_coeff(poly.clone()))
287        .collect();
288
289    let fixed_cosets = fixed_polys
290        .iter()
291        .map(|poly| vk.domain.coeff_to_extended(poly.clone()))
292        .collect();
293
294    let permutation_pk = assembly
295        .permutation
296        .build_pk(params, &vk.domain, &cs.permutation);
297
298    // Compute l_0(X)
299    // TODO: this can be done more efficiently
300    let mut l0 = vk.domain.empty_lagrange();
301    l0[0] = C::Scalar::one();
302    let l0 = vk.domain.lagrange_to_coeff(l0);
303    let l0 = vk.domain.coeff_to_extended(l0);
304
305    // Compute l_blind(X) which evaluates to 1 for each blinding factor row
306    // and 0 otherwise over the domain.
307    let mut l_blind = vk.domain.empty_lagrange();
308    for evaluation in l_blind[..].iter_mut().rev().take(cs.blinding_factors()) {
309        *evaluation = C::Scalar::one();
310    }
311    let l_blind = vk.domain.lagrange_to_coeff(l_blind);
312    let l_blind = vk.domain.coeff_to_extended(l_blind);
313
314    // Compute l_last(X) which evaluates to 1 on the first inactive row (just
315    // before the blinding factors) and 0 otherwise over the domain
316    let mut l_last = vk.domain.empty_lagrange();
317    l_last[params.n as usize - cs.blinding_factors() - 1] = C::Scalar::one();
318    let l_last = vk.domain.lagrange_to_coeff(l_last);
319    let l_last = vk.domain.coeff_to_extended(l_last);
320
321    Ok(ProvingKey {
322        vk,
323        l0,
324        l_blind,
325        l_last,
326        fixed_values: fixed,
327        fixed_polys,
328        fixed_cosets,
329        permutation: permutation_pk,
330    })
331}