use crate::{ boolean::Boolean, eq::EqGadget, fields::{fp::FpVar, FieldVar}, }; use ark_ff::PrimeField; use ark_relations::r1cs::SynthesisError; use ark_std::vec::Vec; pub mod vanishing_poly; #[derive(Clone, Debug)] /// Defines an evaluation domain over a prime field. The domain is a coset of /// size `1< { /// generator of subgroup g pub gen: F, /// index of the quotient group (i.e. the `offset`) offset: FpVar, /// dimension of evaluation domain, which is log2(size of coset) pub dim: u64, } impl Radix2DomainVar { /// Construct an evaluation domain with the given offset. pub fn new(gen: F, dimension: u64, offset: FpVar) -> Result { offset.enforce_not_equal(&FpVar::zero())?; Ok(Self { gen, offset, dim: dimension, }) } /// What is the offset of `self`? pub fn offset(&self) -> &FpVar { &self.offset } } impl EqGadget for Radix2DomainVar { fn is_eq(&self, other: &Self) -> Result, SynthesisError> { if self.gen != other.gen || self.dim != other.dim { Ok(Boolean::FALSE) } else { self.offset.is_eq(&other.offset) } } } impl Radix2DomainVar { /// order of the domain pub fn order(&self) -> usize { 1 << self.dim } /// Returns offset, offset*g, offset*g^2, ..., offset*g^{coset_size} pub fn elements(&self) -> Vec> { let mut result = Vec::new(); result.push(self.offset.clone()); for _ in 1..(1 << self.dim) { let new_element = result.last().unwrap() * self.gen; result.push(new_element); } result } /// Size of the domain pub fn size(&self) -> u64 { 1 << self.dim } /// For domain `h` with dimension `n`, `position` represented by /// `query_pos` in big endian form, returns all points of /// `h*g^{position}`. The result is the query coset at /// index `query_pos` for the FRI protocol. /// /// # Panics /// This function panics when `query_pos.len() != coset_dim` or /// `query_pos.len() != self.dim`. pub fn query_position_to_coset_elements( &self, query_pos: &[Boolean], coset_dim: u64, ) -> Result>, SynthesisError> { Ok(self .query_position_to_coset(query_pos, coset_dim)? .elements()) } /// For domain `h` with dimension `n`, `position` represented by /// `query_pos` in big endian form, returns all points of /// `h*g^{position}` /// /// # Panics /// This function panics when `query_pos.len() < log2_num_cosets`. pub fn query_position_to_coset( &self, query_pos: &[Boolean], coset_dim: u64, ) -> Result { let coset_index = truncate_to_coset_index(query_pos, self.dim, coset_dim); let offset_var = &self.offset * FpVar::Constant(self.gen).pow_le(&coset_index)?; Ok(Self { gen: self.gen.pow(&[1 << (self.dim - coset_dim)]), // distance between coset offset: offset_var, dim: coset_dim, }) } } fn truncate_to_coset_index( query_pos: &[Boolean], codeword_dim: u64, coset_dim: u64, ) -> Vec> { let log2_num_cosets = (codeword_dim - coset_dim) as usize; assert!(query_pos.len() >= log2_num_cosets); query_pos[0..log2_num_cosets].to_vec() } #[cfg(test)] mod tests { use crate::prelude::*; use ark_ff::PrimeField; use ark_relations::r1cs::ConstraintSystem; use ark_std::{rand::Rng, test_rng}; use crate::{ alloc::AllocVar, convert::ToBitsGadget, fields::fp::FpVar, poly::domain::Radix2DomainVar, R1CSVar, }; fn test_query_coset_template() { const COSET_DIM: u64 = 7; const COSET_SIZE: u64 = 1 << COSET_DIM; const LOCALIZATION: u64 = 3; let cs = ConstraintSystem::new_ref(); let mut rng = test_rng(); let gen = F::get_root_of_unity(COSET_SIZE).unwrap(); let offset = F::rand(&mut rng); let offset_var = FpVar::new_witness(cs.clone(), || Ok(offset)).unwrap(); let domain = Radix2DomainVar::new(gen, COSET_DIM, offset_var).unwrap(); let num_cosets = 1 << (COSET_DIM - LOCALIZATION); let coset_index = rng.gen_range(0..num_cosets); println!("{:0b}", coset_index); let coset_index_var = UInt32::new_witness(cs.clone(), || Ok(coset_index)) .unwrap() .to_bits_le() .unwrap() .into_iter() .take(COSET_DIM as usize) .collect::>(); let elements_actual = domain .query_position_to_coset(&coset_index_var, LOCALIZATION) .unwrap() .elements(); let elements_expected = domain .elements() .into_iter() .skip(coset_index as usize) .step_by(1 << (COSET_DIM - LOCALIZATION)) .collect::>(); assert_eq!(elements_expected.len(), elements_actual.len()); elements_expected .into_iter() .zip(elements_actual.into_iter()) .for_each(|(left, right)| { assert_eq!(left.value().unwrap(), right.value().unwrap()); }); } #[test] fn test_on_bls12_381() { test_query_coset_template::(); } #[test] fn test_on_bls12_377() { test_query_coset_template::(); } }