diff --git a/ecc/src/base_field_ecc.rs b/ecc/src/base_field_ecc.rs index 1fa992c8..3f63f82d 100644 --- a/ecc/src/base_field_ecc.rs +++ b/ecc/src/base_field_ecc.rs @@ -422,8 +422,7 @@ mod tests { fn config_range(&self, layouter: &mut impl Layouter) -> Result<(), Error> { let range_chip = RangeChip::::new(self.range_config.clone()); - range_chip.load_composition_tables(layouter)?; - range_chip.load_overflow_tables(layouter)?; + range_chip.load_table(layouter)?; Ok(()) } diff --git a/ecc/src/general_ecc.rs b/ecc/src/general_ecc.rs index b1b6d95c..72f63c37 100644 --- a/ecc/src/general_ecc.rs +++ b/ecc/src/general_ecc.rs @@ -492,8 +492,7 @@ mod tests { fn config_range(&self, layouter: &mut impl Layouter) -> Result<(), Error> { let range_chip = RangeChip::::new(self.range_config.clone()); - range_chip.load_composition_tables(layouter)?; - range_chip.load_overflow_tables(layouter)?; + range_chip.load_table(layouter)?; Ok(()) } diff --git a/ecdsa/src/ecdsa.rs b/ecdsa/src/ecdsa.rs index ca617d7f..559f5a5d 100644 --- a/ecdsa/src/ecdsa.rs +++ b/ecdsa/src/ecdsa.rs @@ -200,8 +200,7 @@ mod tests { layouter: &mut impl Layouter, ) -> Result<(), Error> { let range_chip = RangeChip::::new(self.range_config.clone()); - range_chip.load_composition_tables(layouter)?; - range_chip.load_overflow_tables(layouter)?; + range_chip.load_table(layouter)?; Ok(()) } diff --git a/integer/src/chip.rs b/integer/src/chip.rs index 7774cdd1..ac732fb7 100644 --- a/integer/src/chip.rs +++ b/integer/src/chip.rs @@ -703,8 +703,7 @@ mod tests { fn config_range(&self, layouter: &mut impl Layouter) -> Result<(), Error> { let range_chip = RangeChip::::new(self.range_config.clone()); - range_chip.load_composition_tables(layouter)?; - range_chip.load_overflow_tables(layouter)?; + range_chip.load_table(layouter)?; Ok(()) } diff --git a/maingate/src/instructions.rs b/maingate/src/instructions.rs index cd5de37b..49136b3a 100644 --- a/maingate/src/instructions.rs +++ b/maingate/src/instructions.rs @@ -11,7 +11,6 @@ use crate::{ AssignedCondition, AssignedValue, ColumnTags, MainGateColumn, }; use halo2wrong::{ - halo2::plonk::Selector, utils::{big_to_fe, decompose, fe_to_big, power_of_two}, RegionCtx, }; @@ -1003,12 +1002,12 @@ pub trait MainGateInstructions: Chip { /// Assigns a new witness composed of given array of terms /// `result = constant + term_0 + term_1 + ... ` /// where `term_i = a_i * q_i` - fn decompose Vec>( + fn decompose, bool) -> Result<(), Error>>( &self, ctx: &mut RegionCtx<'_, F>, terms: &[Term], constant: F, - mut fetch_tables: T, + mut enable_lookup: T, ) -> Result<(AssignedValue, Vec>), Error> { assert!(!terms.is_empty(), "At least one term is expected"); @@ -1054,11 +1053,7 @@ pub trait MainGateInstructions: Chip { CombinationOptionCommon::CombineToNextAdd(F::one()) }; - // Enable tables if there is any - let tables = fetch_tables(is_final); - for table in tables { - ctx.enable(table)?; - } + enable_lookup(ctx, is_final)?; let chunk_len = chunk.len(); let mut combined = self.apply( @@ -1098,7 +1093,7 @@ pub trait MainGateInstructions: Chip { constant: F, ) -> Result, Error> { assert!(!terms.is_empty(), "At least one term is expected"); - let (composed, _) = self.decompose(ctx, terms, constant, |_| vec![])?; + let (composed, _) = self.decompose(ctx, terms, constant, |_, _| Ok(()))?; Ok(composed) } diff --git a/maingate/src/range.rs b/maingate/src/range.rs index dfc7afee..f6e82346 100644 --- a/maingate/src/range.rs +++ b/maingate/src/range.rs @@ -3,33 +3,33 @@ use crate::halo2::arithmetic::FieldExt; use crate::halo2::circuit::Chip; use crate::halo2::circuit::Layouter; use crate::halo2::circuit::Value; -use crate::halo2::plonk::{ConstraintSystem, Error}; +use crate::halo2::plonk::{ConstraintSystem, Error, Expression}; use crate::halo2::plonk::{Selector, TableColumn}; use crate::halo2::poly::Rotation; use crate::instructions::{MainGateInstructions, Term}; use crate::AssignedValue; +use halo2wrong::halo2::plonk::Column; +use halo2wrong::halo2::plonk::Fixed; use halo2wrong::utils::decompose; use halo2wrong::RegionCtx; use num_integer::Integer; use std::collections::BTreeMap; +use std::collections::BTreeSet; /// Maximum number of cells in one line enabled with composition selector pub const NUMBER_OF_LOOKUP_LIMBS: usize = 4; -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -struct TableConfig { - selector: Selector, - column: TableColumn, -} - -impl TableConfig {} - /// Range gate configuration #[derive(Clone, Debug)] pub struct RangeConfig { main_gate_config: MainGateConfig, - composition_tables: BTreeMap, - overflow_tables: BTreeMap, + bit_len_tag: BTreeMap, + t_tag: TableColumn, + t_value: TableColumn, + s_composition: Selector, + tag_composition: Option>, + s_overflow: Option, + tag_overflow: Option>, } /// ['RangeChip'] applies binary range constraints @@ -77,10 +77,8 @@ pub trait RangeInstructions: Chip { bit_len: usize, ) -> Result<(AssignedValue, Vec>), Error>; - /// Appends base limb length table in sythnesis time - fn load_composition_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error>; - /// Appends shorter range tables in sythesis time - fn load_overflow_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error>; + /// Load table in sythnesis time + fn load_table(&self, layouter: &mut impl Layouter) -> Result<(), Error>; } impl RangeInstructions for RangeChip { @@ -115,19 +113,28 @@ impl RangeInstructions for RangeChip { .map(|(limb, base)| Term::Unassigned(limb, *base)) .collect(); - let composition_table = self - .config - .composition_tables - .get(&limb_bit_len) - .unwrap_or_else(|| { - panic!("composition table is not set, bit lenght: {}", limb_bit_len) - }); self.main_gate() - .decompose(ctx, &terms[..], F::zero(), |is_last| { + .decompose(ctx, &terms[..], F::zero(), |ctx, is_last| { + let composition_tag = + self.config + .bit_len_tag + .get(&limb_bit_len) + .unwrap_or_else(|| { + panic!("composition table is not set, bit lenght: {}", limb_bit_len) + }); + ctx.enable(self.config.s_composition)?; + if let Some(tag_composition) = self.config.tag_composition { + ctx.assign_fixed( + || "tag_composition", + tag_composition, + F::from(*composition_tag as u64), + )?; + } + if is_last && overflow_bit_len != 0 { - let overflow_table = self + let overflow_tag = self .config - .overflow_tables + .bit_len_tag .get(&overflow_bit_len) .unwrap_or_else(|| { panic!( @@ -135,53 +142,63 @@ impl RangeInstructions for RangeChip { overflow_bit_len ) }); - vec![composition_table.selector, overflow_table.selector] - } else { - vec![composition_table.selector] + ctx.enable(self.config.s_overflow.unwrap())?; + if let Some(tag_overflow) = self.config.tag_overflow { + ctx.assign_fixed( + || "tag_overflow", + tag_overflow, + F::from(*overflow_tag as u64), + )?; + } } + + Ok(()) }) } - fn load_composition_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - for (bit_len, config) in self.config.composition_tables.iter() { - let table_values: Vec = (0..1 << bit_len).map(|e| F::from(e)).collect(); - layouter.assign_table( - || "", - |mut table| { - for (index, &value) in table_values.iter().enumerate() { + fn load_table(&self, layouter: &mut impl Layouter) -> Result<(), Error> { + layouter.assign_table( + || "", + |mut table| { + let mut offset = 0; + + table.assign_cell( + || "table tag", + self.config.t_tag, + offset, + || Value::known(F::zero()), + )?; + table.assign_cell( + || "table value", + self.config.t_value, + offset, + || Value::known(F::zero()), + )?; + offset += 1; + + for (bit_len, tag) in self.config.bit_len_tag.iter() { + let tag = F::from(*tag as u64); + let table_values: Vec = (0..1 << bit_len).map(|e| F::from(e)).collect(); + for value in table_values.iter() { table.assign_cell( - || "composition table", - config.column, - index, - || Value::known(value), + || "table tag", + self.config.t_tag, + offset, + || Value::known(tag), )?; - } - Ok(()) - }, - )?; - } - - Ok(()) - } - - fn load_overflow_tables(&self, layouter: &mut impl Layouter) -> Result<(), Error> { - for (bit_len, config) in self.config.overflow_tables.iter() { - let table_values: Vec = (0..1 << bit_len).map(|e| F::from(e)).collect(); - layouter.assign_table( - || "", - |mut table| { - for (index, &value) in table_values.iter().enumerate() { table.assign_cell( - || "composition table", - config.column, - index, - || Value::known(value), + || "table value", + self.config.t_value, + offset, + || Value::known(*value), )?; + offset += 1; } - Ok(()) - }, - )?; - } + } + + Ok(()) + }, + )?; Ok(()) } @@ -192,13 +209,17 @@ impl RangeChip { pub fn new(config: RangeConfig) -> Self { let main_gate = MainGate::new(config.main_gate_config.clone()); let bases = config - .composition_tables + .bit_len_tag .keys() - .map(|&bit_len| { - let bases = (0..F::NUM_BITS as usize / bit_len) - .map(|i| F::from(2).pow(&[(bit_len * i) as u64, 0, 0, 0])) - .collect(); - (bit_len, bases) + .filter_map(|&bit_len| { + if bit_len == 0 { + None + } else { + let bases = (0..F::NUM_BITS as usize / bit_len) + .map(|i| F::from(2).pow(&[(bit_len * i) as u64, 0, 0, 0])) + .collect(); + Some((bit_len, bases)) + } }) .collect(); Self { @@ -216,19 +237,42 @@ impl RangeChip { composition_bit_lens: Vec, overflow_bit_lens: Vec, ) -> RangeConfig { - let mut overflow_bit_lens = overflow_bit_lens; - overflow_bit_lens.sort_unstable(); - overflow_bit_lens.dedup(); - let overflow_bit_lens: Vec = - overflow_bit_lens.into_iter().filter(|e| *e != 0).collect(); - - let mut composition_bit_lens = composition_bit_lens; - composition_bit_lens.sort_unstable(); - composition_bit_lens.dedup(); - let composition_bit_lens: Vec = composition_bit_lens - .into_iter() - .filter(|e| *e != 0) - .collect(); + let [composition_bit_lens, overflow_bit_lens] = [composition_bit_lens, overflow_bit_lens] + .map(|mut bit_lens| { + bit_lens.sort_unstable(); + bit_lens.dedup(); + bit_lens + }); + + let has_overflow_limb = !overflow_bit_lens.is_empty(); + let bit_len_tag = BTreeMap::from_iter( + BTreeSet::from_iter(composition_bit_lens.iter().chain(overflow_bit_lens.iter())) + .into_iter() + .enumerate() + .map(|(idx, bit_len)| (*bit_len, idx + 1)), + ); + + let t_tag = meta.lookup_table_column(); + let t_value = meta.lookup_table_column(); + + macro_rules! lookup { + ($prefix:literal, $selector:ident, $tag:ident, $bit_lens:ident, [$($value:ident),*]) => { + $( + meta.lookup(concat!($prefix, stringify!($value)), |meta| { + let selector = meta.query_selector($selector); + let tag = match $tag { + Some(tag) => meta.query_fixed(tag, Rotation::cur()), + None => { + let tag = F::from(bit_len_tag[&$bit_lens[0]] as u64); + selector.clone() * Expression::Constant(tag) + }, + }; + let value = meta.query_advice($value, Rotation::cur()); + vec![(tag, t_tag), (selector * value, t_value)] + }); + )* + }; + } // TODO: consider for a generic MainGateConfig let (a, b, c, d) = ( @@ -238,44 +282,43 @@ impl RangeChip { main_gate_config.d, ); - macro_rules! meta_lookup { - ($prefix:literal, $column:expr, $table_config:expr) => { - meta.lookup(concat!($prefix, "_", stringify!($column)), |meta| { - let exp = meta.query_advice($column, Rotation::cur()); - let s = meta.query_selector($table_config.selector); - vec![(exp * s, $table_config.column)] - }); - }; - } - - let mut composition_tables = BTreeMap::::new(); - let mut overflow_tables = BTreeMap::::new(); + let s_composition = meta.complex_selector(); + let tag_composition = if composition_bit_lens.len() > 1 { + Some(meta.fixed_column()) + } else { + None + }; + lookup!( + "composition", + s_composition, + tag_composition, + composition_bit_lens, + [a, b, c, d] + ); - for bit_len in composition_bit_lens.iter() { - let config = TableConfig { - selector: meta.complex_selector(), - column: meta.lookup_table_column(), - }; - meta_lookup!("composition", a, config); - meta_lookup!("composition", b, config); - meta_lookup!("composition", c, config); - meta_lookup!("composition", d, config); - composition_tables.insert(*bit_len, config); - } - for bit_len in overflow_bit_lens.iter() { - let config = TableConfig { - selector: meta.complex_selector(), - column: meta.lookup_table_column(), + let (s_overflow, tag_overflow) = if has_overflow_limb { + let s_overflow = meta.complex_selector(); + let tag_overflow = if overflow_bit_lens.len() > 1 { + Some(meta.fixed_column()) + } else { + None }; + lookup!("overflow", s_overflow, tag_overflow, overflow_bit_lens, [a]); - meta_lookup!("overflow", a, config); - overflow_tables.insert(*bit_len, config); - } + (Some(s_overflow), tag_overflow) + } else { + (None, None) + }; RangeConfig { main_gate_config: main_gate_config.clone(), - composition_tables, - overflow_tables, + bit_len_tag, + t_tag, + t_value, + s_composition, + tag_composition, + s_overflow, + tag_overflow, } } @@ -409,8 +452,7 @@ mod tests { }, )?; - range_chip.load_composition_tables(&mut layouter)?; - range_chip.load_overflow_tables(&mut layouter)?; + range_chip.load_table(&mut layouter)?; Ok(()) } diff --git a/transcript/src/transcript.rs b/transcript/src/transcript.rs index ef0a412b..10078ac9 100644 --- a/transcript/src/transcript.rs +++ b/transcript/src/transcript.rs @@ -234,8 +234,7 @@ mod tests { fn config_range(&self, layouter: &mut impl Layouter) -> Result<(), Error> { let range_chip = RangeChip::::new(self.range_config.clone()); - range_chip.load_composition_tables(layouter)?; - range_chip.load_overflow_tables(layouter)?; + range_chip.load_table(layouter)?; Ok(()) }