| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- use std::{marker::PhantomData, ops::Deref};
- use halo2_proofs::{
- arithmetic::FieldExt,
- circuit::{AssignedCell, Chip, Layouter, Region},
- plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, TableColumn},
- poly::Rotation,
- };
- /// Chip state is stored in a config struct. This is generated by the
- /// chip during configuration, and then stored inside the chip
- #[derive(Clone, Debug)]
- pub struct EvenBitsConfig {
- advice: [Column<Advice>; 2],
- even_bits: TableColumn,
- s_decompose: Selector,
- }
- impl EvenBitsConfig {
- pub fn load_private<F: FieldExt>(
- &self,
- mut layouter: impl Layouter<F>,
- value: Option<F>,
- ) -> Result<AssignedCell<F, F>, Error> {
- layouter.assign_region(
- || "load private",
- |mut region| {
- region.assign_advice(
- || "private input",
- self.advice[0],
- 0,
- || value.ok_or(Error::Synthesis),
- )
- },
- )
- }
- }
- #[derive(Clone, Debug)]
- pub struct EvenBitsChip<F: FieldExt, const WORD_BITS: u32> {
- config: EvenBitsConfig,
- _marker: PhantomData<F>,
- }
- impl<F: FieldExt, const WORD_BITS: u32> Chip<F> for EvenBitsChip<F, WORD_BITS> {
- type Config = EvenBitsConfig;
- type Loaded = ();
- fn config(&self) -> &Self::Config {
- &self.config
- }
- fn loaded(&self) -> &Self::Loaded {
- &()
- }
- }
- impl<F: FieldExt, const WORD_BITS: u32> EvenBitsChip<F, WORD_BITS> {
- pub fn construct(config: <Self as Chip<F>>::Config) -> Self {
- Self { config, _marker: PhantomData }
- }
- pub fn configure(meta: &mut ConstraintSystem<F>) -> <Self as Chip<F>>::Config {
- let advice = [meta.advice_column(), meta.advice_column()];
- for column in &advice {
- meta.enable_equality(*column);
- }
- let s_decompose = meta.complex_selector();
- let even_bits = meta.lookup_table_column();
- meta.create_gate("decompose", |meta| {
- let lhs = meta.query_advice(advice[0], Rotation::cur());
- let rhs = meta.query_advice(advice[1], Rotation::cur());
- let out = meta.query_advice(advice[0], Rotation::next());
- let s_decompose = meta.query_selector(s_decompose);
- // Finally, we return the polynomial expressions that constrain this gate.
- // For our multiplication gate, we only need a single polynomial constraint.
- //
- // The polynomial expressions returned from `create_gate` will be
- // constrained by the proving system to equal zero.
- vec![s_decompose * (lhs + Expression::Constant(F::from(2)) * rhs - out)]
- });
- let _ = meta.lookup(|meta| {
- let lookup = meta.query_selector(s_decompose);
- let a = meta.query_advice(advice[0], Rotation::cur());
- vec![(lookup * a, even_bits)]
- });
- let _ = meta.lookup(|meta| {
- let lookup = meta.query_selector(s_decompose);
- let b = meta.query_advice(advice[1], Rotation::cur());
- vec![(lookup * b, even_bits)]
- });
- EvenBitsConfig { advice, even_bits, s_decompose }
- }
- // Allocates all even bits in a table for the word size WORD_BITS.
- // `2^(WORD_BITS/2)` rows of the constraint system
- pub fn alloc_table(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
- layouter.assign_table(
- || "even bits table",
- |mut table| {
- for i in 0..2usize.pow(WORD_BITS / 2) {
- table.assign_cell(
- || format!("even_bits row {}", i),
- self.config.even_bits,
- i,
- || Ok(F::from(even_bits_at(i) as u64)),
- )?;
- }
- Ok(())
- },
- )
- }
- }
- fn even_bits_at(mut i: usize) -> usize {
- let mut r = 0;
- let mut c = 0;
- while i != 0 {
- let lower_bit = i % 2;
- r += lower_bit * 4usize.pow(c);
- i >>= 1;
- c += 1;
- }
- r
- }
- /// A newtype of a field element containing only bits that were in the
- /// even position of the decomposed element.
- /// All odd bits will be zero.
- #[derive(Clone, Copy, Debug)]
- pub struct EvenBits<W>(pub W);
- impl<W> Deref for EvenBits<W> {
- type Target = W;
- fn deref(&self) -> &Self::Target {
- &self.0
- }
- }
- /// A newtype of a field element containing only bits thet were in the
- /// odd position of the decomposed element.
- /// All odd bits will be right shifted by 1 into even positions.
- /// All odd bits will be zero.
- #[derive(Clone, Copy, Debug)]
- pub struct OddBits<W>(pub W);
- impl<W> Deref for OddBits<W> {
- type Target = W;
- fn deref(&self) -> &Self::Target {
- &self.0
- }
- }
- pub trait EvenBitsLookup<F: FieldExt>: Chip<F> {
- type Word;
- #[allow(clippy::type_complexity)]
- fn decompose(
- &self,
- layouter: impl Layouter<F>,
- c: Self::Word,
- ) -> Result<(EvenBits<Self::Word>, OddBits<Self::Word>), Error>;
- }
- impl<F: FieldExt, const WORD_BITS: u32> EvenBitsLookup<F> for EvenBitsChip<F, WORD_BITS> {
- type Word = AssignedCell<F, F>;
- fn decompose(
- &self,
- mut layouter: impl Layouter<F>,
- c: Self::Word,
- ) -> Result<(EvenBits<Self::Word>, OddBits<Self::Word>), Error> {
- let config = self.config();
- layouter.assign_region(
- || "decompose",
- |mut region: Region<'_, F>| {
- config.s_decompose.enable(&mut region, 0)?;
- let o_eo = c.value().cloned().map(decompose);
- let e_cell = region
- .assign_advice(
- || "even bits",
- config.advice[0],
- 0,
- || o_eo.map(|eo| *eo.0).ok_or(Error::Synthesis),
- )
- .map(EvenBits)?;
- let o_cell = region
- .assign_advice(
- || "odd bits",
- config.advice[1],
- 0,
- || o_eo.map(|eo| *eo.1).ok_or(Error::Synthesis),
- )
- .map(OddBits)?;
- c.copy_advice(|| "out", &mut region, config.advice[0], 1)?;
- Ok((e_cell, o_cell))
- },
- )
- }
- }
- fn decompose<F: FieldExt>(word: F) -> (EvenBits<F>, OddBits<F>) {
- assert!(word <= F::from_u128(u128::MAX));
- let mut even_only = word.to_repr();
- even_only.as_mut().iter_mut().for_each(|bits| {
- *bits &= 0b01010101;
- });
- let mut odd_only = word.to_repr();
- odd_only.as_mut().iter_mut().for_each(|bits| {
- *bits &= 0b10101010;
- });
- let even_only = EvenBits(F::from_repr(even_only).unwrap());
- let odd_only = F::from_repr(odd_only).unwrap();
- let odds_in_even = OddBits(F::from_u128(odd_only.get_lower_128() >> 1));
- (even_only, odds_in_even)
- }
- #[cfg(test)]
- mod tests {
- use super::*;
- #[test]
- fn even_bits_at_test() {
- assert_eq!(0b0, even_bits_at(0));
- assert_eq!(0b1, even_bits_at(1));
- assert_eq!(0b100, even_bits_at(2));
- assert_eq!(0b101, even_bits_at(3));
- }
- #[test]
- fn decompose_even_odd_test() {
- use pasta_curves::pallas;
- let odds = 0xAAAA;
- let evens = 0x5555;
- let (e, o) = decompose(pallas::Base::from_u128(odds));
- assert_eq!(e.get_lower_128(), 0);
- assert_eq!(o.get_lower_128(), odds >> 1);
- let (e, o) = decompose(pallas::Base::from_u128(evens));
- assert_eq!(e.get_lower_128(), evens);
- assert_eq!(o.get_lower_128(), 0);
- }
- }
|