zk: Implement EvenBits chip for bit decomposition.

This commit is contained in:
parazyd
2022-03-24 18:59:14 +01:00
parent 5f0c0b05d8
commit 8ca8406aec

262
src/zk/even_bits.rs Normal file
View File

@@ -0,0 +1,262 @@
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);
}
}