arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/src/poly/domain/mod.rs
src/poly/domain/mod.rsRaw
  1use crate::boolean::Boolean;
  2use crate::eq::EqGadget;
  3use crate::fields::fp::FpVar;
  4use crate::fields::FieldVar;
  5use ark_ff::PrimeField;
  6use ark_relations::r1cs::SynthesisError;
  7use ark_std::vec::Vec;
  8
  9pub mod vanishing_poly;
 10
 11#[derive(Clone, Debug)]
 12/// Defines an evaluation domain over a prime field. The domain is a coset of size `1<<dim`.
 13///
 14/// Native code corresponds to `ark-poly::univariate::domain::radix2`, but `ark-poly` only supports
 15/// subgroup for now.
 16///
 17// TODO: support cosets in `ark-poly`.
 18pub struct Radix2DomainVar<F: PrimeField> {
 19    /// generator of subgroup g
 20    pub gen: F,
 21    /// index of the quotient group (i.e. the `offset`)
 22    offset: FpVar<F>,
 23    /// dimension of evaluation domain, which is log2(size of coset)
 24    pub dim: u64,
 25}
 26impl<F: PrimeField> Radix2DomainVar<F> {
 27    /// Construct an evaluation domain with the given offset.
 28    pub fn new(gen: F, dimension: u64, offset: FpVar<F>) -> Result<Self, SynthesisError> {
 29        offset.enforce_not_equal(&FpVar::zero())?;
 30        Ok(Self {
 31            gen,
 32            offset,
 33            dim: dimension,
 34        })
 35    }
 36
 37    /// What is the offset of `self`?
 38    pub fn offset(&self) -> &FpVar<F> {
 39        &self.offset
 40    }
 41}
 42
 43impl<F: PrimeField> EqGadget<F> for Radix2DomainVar<F> {
 44    fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
 45        if self.gen != other.gen || self.dim != other.dim {
 46            Ok(Boolean::constant(false))
 47        } else {
 48            self.offset.is_eq(&other.offset)
 49        }
 50    }
 51}
 52
 53impl<F: PrimeField> Radix2DomainVar<F> {
 54    /// order of the domain
 55    pub fn order(&self) -> usize {
 56        1 << self.dim
 57    }
 58
 59    /// Returns offset, offset*g, offset*g^2, ..., offset*g^{coset_size}
 60    pub fn elements(&self) -> Vec<FpVar<F>> {
 61        let mut result = Vec::new();
 62        result.push(self.offset.clone());
 63        for _ in 1..(1 << self.dim) {
 64            let new_element = result.last().unwrap() * self.gen;
 65            result.push(new_element);
 66        }
 67        result
 68    }
 69
 70    /// Size of the domain
 71    pub fn size(&self) -> u64 {
 72        1 << self.dim
 73    }
 74
 75    /// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form,
 76    /// returns all points of `h*g^{position}<g^{2^{n-coset_dim}}>`. The result is the query coset at index `query_pos`
 77    /// for the FRI protocol.
 78    ///
 79    /// # Panics
 80    /// This function panics when `query_pos.len() != coset_dim` or `query_pos.len() != self.dim`.  
 81    pub fn query_position_to_coset_elements(
 82        &self,
 83        query_pos: &[Boolean<F>],
 84        coset_dim: u64,
 85    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 86        Ok(self
 87            .query_position_to_coset(query_pos, coset_dim)?
 88            .elements())
 89    }
 90
 91    /// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form,
 92    /// returns all points of `h*g^{position}<g^{n-query_pos.len()}>`
 93    ///
 94    /// # Panics
 95    /// This function panics when `query_pos.len() != coset_dim` or `query_pos.len() != self.dim`.  
 96    pub fn query_position_to_coset(
 97        &self,
 98        query_pos: &[Boolean<F>],
 99        coset_dim: u64,
100    ) -> Result<Self, SynthesisError> {
101        let coset_index = truncate_to_coset_index(query_pos, self.dim, coset_dim);
102        let offset_var = &self.offset * FpVar::Constant(self.gen).pow_le(&coset_index)?;
103        Ok(Self {
104            gen: self.gen.pow(&[1 << (self.dim - coset_dim)]), // distance between coset
105            offset: offset_var,
106            dim: coset_dim,
107        })
108    }
109}
110
111fn truncate_to_coset_index<F: PrimeField>(
112    query_pos: &[Boolean<F>],
113    codeword_dim: u64,
114    coset_dim: u64,
115) -> Vec<Boolean<F>> {
116    assert!(query_pos.len() == codeword_dim as usize || query_pos.len() == coset_dim as usize);
117    if query_pos.len() == codeword_dim as usize {
118        query_pos[0..(query_pos.len() - coset_dim as usize)].to_vec()
119    } else {
120        query_pos.to_vec()
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use crate::prelude::*;
127    use ark_ff::PrimeField;
128    use ark_relations::r1cs::ConstraintSystem;
129    use ark_std::{rand::Rng, test_rng};
130
131    use crate::{alloc::AllocVar, fields::fp::FpVar, poly::domain::Radix2DomainVar, R1CSVar};
132
133    fn test_query_coset_template<F: PrimeField>() {
134        const COSET_DIM: u64 = 7;
135        const COSET_SIZE: usize = 1 << COSET_DIM;
136        const LOCALIZATION: u64 = 3;
137        let cs = ConstraintSystem::new_ref();
138        let mut rng = test_rng();
139        let gen = F::get_root_of_unity(COSET_SIZE).unwrap();
140        let offset = F::rand(&mut rng);
141        let offset_var = FpVar::new_witness(cs.clone(), || Ok(offset)).unwrap();
142        let domain = Radix2DomainVar::new(gen, COSET_DIM, offset_var).unwrap();
143
144        let num_cosets = 1 << (COSET_DIM - LOCALIZATION);
145
146        let coset_index = rng.gen_range(0..num_cosets);
147        let coset_index_var = UInt32::new_witness(cs.clone(), || Ok(coset_index))
148            .unwrap()
149            .to_bits_le()
150            .into_iter()
151            .take(COSET_DIM as usize)
152            .collect::<Vec<_>>();
153
154        let elements_actual = domain
155            .query_position_to_coset(&coset_index_var, LOCALIZATION)
156            .unwrap()
157            .elements();
158
159        let elements_expected = domain
160            .elements()
161            .into_iter()
162            .skip(coset_index as usize)
163            .step_by(1 << (COSET_DIM - LOCALIZATION))
164            .collect::<Vec<_>>();
165
166        assert_eq!(elements_expected.len(), elements_actual.len());
167
168        elements_expected
169            .into_iter()
170            .zip(elements_actual.into_iter())
171            .for_each(|(left, right)| {
172                assert_eq!(left.value().unwrap(), right.value().unwrap());
173            });
174    }
175
176    #[test]
177    fn test_on_bls12_381() {
178        test_query_coset_template::<ark_bls12_381::Fr>();
179    }
180
181    #[test]
182    fn test_on_bls12_377() {
183        test_query_coset_template::<ark_bls12_377::Fr>();
184    }
185}