halo2_base/utils/
halo2.rs

1use std::collections::hash_map::Entry;
2
3use crate::ff::Field;
4use crate::halo2_proofs::{
5    circuit::{AssignedCell, Cell, Region, Value},
6    halo2curves::bn256::Bn256,
7    plonk::{Advice, Assigned, Circuit, Column, Fixed},
8    poly::kzg::commitment::ParamsKZG,
9};
10use crate::virtual_region::copy_constraints::{CopyConstraintManager, EXTERNAL_CELL_TYPE_ID};
11use crate::AssignedValue;
12
13pub use keygen::ProvingKeyGenerator;
14
15/// Raw (physical) assigned cell in Plonkish arithmetization.
16#[cfg(feature = "halo2-axiom")]
17pub type Halo2AssignedCell<'v, F> = AssignedCell<&'v Assigned<F>, F>;
18/// Raw (physical) assigned cell in Plonkish arithmetization.
19#[cfg(not(feature = "halo2-axiom"))]
20pub type Halo2AssignedCell<'v, F> = AssignedCell<Assigned<F>, F>;
21
22/// Assign advice to physical region.
23#[inline(always)]
24pub fn raw_assign_advice<'v, F: Field>(
25    region: &mut Region<F>,
26    column: Column<Advice>,
27    offset: usize,
28    value: Value<impl Into<Assigned<F>>>,
29) -> Halo2AssignedCell<'v, F> {
30    #[cfg(feature = "halo2-axiom")]
31    {
32        region.assign_advice(column, offset, value)
33    }
34    #[cfg(feature = "halo2-pse")]
35    {
36        let value = value.map(|a| Into::<Assigned<F>>::into(a));
37        region
38            .assign_advice(
39                || format!("assign advice {column:?} offset {offset}"),
40                column,
41                offset,
42                || value,
43            )
44            .unwrap()
45    }
46}
47
48/// Assign fixed to physical region.
49#[inline(always)]
50pub fn raw_assign_fixed<F: Field>(
51    region: &mut Region<F>,
52    column: Column<Fixed>,
53    offset: usize,
54    value: F,
55) -> Cell {
56    #[cfg(feature = "halo2-axiom")]
57    {
58        region.assign_fixed(column, offset, value)
59    }
60    #[cfg(feature = "halo2-pse")]
61    {
62        region
63            .assign_fixed(
64                || format!("assign fixed {column:?} offset {offset}"),
65                column,
66                offset,
67                || Value::known(value),
68            )
69            .unwrap()
70            .cell()
71    }
72}
73
74/// Constrain two physical cells to be equal.
75#[inline(always)]
76pub fn raw_constrain_equal<F: Field>(region: &mut Region<F>, left: Cell, right: Cell) {
77    #[cfg(feature = "halo2-axiom")]
78    region.constrain_equal(left, right);
79    #[cfg(not(feature = "halo2-axiom"))]
80    region.constrain_equal(left, right).unwrap();
81}
82
83/// Constrains that `virtual_cell` is equal to `external_cell`. The `virtual_cell` must have
84/// already been raw assigned with the raw assigned cell stored in `copy_manager`
85/// **unless** it is marked an external-only cell with type id [EXTERNAL_CELL_TYPE_ID].
86/// * When the virtual cell has already been assigned, the assigned cell is constrained to be equal to the external cell.
87/// * When the virtual cell has not been assigned **and** it is marked as an external cell, it is assigned to `external_cell` and the mapping is stored in `copy_manager`.
88///
89/// This should only be called when `witness_gen_only` is false, otherwise it will panic.
90///
91/// ## Panics
92/// If witness generation only mode is true.
93pub fn constrain_virtual_equals_external<F: Field + Ord>(
94    region: &mut Region<F>,
95    virtual_cell: AssignedValue<F>,
96    external_cell: Cell,
97    copy_manager: &mut CopyConstraintManager<F>,
98) {
99    let ctx_cell = virtual_cell.cell.unwrap();
100    match copy_manager.assigned_advices.entry(ctx_cell) {
101        Entry::Occupied(acell) => {
102            // The virtual cell has already been assigned, so we can constrain it to equal the external cell.
103            region.constrain_equal(*acell.get(), external_cell);
104        }
105        Entry::Vacant(assigned) => {
106            // The virtual cell **must** be an external cell
107            assert_eq!(ctx_cell.type_id, EXTERNAL_CELL_TYPE_ID);
108            // We map the virtual cell to point to the raw external cell in `copy_manager`
109            assigned.insert(external_cell);
110        }
111    }
112}
113
114/// This trait should be implemented on the minimal circuit configuration data necessary to
115/// completely determine a circuit (independent of circuit inputs).
116/// This is used to generate a _dummy_ instantiation of a concrete `Circuit` type for the purposes of key generation.
117/// This dummy instantiation just needs to have the correct arithmetization format, but the witnesses do not need to
118/// satisfy constraints.
119pub trait KeygenCircuitIntent<F: Field> {
120    /// Concrete circuit type
121    type ConcreteCircuit: Circuit<F>;
122    /// Additional data that "pins" down the circuit. These can always to deterministically rederived from `Self`, but
123    /// storing the `Pinning` saves recomputations in future proof generations.
124    type Pinning;
125
126    /// The intent must include the log_2 domain size of the circuit.
127    /// This is used to get the correct trusted setup file.
128    fn get_k(&self) -> u32;
129
130    /// Builds a _dummy_ instantiation of `Self::ConcreteCircuit` for the purposes of key generation.
131    /// This dummy instantiation just needs to have the correct arithmetization format, but the witnesses do not need to
132    /// satisfy constraints.
133    fn build_keygen_circuit(self) -> Self::ConcreteCircuit;
134
135    /// Pinning is only fully computed after `synthesize` has been run during keygen
136    fn get_pinning_after_keygen(
137        self,
138        kzg_params: &ParamsKZG<Bn256>,
139        circuit: &Self::ConcreteCircuit,
140    ) -> Self::Pinning;
141}
142
143mod keygen {
144    use crate::halo2_proofs::{
145        halo2curves::bn256::{Bn256, Fr, G1Affine},
146        plonk::{self, ProvingKey},
147        poly::{commitment::Params, kzg::commitment::ParamsKZG},
148    };
149
150    use super::KeygenCircuitIntent;
151
152    /// Trait for creating a proving key and a pinning for a circuit from minimal circuit configuration data.
153    pub trait ProvingKeyGenerator {
154        /// Create proving key and pinning.
155        fn create_pk_and_pinning(
156            self,
157            kzg_params: &ParamsKZG<Bn256>,
158        ) -> (ProvingKey<G1Affine>, serde_json::Value);
159    }
160
161    impl<CI> ProvingKeyGenerator for CI
162    where
163        CI: KeygenCircuitIntent<Fr> + Clone,
164        CI::Pinning: serde::Serialize,
165    {
166        fn create_pk_and_pinning(
167            self,
168            kzg_params: &ParamsKZG<Bn256>,
169        ) -> (ProvingKey<G1Affine>, serde_json::Value) {
170            assert_eq!(kzg_params.k(), self.get_k());
171            let circuit = self.clone().build_keygen_circuit();
172            #[cfg(feature = "halo2-axiom")]
173            let pk = plonk::keygen_pk2(kzg_params, &circuit, false).unwrap();
174            #[cfg(not(feature = "halo2-axiom"))]
175            let pk = {
176                let vk = plonk::keygen_vk_custom(kzg_params, &circuit, false).unwrap();
177                plonk::keygen_pk(kzg_params, vk, &circuit).unwrap()
178            };
179            let pinning = self.get_pinning_after_keygen(kzg_params, &circuit);
180            (pk, serde_json::to_value(pinning).unwrap())
181        }
182    }
183}