halo2_axiom/plonk/
keygen.rs

1#![allow(clippy::int_plus_one)]
2
3use std::ops::Range;
4
5use ff::{Field, FromUniformBytes};
6use group::Curve;
7
8use super::{
9    circuit::{
10        Advice, Any, Assignment, Circuit, Column, ConstraintSystem, Fixed, FloorPlanner, Instance,
11        Selector,
12    },
13    evaluation::Evaluator,
14    permutation, Assigned, Challenge, Error, LagrangeCoeff, Polynomial, ProvingKey, VerifyingKey,
15};
16use crate::{
17    arithmetic::{parallelize, CurveAffine},
18    circuit::Value,
19    multicore::{IntoParallelIterator, ParallelIterator},
20    poly::{
21        batch_invert_assigned,
22        commitment::{Blind, Params},
23        EvaluationDomain,
24    },
25};
26
27pub(crate) fn create_domain<C, ConcreteCircuit>(
28    k: u32,
29    #[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params,
30) -> (
31    EvaluationDomain<C::Scalar>,
32    ConstraintSystem<C::Scalar>,
33    ConcreteCircuit::Config,
34)
35where
36    C: CurveAffine,
37    ConcreteCircuit: Circuit<C::Scalar>,
38{
39    let mut cs = ConstraintSystem::default();
40    #[cfg(feature = "circuit-params")]
41    let config = ConcreteCircuit::configure_with_params(&mut cs, params);
42    #[cfg(not(feature = "circuit-params"))]
43    let config = ConcreteCircuit::configure(&mut cs);
44
45    let degree = cs.degree();
46
47    let domain = EvaluationDomain::new(degree as u32, k);
48
49    (domain, cs, config)
50}
51
52/// Assembly to be used in circuit synthesis.
53#[derive(Debug)]
54struct Assembly<F: Field> {
55    k: u32,
56    fixed: Vec<Polynomial<Assigned<F>, LagrangeCoeff>>,
57    permutation: permutation::keygen::Assembly,
58    selectors: Vec<Vec<bool>>,
59    // A range of available rows for assignment and copies.
60    usable_rows: Range<usize>,
61    _marker: std::marker::PhantomData<F>,
62}
63
64impl<F: Field> Assignment<F> for Assembly<F> {
65    fn enter_region<NR, N>(&mut self, _: N)
66    where
67        NR: Into<String>,
68        N: FnOnce() -> NR,
69    {
70        // Do nothing; we don't care about regions in this context.
71    }
72
73    fn exit_region(&mut self) {
74        // Do nothing; we don't care about regions in this context.
75    }
76
77    fn enable_selector<A, AR>(&mut self, _: A, selector: &Selector, row: usize) -> Result<(), Error>
78    where
79        A: FnOnce() -> AR,
80        AR: Into<String>,
81    {
82        if !self.usable_rows.contains(&row) {
83            return Err(Error::not_enough_rows_available(self.k));
84        }
85
86        self.selectors[selector.0][row] = true;
87
88        Ok(())
89    }
90
91    fn query_instance(&self, _: Column<Instance>, row: usize) -> Result<Value<F>, Error> {
92        if !self.usable_rows.contains(&row) {
93            return Err(Error::not_enough_rows_available(self.k));
94        }
95
96        // There is no instance in this context.
97        Ok(Value::unknown())
98    }
99
100    fn assign_advice<'v>(
101        //<V, VR, A, AR>(
102        &mut self,
103        //_: A,
104        _: Column<Advice>,
105        _: usize,
106        _: Value<Assigned<F>>,
107    ) -> Value<&'v Assigned<F>> {
108        Value::unknown()
109    }
110
111    fn assign_fixed(&mut self, column: Column<Fixed>, row: usize, to: Assigned<F>) {
112        if !self.usable_rows.contains(&row) {
113            panic!(
114                "Assign Fixed {:?}",
115                Error::not_enough_rows_available(self.k)
116            );
117        }
118
119        *self
120            .fixed
121            .get_mut(column.index())
122            .and_then(|v| v.get_mut(row))
123            .unwrap_or_else(|| panic!("{:?}", Error::BoundsFailure)) = to;
124    }
125
126    fn copy(
127        &mut self,
128        left_column: Column<Any>,
129        left_row: usize,
130        right_column: Column<Any>,
131        right_row: usize,
132    ) {
133        if !self.usable_rows.contains(&left_row) || !self.usable_rows.contains(&right_row) {
134            panic!("{:?}", Error::not_enough_rows_available(self.k));
135        }
136
137        self.permutation
138            .copy(left_column, left_row, right_column, right_row)
139            .unwrap_or_else(|err| panic!("{err:?}"))
140    }
141
142    fn fill_from_row(
143        &mut self,
144        column: Column<Fixed>,
145        from_row: usize,
146        to: Value<Assigned<F>>,
147    ) -> Result<(), Error> {
148        if !self.usable_rows.contains(&from_row) {
149            return Err(Error::not_enough_rows_available(self.k));
150        }
151
152        let col = self
153            .fixed
154            .get_mut(column.index())
155            .ok_or(Error::BoundsFailure)?;
156
157        let filler = to.assign()?;
158        for row in self.usable_rows.clone().skip(from_row) {
159            col[row] = filler;
160        }
161
162        Ok(())
163    }
164
165    fn get_challenge(&self, _: Challenge) -> Value<F> {
166        Value::unknown()
167    }
168
169    fn annotate_column<A, AR>(&mut self, _annotation: A, _column: Column<Any>)
170    where
171        A: FnOnce() -> AR,
172        AR: Into<String>,
173    {
174        // Do nothing
175    }
176
177    fn push_namespace<NR, N>(&mut self, _: N)
178    where
179        NR: Into<String>,
180        N: FnOnce() -> NR,
181    {
182        // Do nothing; we don't care about namespaces in this context.
183    }
184
185    fn pop_namespace(&mut self, _: Option<String>) {
186        // Do nothing; we don't care about namespaces in this context.
187    }
188}
189
190/// Generate a `VerifyingKey` from an instance of `Circuit`.
191/// By default, selector compression is turned **off**.
192pub fn keygen_vk<'params, C, P, ConcreteCircuit>(
193    params: &P,
194    circuit: &ConcreteCircuit,
195) -> Result<VerifyingKey<C>, Error>
196where
197    C: CurveAffine,
198    P: Params<'params, C> + Sync,
199    ConcreteCircuit: Circuit<C::Scalar>,
200    C::Scalar: FromUniformBytes<64>,
201{
202    keygen_vk_custom(params, circuit, false)
203}
204
205/// Generate a `VerifyingKey` from an instance of `Circuit`.
206///
207/// The selector compression optimization is turned on only if `compress_selectors` is `true`.
208pub fn keygen_vk_custom<'params, C, P, ConcreteCircuit>(
209    params: &P,
210    circuit: &ConcreteCircuit,
211    compress_selectors: bool,
212) -> Result<VerifyingKey<C>, Error>
213where
214    C: CurveAffine,
215    P: Params<'params, C> + Sync,
216    ConcreteCircuit: Circuit<C::Scalar>,
217    C::Scalar: FromUniformBytes<64>,
218{
219    let (domain, cs, config) = create_domain::<C, ConcreteCircuit>(
220        params.k(),
221        #[cfg(feature = "circuit-params")]
222        circuit.params(),
223    );
224
225    if (params.n() as usize) < cs.minimum_rows() {
226        return Err(Error::not_enough_rows_available(params.k()));
227    }
228
229    let mut assembly: Assembly<C::Scalar> = Assembly {
230        k: params.k(),
231        fixed: vec![domain.empty_lagrange_assigned(); cs.num_fixed_columns],
232        permutation: permutation::keygen::Assembly::new(params.n() as usize, &cs.permutation),
233        selectors: vec![vec![false; params.n() as usize]; cs.num_selectors],
234        usable_rows: 0..params.n() as usize - (cs.blinding_factors() + 1),
235        _marker: std::marker::PhantomData,
236    };
237
238    // Synthesize the circuit to obtain URS
239    ConcreteCircuit::FloorPlanner::synthesize(
240        &mut assembly,
241        circuit,
242        config,
243        cs.constants.clone(),
244    )?;
245
246    let mut fixed = batch_invert_assigned(assembly.fixed);
247    let (cs, selector_polys) = if compress_selectors {
248        cs.compress_selectors(assembly.selectors.clone())
249    } else {
250        // After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways.
251        let selectors = std::mem::take(&mut assembly.selectors);
252        cs.directly_convert_selectors_to_fixed(selectors)
253    };
254    fixed.extend(
255        selector_polys
256            .into_iter()
257            .map(|poly| domain.lagrange_from_vec(poly)),
258    );
259
260    let permutation_vk = assembly
261        .permutation
262        .build_vk(params, &domain, &cs.permutation);
263
264    let fixed_commitments = (&fixed)
265        .into_par_iter()
266        .map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine())
267        .collect();
268
269    Ok(VerifyingKey::from_parts(
270        domain,
271        fixed_commitments,
272        permutation_vk,
273        cs,
274        assembly.selectors,
275        compress_selectors,
276    ))
277}
278
279/// Generate a `ProvingKey` from a `VerifyingKey` and an instance of `Circuit`.
280pub fn keygen_pk<'params, C, P, ConcreteCircuit>(
281    params: &P,
282    vk: VerifyingKey<C>,
283    circuit: &ConcreteCircuit,
284) -> Result<ProvingKey<C>, Error>
285where
286    C: CurveAffine,
287    C::Scalar: FromUniformBytes<64>,
288    P: Params<'params, C> + Sync,
289    ConcreteCircuit: Circuit<C::Scalar>,
290{
291    let compress_selectors = vk.compress_selectors;
292    keygen_pk_impl(params, Some(vk), circuit, compress_selectors)
293}
294
295/// Generate a `ProvingKey` from an instance of `Circuit`. `VerifyingKey` is generated in the process.
296pub fn keygen_pk2<'params, C, P, ConcreteCircuit>(
297    params: &P,
298    circuit: &ConcreteCircuit,
299    compress_selectors: bool,
300) -> Result<ProvingKey<C>, Error>
301where
302    C: CurveAffine,
303    C::Scalar: FromUniformBytes<64>,
304    P: Params<'params, C> + Sync,
305    ConcreteCircuit: Circuit<C::Scalar>,
306{
307    keygen_pk_impl(params, None, circuit, compress_selectors)
308}
309
310/// Generate a `ProvingKey` from either a precalculated `VerifyingKey` and an instance of `Circuit`, or
311/// just a `Circuit`, in which case a new `VerifyingKey` is generated. The latter is more efficient because
312/// it does fixed column FFTs only once.
313pub fn keygen_pk_impl<'params, C, P, ConcreteCircuit>(
314    params: &P,
315    vk: Option<VerifyingKey<C>>,
316    circuit: &ConcreteCircuit,
317    compress_selectors: bool,
318) -> Result<ProvingKey<C>, Error>
319where
320    C: CurveAffine,
321    C::Scalar: FromUniformBytes<64>,
322    P: Params<'params, C> + Sync,
323    ConcreteCircuit: Circuit<C::Scalar>,
324{
325    let (domain, cs, config) = create_domain::<C, ConcreteCircuit>(
326        params.k(),
327        #[cfg(feature = "circuit-params")]
328        circuit.params(),
329    );
330
331    if (params.n() as usize) < cs.minimum_rows() {
332        return Err(Error::not_enough_rows_available(params.k()));
333    }
334
335    let mut assembly: Assembly<C::Scalar> = Assembly {
336        k: params.k(),
337        fixed: vec![domain.empty_lagrange_assigned(); cs.num_fixed_columns],
338        permutation: permutation::keygen::Assembly::new(params.n() as usize, &cs.permutation),
339        selectors: vec![vec![false; params.n() as usize]; cs.num_selectors],
340        usable_rows: 0..params.n() as usize - (cs.blinding_factors() + 1),
341        _marker: std::marker::PhantomData,
342    };
343
344    // Synthesize the circuit to obtain URS
345    ConcreteCircuit::FloorPlanner::synthesize(
346        &mut assembly,
347        circuit,
348        config,
349        cs.constants.clone(),
350    )?;
351
352    let mut fixed = batch_invert_assigned(assembly.fixed);
353    let (cs, selector_polys) = if compress_selectors {
354        cs.compress_selectors(assembly.selectors.clone())
355    } else {
356        let selectors = std::mem::take(&mut assembly.selectors);
357        cs.directly_convert_selectors_to_fixed(selectors)
358    };
359    fixed.extend(
360        selector_polys
361            .into_iter()
362            .map(|poly| domain.lagrange_from_vec(poly)),
363    );
364
365    let permutation_pk = assembly
366        .permutation
367        .clone()
368        .build_pk(params, &domain, &cs.permutation);
369
370    let vk = match vk {
371        Some(vk) => vk,
372        None => {
373            let permutation_vk = assembly
374                .permutation
375                .build_vk(params, &domain, &cs.permutation);
376
377            let fixed_commitments = (&fixed)
378                .into_par_iter()
379                .map(|poly| params.commit_lagrange(poly, Blind::default()).to_affine())
380                .collect();
381
382            VerifyingKey::from_parts(
383                domain,
384                fixed_commitments,
385                permutation_vk,
386                cs,
387                assembly.selectors,
388                compress_selectors,
389            )
390        }
391    };
392
393    let fixed_polys: Vec<_> = fixed
394        .iter()
395        .map(|poly| vk.domain.lagrange_to_coeff(poly.clone()))
396        .collect();
397
398    // Compute l_0(X)
399    // TODO: this can be done more efficiently
400    let mut l0 = vk.domain.empty_lagrange();
401    l0[0] = C::Scalar::ONE;
402    let l0 = vk.domain.lagrange_to_coeff(l0);
403
404    // Compute l_blind(X) which evaluates to 1 for each blinding factor row
405    // and 0 otherwise over the domain.
406    let mut l_blind = vk.domain.empty_lagrange();
407    for evaluation in l_blind[..].iter_mut().rev().take(vk.cs.blinding_factors()) {
408        *evaluation = C::Scalar::ONE;
409    }
410
411    // Compute l_last(X) which evaluates to 1 on the first inactive row (just
412    // before the blinding factors) and 0 otherwise over the domain
413    let mut l_last = vk.domain.empty_lagrange();
414    l_last[params.n() as usize - vk.cs.blinding_factors() - 1] = C::Scalar::ONE;
415
416    // Compute l_active_row(X)
417    let one = C::Scalar::ONE;
418    let mut l_active_row = vk.domain.empty_lagrange();
419    parallelize(&mut l_active_row, |values, start| {
420        for (i, value) in values.iter_mut().enumerate() {
421            let idx = i + start;
422            *value = one - (l_last[idx] + l_blind[idx]);
423        }
424    });
425
426    let l_last = vk.domain.lagrange_to_coeff(l_last);
427    let l_active_row = vk.domain.lagrange_to_coeff(l_active_row);
428
429    // Compute the optimized evaluation data structure
430    let ev = Evaluator::new(&vk.cs);
431
432    Ok(ProvingKey {
433        vk,
434        l0,
435        l_last,
436        l_active_row,
437        fixed_values: fixed,
438        fixed_polys,
439        permutation: permutation_pk,
440        ev,
441    })
442}