use serde::{Deserialize, Serialize};
use crate::utils::ScalarField;
use crate::{
halo2_proofs::{
circuit::{Layouter, SimpleFloorPlanner},
plonk::{Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector},
},
virtual_region::manager::VirtualRegionManager,
};
use self::builder::BaseCircuitBuilder;
use super::flex_gate::{FlexGateConfig, FlexGateConfigParams};
use super::range::RangeConfig;
pub mod builder;
#[derive(Clone, Default, Debug, Hash, Serialize, Deserialize)]
pub struct BaseCircuitParams {
pub k: usize,
pub num_advice_per_phase: Vec<usize>,
pub num_fixed: usize,
pub num_lookup_advice_per_phase: Vec<usize>,
pub lookup_bits: Option<usize>,
#[serde(default)]
pub num_instance_columns: usize,
}
impl BaseCircuitParams {
fn gate_params(&self) -> FlexGateConfigParams {
FlexGateConfigParams {
k: self.k,
num_advice_per_phase: self.num_advice_per_phase.clone(),
num_fixed: self.num_fixed,
}
}
}
#[derive(Clone, Debug)]
pub struct BaseConfig<F: ScalarField> {
pub base: MaybeRangeConfig<F>,
pub instance: Vec<Column<Instance>>,
}
#[derive(Clone, Debug)]
pub enum MaybeRangeConfig<F: ScalarField> {
WithoutRange(FlexGateConfig<F>),
WithRange(RangeConfig<F>),
}
impl<F: ScalarField> BaseConfig<F> {
pub fn configure(meta: &mut ConstraintSystem<F>, params: BaseCircuitParams) -> Self {
let total_lookup_advice_cols = params.num_lookup_advice_per_phase.iter().sum::<usize>();
let base = if params.lookup_bits.is_some() && total_lookup_advice_cols != 0 {
MaybeRangeConfig::WithRange(RangeConfig::configure(
meta,
params.gate_params(),
¶ms.num_lookup_advice_per_phase,
params.lookup_bits.unwrap(),
))
} else {
MaybeRangeConfig::WithoutRange(FlexGateConfig::configure(meta, params.gate_params()))
};
let instance = (0..params.num_instance_columns)
.map(|_| {
let inst = meta.instance_column();
meta.enable_equality(inst);
inst
})
.collect();
Self { base, instance }
}
pub fn gate(&self) -> &FlexGateConfig<F> {
match &self.base {
MaybeRangeConfig::WithoutRange(config) => config,
MaybeRangeConfig::WithRange(config) => &config.gate,
}
}
pub fn constants(&self) -> &Vec<Column<Fixed>> {
match &self.base {
MaybeRangeConfig::WithoutRange(config) => &config.constants,
MaybeRangeConfig::WithRange(config) => &config.gate.constants,
}
}
pub fn q_lookup(&self) -> &[Option<Selector>] {
match &self.base {
MaybeRangeConfig::WithoutRange(_) => &[],
MaybeRangeConfig::WithRange(config) => &config.q_lookup,
}
}
pub fn set_usable_rows(&mut self, usable_rows: usize) {
match &mut self.base {
MaybeRangeConfig::WithoutRange(config) => config.max_rows = usable_rows,
MaybeRangeConfig::WithRange(config) => config.gate.max_rows = usable_rows,
}
}
pub fn initialize(&self, layouter: &mut impl Layouter<F>) {
if let MaybeRangeConfig::WithRange(config) = &self.base {
config.load_lookup_table(layouter).expect("load lookup table should not fail");
}
}
}
impl<F: ScalarField> Circuit<F> for BaseCircuitBuilder<F> {
type Config = BaseConfig<F>;
type FloorPlanner = SimpleFloorPlanner;
type Params = BaseCircuitParams;
fn params(&self) -> Self::Params {
self.config_params.clone()
}
fn without_witnesses(&self) -> Self {
unimplemented!()
}
fn configure_with_params(meta: &mut ConstraintSystem<F>, params: Self::Params) -> Self::Config {
BaseConfig::configure(meta, params)
}
fn configure(_: &mut ConstraintSystem<F>) -> Self::Config {
unreachable!("You must use configure_with_params");
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
if let MaybeRangeConfig::WithRange(config) = &config.base {
config.load_lookup_table(&mut layouter).expect("load lookup table should not fail");
} else {
assert!(
self.lookup_manager.iter().all(|lookup_manager| lookup_manager.total_rows() == 0),
"range lookups were queued but the circuit was configured without a RangeConfig"
);
}
layouter
.assign_region(
|| "BaseCircuitBuilder generated circuit",
|mut region| {
let usable_rows = config.gate().max_rows;
self.core.phase_manager[0].assign_raw(
&(config.gate().basic_gates[0].clone(), usable_rows),
&mut region,
);
if let MaybeRangeConfig::WithRange(config) = &config.base {
self.assign_lookups_in_phase(config, &mut region, 0);
}
if !self.core.witness_gen_only() {
self.core.copy_manager.assign_raw(config.constants(), &mut region);
}
Ok(())
},
)
.unwrap();
self.assign_instances(&config.instance, layouter.namespace(|| "expose"));
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CircuitBuilderStage {
Keygen,
Prover,
Mock,
}
impl CircuitBuilderStage {
pub fn witness_gen_only(&self) -> bool {
matches!(self, CircuitBuilderStage::Prover)
}
}
impl<F: ScalarField> AsRef<BaseConfig<F>> for BaseConfig<F> {
fn as_ref(&self) -> &BaseConfig<F> {
self
}
}
impl<F: ScalarField> AsMut<BaseConfig<F>> for BaseConfig<F> {
fn as_mut(&mut self) -> &mut BaseConfig<F> {
self
}
}