1use serde::{Deserialize, Serialize};
23use crate::utils::ScalarField;
4use crate::{
5 halo2_proofs::{
6 circuit::{Layouter, SimpleFloorPlanner},
7 plonk::{Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector},
8 },
9 virtual_region::manager::VirtualRegionManager,
10};
1112use self::builder::BaseCircuitBuilder;
1314use super::flex_gate::{FlexGateConfig, FlexGateConfigParams};
15use super::range::RangeConfig;
1617/// Module that helps auto-build circuits
18pub mod builder;
1920/// A struct defining the configuration parameters for a halo2-base circuit
21/// - this is used to configure [BaseConfig].
22#[derive(Clone, Default, Debug, Hash, Serialize, Deserialize)]
23pub struct BaseCircuitParams {
24// Keeping FlexGateConfigParams expanded for backwards compatibility
25/// Specifies the number of rows in the circuit to be 2<sup>k</sup>
26pub k: usize,
27/// The number of advice columns per phase
28pub num_advice_per_phase: Vec<usize>,
29/// The number of fixed columns
30pub num_fixed: usize,
31/// The number of bits that can be ranged checked using a special lookup table with values [0, 2<sup>lookup_bits</sup>), if using.
32 /// The number of special advice columns that have range lookup enabled per phase
33pub num_lookup_advice_per_phase: Vec<usize>,
34/// This is `None` if no lookup table is used.
35pub lookup_bits: Option<usize>,
36/// Number of public instance columns
37#[serde(default)]
38pub num_instance_columns: usize,
39}
4041impl BaseCircuitParams {
42fn gate_params(&self) -> FlexGateConfigParams {
43 FlexGateConfigParams {
44 k: self.k,
45 num_advice_per_phase: self.num_advice_per_phase.clone(),
46 num_fixed: self.num_fixed,
47 }
48 }
49}
5051/// Configuration with [`BaseConfig`] with `NI` public instance columns.
52#[derive(Clone, Debug)]
53pub struct BaseConfig<F: ScalarField> {
54/// The underlying private gate/range configuration
55pub base: MaybeRangeConfig<F>,
56/// The public instance column
57pub instance: Vec<Column<Instance>>,
58}
5960/// Smart Halo2 circuit config that has different variants depending on whether you need range checks or not.
61/// The difference is that to enable range checks, the Halo2 config needs to add a lookup table.
62#[derive(Clone, Debug)]
63pub enum MaybeRangeConfig<F: ScalarField> {
64/// Config for a circuit that does not use range checks
65WithoutRange(FlexGateConfig<F>),
66/// Config for a circuit that does use range checks
67WithRange(RangeConfig<F>),
68}
6970impl<F: ScalarField> BaseConfig<F> {
71/// Generates a new `BaseConfig` depending on `params`.
72 /// - It will generate a `RangeConfig` is `params` has `lookup_bits` not None **and** `num_lookup_advice_per_phase` are not all empty or zero (i.e., if `params` indicates that the circuit actually requires a lookup table).
73 /// - Otherwise it will generate a `FlexGateConfig`.
74pub fn configure(meta: &mut ConstraintSystem<F>, params: BaseCircuitParams) -> Self {
75let total_lookup_advice_cols = params.num_lookup_advice_per_phase.iter().sum::<usize>();
76let base = if params.lookup_bits.is_some() && total_lookup_advice_cols != 0 {
77// We only add a lookup table if lookup bits is not None
78MaybeRangeConfig::WithRange(RangeConfig::configure(
79 meta,
80 params.gate_params(),
81¶ms.num_lookup_advice_per_phase,
82 params.lookup_bits.unwrap(),
83 ))
84 } else {
85 MaybeRangeConfig::WithoutRange(FlexGateConfig::configure(meta, params.gate_params()))
86 };
87let instance = (0..params.num_instance_columns)
88 .map(|_| {
89let inst = meta.instance_column();
90 meta.enable_equality(inst);
91 inst
92 })
93 .collect();
94Self { base, instance }
95 }
9697/// Returns the inner [`FlexGateConfig`]
98pub fn gate(&self) -> &FlexGateConfig<F> {
99match &self.base {
100 MaybeRangeConfig::WithoutRange(config) => config,
101 MaybeRangeConfig::WithRange(config) => &config.gate,
102 }
103 }
104105/// Returns the fixed columns for constants
106pub fn constants(&self) -> &Vec<Column<Fixed>> {
107match &self.base {
108 MaybeRangeConfig::WithoutRange(config) => &config.constants,
109 MaybeRangeConfig::WithRange(config) => &config.gate.constants,
110 }
111 }
112113/// Returns a slice of the selector column to enable lookup -- this is only in the situation where there is a single advice column of any kind -- per phase
114 /// Returns empty slice if there are no lookups enabled.
115pub fn q_lookup(&self) -> &[Option<Selector>] {
116match &self.base {
117 MaybeRangeConfig::WithoutRange(_) => &[],
118 MaybeRangeConfig::WithRange(config) => &config.q_lookup,
119 }
120 }
121122/// Updates the number of usable rows in the circuit. Used if you mutate [ConstraintSystem] after `BaseConfig::configure` is called.
123pub fn set_usable_rows(&mut self, usable_rows: usize) {
124match &mut self.base {
125 MaybeRangeConfig::WithoutRange(config) => config.max_rows = usable_rows,
126 MaybeRangeConfig::WithRange(config) => config.gate.max_rows = usable_rows,
127 }
128 }
129130/// Initialization of config at very beginning of `synthesize`.
131 /// Loads fixed lookup table, if using.
132pub fn initialize(&self, layouter: &mut impl Layouter<F>) {
133// only load lookup table if we are actually doing lookups
134if let MaybeRangeConfig::WithRange(config) = &self.base {
135 config.load_lookup_table(layouter).expect("load lookup table should not fail");
136 }
137 }
138}
139140impl<F: ScalarField> Circuit<F> for BaseCircuitBuilder<F> {
141type Config = BaseConfig<F>;
142type FloorPlanner = SimpleFloorPlanner;
143type Params = BaseCircuitParams;
144145fn params(&self) -> Self::Params {
146self.config_params.clone()
147 }
148149/// Creates a new instance of the [BaseCircuitBuilder] without witnesses by setting the witness_gen_only flag to false
150fn without_witnesses(&self) -> Self {
151unimplemented!()
152 }
153154/// Configures a new circuit using [`BaseCircuitParams`]
155fn configure_with_params(meta: &mut ConstraintSystem<F>, params: Self::Params) -> Self::Config {
156 BaseConfig::configure(meta, params)
157 }
158159fn configure(_: &mut ConstraintSystem<F>) -> Self::Config {
160unreachable!("You must use configure_with_params");
161 }
162163/// Performs the actual computation on the circuit (e.g., witness generation), populating the lookup table and filling in all the advice values for a particular proof.
164fn synthesize(
165&self,
166 config: Self::Config,
167mut layouter: impl Layouter<F>,
168 ) -> Result<(), Error> {
169// only load lookup table if we are actually doing lookups
170if let MaybeRangeConfig::WithRange(config) = &config.base {
171 config.load_lookup_table(&mut layouter).expect("load lookup table should not fail");
172 }
173// Only FirstPhase (phase 0)
174layouter
175 .assign_region(
176 || "BaseCircuitBuilder generated circuit",
177 |mut region| {
178let usable_rows = config.gate().max_rows;
179self.core.phase_manager[0].assign_raw(
180&(config.gate().basic_gates[0].clone(), usable_rows),
181&mut region,
182 );
183// Only assign cells to lookup if we're sure we're doing range lookups
184if let MaybeRangeConfig::WithRange(config) = &config.base {
185self.assign_lookups_in_phase(config, &mut region, 0);
186 }
187// Impose equality constraints
188if !self.core.witness_gen_only() {
189self.core.copy_manager.assign_raw(config.constants(), &mut region);
190 }
191Ok(())
192 },
193 )
194 .unwrap();
195196self.assign_instances(&config.instance, layouter.namespace(|| "expose"));
197Ok(())
198 }
199}
200201/// Defines stage of circuit building.
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub enum CircuitBuilderStage {
204/// Keygen phase
205Keygen,
206/// Prover Circuit
207Prover,
208/// Mock Circuit
209Mock,
210}
211212impl CircuitBuilderStage {
213/// Returns true if the circuit is used for witness generation only.
214pub fn witness_gen_only(&self) -> bool {
215matches!(self, CircuitBuilderStage::Prover)
216 }
217}
218219impl<F: ScalarField> AsRef<BaseConfig<F>> for BaseConfig<F> {
220fn as_ref(&self) -> &BaseConfig<F> {
221self
222}
223}
224225impl<F: ScalarField> AsMut<BaseConfig<F>> for BaseConfig<F> {
226fn as_mut(&mut self) -> &mut BaseConfig<F> {
227self
228}
229}