1use std::collections::hash_map::Entry;
23use 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;
1213pub use keygen::ProvingKeyGenerator;
1415/// 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>;
2122/// 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{
36let 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}
4748/// 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}
7374/// 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")]
78region.constrain_equal(left, right);
79#[cfg(not(feature = "halo2-axiom"))]
80region.constrain_equal(left, right).unwrap();
81}
8283/// 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) {
99let ctx_cell = virtual_cell.cell.unwrap();
100match 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.
103region.constrain_equal(*acell.get(), external_cell);
104 }
105 Entry::Vacant(assigned) => {
106// The virtual cell **must** be an external cell
107assert_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`
109assigned.insert(external_cell);
110 }
111 }
112}
113114/// 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
121type 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.
124type Pinning;
125126/// The intent must include the log_2 domain size of the circuit.
127 /// This is used to get the correct trusted setup file.
128fn get_k(&self) -> u32;
129130/// 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.
133fn build_keygen_circuit(self) -> Self::ConcreteCircuit;
134135/// Pinning is only fully computed after `synthesize` has been run during keygen
136fn get_pinning_after_keygen(
137self,
138 kzg_params: &ParamsKZG<Bn256>,
139 circuit: &Self::ConcreteCircuit,
140 ) -> Self::Pinning;
141}
142143mod keygen {
144use crate::halo2_proofs::{
145 halo2curves::bn256::{Bn256, Fr, G1Affine},
146 plonk::{self, ProvingKey},
147 poly::{commitment::Params, kzg::commitment::ParamsKZG},
148 };
149150use super::KeygenCircuitIntent;
151152/// Trait for creating a proving key and a pinning for a circuit from minimal circuit configuration data.
153pub trait ProvingKeyGenerator {
154/// Create proving key and pinning.
155fn create_pk_and_pinning(
156self,
157 kzg_params: &ParamsKZG<Bn256>,
158 ) -> (ProvingKey<G1Affine>, serde_json::Value);
159 }
160161impl<CI> ProvingKeyGenerator for CI
162where
163CI: KeygenCircuitIntent<Fr> + Clone,
164 CI::Pinning: serde::Serialize,
165 {
166fn create_pk_and_pinning(
167self,
168 kzg_params: &ParamsKZG<Bn256>,
169 ) -> (ProvingKey<G1Affine>, serde_json::Value) {
170assert_eq!(kzg_params.k(), self.get_k());
171let circuit = self.clone().build_keygen_circuit();
172#[cfg(feature = "halo2-axiom")]
173let pk = plonk::keygen_pk2(kzg_params, &circuit, false).unwrap();
174#[cfg(not(feature = "halo2-axiom"))]
175let pk = {
176let vk = plonk::keygen_vk_custom(kzg_params, &circuit, false).unwrap();
177 plonk::keygen_pk(kzg_params, vk, &circuit).unwrap()
178 };
179let pinning = self.get_pinning_after_keygen(kzg_params, &circuit);
180 (pk, serde_json::to_value(pinning).unwrap())
181 }
182 }
183}