mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-09 07:21:29 +01:00
Bug Fix in domain and API changes (#86)
* Bug Fix in `domain` and API changes Currently, `domain.query_position_to_coset` is not consistent native code in `ark-ldt`. This commit does the following changes: * change `query_position_to_coset` to `query_position_to_coset_elements`, and make the return result consistent with native code * add function `query_position_to_coset` to just return generator and offset instead of coset elements. This is useful when evaluating vanishing poly. * minor bug fix * update CHANGELOG * `query_position_to_coset_elements` use `query_position_to_coset` * Update CHANGELOG.md Co-authored-by: Weikeng Chen <w.k@berkeley.edu>
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
||||||
|
- [\#86](https://github.com/arkworks-rs/r1cs-std/pull/86) Change the API for domains for coset.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- [\#79](https://github.com/arkworks-rs/r1cs-std/pull/79) Move `NonNativeFieldVar` from `ark-nonnative` to `ark-r1cs-std`.
|
- [\#79](https://github.com/arkworks-rs/r1cs-std/pull/79) Move `NonNativeFieldVar` from `ark-nonnative` to `ark-r1cs-std`.
|
||||||
@@ -14,6 +16,7 @@
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
- [\#86](https://github.com/arkworks-rs/r1cs-std/pull/86) Make result of `query_position_to_coset` consistent with `ark-ldt`.
|
||||||
- [\#77](https://github.com/arkworks-rs/r1cs-std/pull/77) Fix BLS12 `G2PreparedGadget`'s `AllocVar` when G2 uses a divisive twist.
|
- [\#77](https://github.com/arkworks-rs/r1cs-std/pull/77) Fix BLS12 `G2PreparedGadget`'s `AllocVar` when G2 uses a divisive twist.
|
||||||
|
|
||||||
## v0.3.1
|
## v0.3.1
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub struct Radix2DomainVar<F: PrimeField> {
|
|||||||
pub gen: F,
|
pub gen: F,
|
||||||
/// index of the quotient group (i.e. the `offset`)
|
/// index of the quotient group (i.e. the `offset`)
|
||||||
offset: FpVar<F>,
|
offset: FpVar<F>,
|
||||||
/// dimension of evaluation domain
|
/// dimension of evaluation domain, which is log2(size of coset)
|
||||||
pub dim: u64,
|
pub dim: u64,
|
||||||
}
|
}
|
||||||
impl<F: PrimeField> Radix2DomainVar<F> {
|
impl<F: PrimeField> Radix2DomainVar<F> {
|
||||||
@@ -56,13 +56,13 @@ impl<F: PrimeField> Radix2DomainVar<F> {
|
|||||||
1 << self.dim
|
1 << self.dim
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns g, g^2, ..., g^{dim}
|
/// Returns offset, offset*g, offset*g^2, ..., offset*g^{coset_size}
|
||||||
fn powers_of_gen(&self, dim: usize) -> Vec<F> {
|
pub fn elements(&self) -> Vec<FpVar<F>> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
let mut cur = self.gen;
|
result.push(self.offset.clone());
|
||||||
for _ in 0..dim {
|
for _ in 1..(1 << self.dim) {
|
||||||
result.push(cur);
|
let new_element = result.last().unwrap() * self.gen;
|
||||||
cur = cur * cur;
|
result.push(new_element);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@@ -73,37 +73,113 @@ impl<F: PrimeField> Radix2DomainVar<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form,
|
/// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form,
|
||||||
/// returns `h*g^{position}<g^{n-query_pos.len()}>`
|
/// returns all points of `h*g^{position}<g^{2^{n-coset_dim}}>`. The result is the query coset at index `query_pos`
|
||||||
pub fn query_position_to_coset(
|
/// 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,
|
&self,
|
||||||
query_pos: &[Boolean<F>],
|
query_pos: &[Boolean<F>],
|
||||||
coset_dim: u64,
|
coset_dim: u64,
|
||||||
) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||||
let mut coset_index = query_pos;
|
Ok(self
|
||||||
assert!(
|
.query_position_to_coset(query_pos, coset_dim)?
|
||||||
query_pos.len() == self.dim as usize
|
.elements())
|
||||||
|| query_pos.len() == (self.dim - coset_dim) as usize
|
}
|
||||||
);
|
|
||||||
if query_pos.len() == self.dim as usize {
|
|
||||||
coset_index = &coset_index[0..(coset_index.len() - coset_dim as usize)];
|
|
||||||
}
|
|
||||||
let mut coset = Vec::new();
|
|
||||||
let powers_of_g = &self.powers_of_gen(self.dim as usize)[(coset_dim as usize)..];
|
|
||||||
|
|
||||||
let mut first_point_in_coset: FpVar<F> = FpVar::zero();
|
/// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form,
|
||||||
for i in 0..coset_index.len() {
|
/// returns all points of `h*g^{position}<g^{n-query_pos.len()}>`
|
||||||
let term = coset_index[i].select(&FpVar::constant(powers_of_g[i]), &FpVar::zero())?;
|
///
|
||||||
first_point_in_coset += &term;
|
/// # Panics
|
||||||
}
|
/// This function panics when `query_pos.len() != coset_dim` or `query_pos.len() != self.dim`.
|
||||||
|
pub fn query_position_to_coset(
|
||||||
first_point_in_coset *= &self.offset;
|
&self,
|
||||||
|
query_pos: &[Boolean<F>],
|
||||||
coset.push(first_point_in_coset);
|
coset_dim: u64,
|
||||||
for i in 1..(1 << (coset_dim as usize)) {
|
) -> Result<Self, SynthesisError> {
|
||||||
let new_elem = &coset[i - 1] * &FpVar::Constant(self.gen);
|
let coset_index = truncate_to_coset_index(query_pos, self.dim, coset_dim);
|
||||||
coset.push(new_elem);
|
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
|
||||||
Ok(coset)
|
offset: offset_var,
|
||||||
|
dim: coset_dim,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate_to_coset_index<F: PrimeField>(
|
||||||
|
query_pos: &[Boolean<F>],
|
||||||
|
codeword_dim: u64,
|
||||||
|
coset_dim: u64,
|
||||||
|
) -> Vec<Boolean<F>> {
|
||||||
|
assert!(query_pos.len() == codeword_dim as usize || query_pos.len() == coset_dim as usize);
|
||||||
|
if query_pos.len() == codeword_dim as usize {
|
||||||
|
query_pos[0..(query_pos.len() - coset_dim as usize)].to_vec()
|
||||||
|
} else {
|
||||||
|
query_pos.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, fields::fp::FpVar, poly::domain::Radix2DomainVar, R1CSVar};
|
||||||
|
|
||||||
|
fn test_query_coset_template<F: PrimeField>() {
|
||||||
|
const COSET_DIM: u64 = 7;
|
||||||
|
const COSET_SIZE: usize = 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);
|
||||||
|
let coset_index_var = UInt32::new_witness(cs.clone(), || Ok(coset_index))
|
||||||
|
.unwrap()
|
||||||
|
.to_bits_le()
|
||||||
|
.into_iter()
|
||||||
|
.take(COSET_DIM as usize)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
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::<ark_bls12_381::Fr>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_on_bls12_377() {
|
||||||
|
test_query_coset_template::<ark_bls12_377::Fr>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user