diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4f5893a..53b6f312 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,13 +109,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - id: toolchain-thumbv6m - with: - target: thumbv6m-none-eabi - - run: rustup override set ${{steps.toolchain-thumbv6m.outputs.name}} - - name: Install Rust ARM64 uses: dtolnay/rust-toolchain@stable id: toolchain-aarch64 diff --git a/CHANGELOG.md b/CHANGELOG.md index 030b61e5..0baf48a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,13 +23,18 @@ - Add `UInt::rotate_{left,right}_in_place`. - Add `{Boolean,UInt}::not_in_place`. - Add `UInt::{from_bytes_le, from_bytes_be, to_bytes_be}`. -- [\#143](https://github.com/arkworks-rs/r1cs-std/pull/143) - - Add `AllocVar::new_variable_with_inferred_mode`. +- [\#143](https://github.com/arkworks-rs/r1cs-std/pull/143) Add `AllocVar::new_variable_with_inferred_mode`. +- [\#144](https://github.com/arkworks-rs/r1cs-std/pull/144) Add `ToConstraintFieldGadget` bounds to `CurveVar` and `FieldVar` ### Improvements ### Bug Fixes +- [\#145](https://github.com/arkworks-rs/r1cs-std/pull/145) + - Avoid deeply nested `LinearCombinations` in `EvaluationsVar::interpolate_and_evaluate` to fix the stack overflow issue when calling `.value()` on the evaluation result. +- [\#148](https://github.com/arkworks-rs/r1cs-std/pull/148) + - Fix panic issues during in-circuit polynomial interpolation. + ## 0.4.0 - [\#117](https://github.com/arkworks-rs/r1cs-std/pull/117) Fix result of `precomputed_base_scalar_mul_le` to not discard previous value. diff --git a/Cargo.toml b/Cargo.toml index 6f69d8dc..7afc7d0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ ark-ec = { version = "0.4.0", default-features = false } ark-std = { version = "0.4.0", default-features = false } ark-relations = { version = "0.4.0", default-features = false } -derivative = { version = "2", features = ["use_core"] } +educe = "0.6.0" tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } num-bigint = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false } @@ -73,6 +73,9 @@ incremental = true debug-assertions = true debug = true +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(ci)'] } + [patch.crates-io] ark-ff = { git = "https://github.com/arkworks-rs/algebra/" } ark-ec = { git = "https://github.com/arkworks-rs/algebra/" } @@ -87,3 +90,4 @@ ark-mnt4-753 = { git = "https://github.com/arkworks-rs/curves/" } ark-mnt6-298 = { git = "https://github.com/arkworks-rs/curves/" } ark-mnt6-753 = { git = "https://github.com/arkworks-rs/curves/" } ark-pallas = { git = "https://github.com/arkworks-rs/curves/" } +ark-std = { git = "https://github.com/arkworks-rs/std/" } \ No newline at end of file diff --git a/src/fields/cubic_extension.rs b/src/fields/cubic_extension.rs index d0b64a71..62105a2d 100644 --- a/src/fields/cubic_extension.rs +++ b/src/fields/cubic_extension.rs @@ -1,21 +1,21 @@ -use ark_ff::{ - fields::{CubicExtField, Field}, - CubicExtConfig, Zero, -}; -use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; -use core::{borrow::Borrow, marker::PhantomData}; - use crate::{ convert::{ToBitsGadget, ToBytesGadget, ToConstraintFieldGadget}, fields::{fp::FpVar, FieldOpsBounds, FieldVar}, prelude::*, Vec, }; +use ark_ff::{ + fields::{CubicExtField, Field}, + CubicExtConfig, Zero, +}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; +use core::{borrow::Borrow, marker::PhantomData}; +use educe::Educe; /// This struct is the `R1CS` equivalent of the cubic extension field type /// in `ark-ff`, i.e. `ark_ff::CubicExtField`. -#[derive(Derivative)] -#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct CubicExtVar, P: CubicExtVarConfig> where @@ -27,7 +27,7 @@ where pub c1: BF, /// The second coefficient of this field element. pub c2: BF, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } diff --git a/src/fields/mod.rs b/src/fields/mod.rs index bced7ffd..bc77cffc 100644 --- a/src/fields/mod.rs +++ b/src/fields/mod.rs @@ -5,7 +5,7 @@ use core::{ ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}, }; -use crate::convert::{ToBitsGadget, ToBytesGadget}; +use crate::convert::{ToBitsGadget, ToBytesGadget, ToConstraintFieldGadget}; use crate::prelude::*; /// This module contains a generic implementation of cubic extension field @@ -76,6 +76,7 @@ pub trait FieldVar: + AllocVar + ToBytesGadget + CondSelectGadget + + ToConstraintFieldGadget + for<'a> FieldOpsBounds<'a, F, Self> + for<'a> AddAssign<&'a Self> + for<'a> SubAssign<&'a Self> diff --git a/src/fields/quadratic_extension.rs b/src/fields/quadratic_extension.rs index a38f47c1..d7419da3 100644 --- a/src/fields/quadratic_extension.rs +++ b/src/fields/quadratic_extension.rs @@ -1,21 +1,21 @@ -use ark_ff::{ - fields::{Field, QuadExtConfig, QuadExtField}, - Zero, -}; -use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; -use core::{borrow::Borrow, marker::PhantomData}; - use crate::{ convert::{ToBitsGadget, ToBytesGadget, ToConstraintFieldGadget}, fields::{fp::FpVar, FieldOpsBounds, FieldVar}, prelude::*, Vec, }; +use ark_ff::{ + fields::{Field, QuadExtConfig, QuadExtField}, + Zero, +}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; +use core::{borrow::Borrow, marker::PhantomData}; +use educe::Educe; /// This struct is the `R1CS` equivalent of the quadratic extension field type /// in `ark-ff`, i.e. `ark_ff::QuadExtField`. -#[derive(Derivative)] -#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct QuadExtVar, P: QuadExtVarConfig> where @@ -25,7 +25,7 @@ where pub c0: BF, /// The first coefficient of this field element. pub c1: BF, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } diff --git a/src/groups/curves/short_weierstrass/bls12/mod.rs b/src/groups/curves/short_weierstrass/bls12/mod.rs index deb6b19a..1549f976 100644 --- a/src/groups/curves/short_weierstrass/bls12/mod.rs +++ b/src/groups/curves/short_weierstrass/bls12/mod.rs @@ -10,7 +10,6 @@ use crate::{ groups::curves::short_weierstrass::*, Vec, }; -use core::fmt::Debug; /// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Config, FpVar<

::Fp>>; @@ -29,8 +28,8 @@ pub type G2AffineVar

= AffineVar<

::G2Config, Fp2G

>; /// Represents the cached precomputation that can be performed on a G1 element /// which enables speeding up pairing computation. -#[derive(Derivative)] -#[derivative(Clone(bound = "G1Var

: Clone"), Debug(bound = "G1Var

: Debug"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct G1PreparedVar(pub AffineVar>); impl G1PreparedVar

{ @@ -103,11 +102,8 @@ type Fp2G

= Fp2Var<

::Fp2Config>; type LCoeff

= (Fp2G

, Fp2G

); /// Represents the cached precomputation that can be performed on a G2 element /// which enables speeding up pairing computation. -#[derive(Derivative)] -#[derivative( - Clone(bound = "Fp2Var: Clone"), - Debug(bound = "Fp2Var: Debug") -)] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct G2PreparedVar { #[doc(hidden)] pub ell_coeffs: Vec>, diff --git a/src/groups/curves/short_weierstrass/mnt4/mod.rs b/src/groups/curves/short_weierstrass/mnt4/mod.rs index 3b960c82..a5c6875d 100644 --- a/src/groups/curves/short_weierstrass/mnt4/mod.rs +++ b/src/groups/curves/short_weierstrass/mnt4/mod.rs @@ -14,6 +14,7 @@ use crate::{ Vec, }; use core::borrow::Borrow; +use educe::Educe; /// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Config, FpVar<

::Fp>>; @@ -23,8 +24,8 @@ pub type G2Var

= ProjectiveVar<

::G2Config, Fp2G

>; /// Represents the cached precomputation that can be performed on a G1 element /// which enables speeding up pairing computation. -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct G1PreparedVar { #[doc(hidden)] pub x: FpVar, @@ -135,8 +136,8 @@ type Fp2G

= Fp2Var<

::Fp2Config>; /// Represents the cached precomputation that can be performed on a G2 element /// which enables speeding up pairing computation. -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct G2PreparedVar { #[doc(hidden)] pub x: Fp2Var, @@ -340,8 +341,8 @@ impl G2PreparedVar

{ } #[doc(hidden)] -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct AteDoubleCoefficientsVar { pub c_h: Fp2Var, pub c_4c: Fp2Var, @@ -425,8 +426,8 @@ impl AteDoubleCoefficientsVar

{ } #[doc(hidden)] -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT4Config"), Debug(bound = "P: MNT4Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct AteAdditionCoefficientsVar { pub c_l1: Fp2Var, pub c_rz: Fp2Var, diff --git a/src/groups/curves/short_weierstrass/mnt6/mod.rs b/src/groups/curves/short_weierstrass/mnt6/mod.rs index 3fdf923d..83f742f4 100644 --- a/src/groups/curves/short_weierstrass/mnt6/mod.rs +++ b/src/groups/curves/short_weierstrass/mnt6/mod.rs @@ -14,6 +14,7 @@ use crate::{ Vec, }; use core::borrow::Borrow; +use educe::Educe; /// Represents a projective point in G1. pub type G1Var

= ProjectiveVar<

::G1Config, FpVar<

::Fp>>; @@ -23,8 +24,8 @@ pub type G2Var

= ProjectiveVar<

::G2Config, Fp3G

>; /// Represents the cached precomputation that can be performed on a G1 element /// which enables speeding up pairing computation. -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT6Config"), Debug(bound = "P: MNT6Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct G1PreparedVar { #[doc(hidden)] pub x: FpVar, @@ -135,8 +136,8 @@ type Fp3G

= Fp3Var<

::Fp3Config>; /// Represents the cached precomputation that can be performed on a G2 element /// which enables speeding up pairing computation. -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT6Config"), Debug(bound = "P: MNT6Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct G2PreparedVar { #[doc(hidden)] pub x: Fp3Var, @@ -340,8 +341,8 @@ impl G2PreparedVar

{ } #[doc(hidden)] -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT6Config"), Debug(bound = "P: MNT6Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct AteDoubleCoefficientsVar { pub c_h: Fp3Var, pub c_4c: Fp3Var, @@ -423,8 +424,8 @@ impl AteDoubleCoefficientsVar

{ } #[doc(hidden)] -#[derive(Derivative)] -#[derivative(Clone(bound = "P: MNT6Config"), Debug(bound = "P: MNT6Config"))] +#[derive(Educe)] +#[educe(Clone, Debug)] pub struct AteAdditionCoefficientsVar { pub c_l1: Fp3Var, pub c_rz: Fp3Var, diff --git a/src/groups/curves/short_weierstrass/mod.rs b/src/groups/curves/short_weierstrass/mod.rs index d18f7209..8604c052 100644 --- a/src/groups/curves/short_weierstrass/mod.rs +++ b/src/groups/curves/short_weierstrass/mod.rs @@ -5,6 +5,7 @@ use ark_ec::{ use ark_ff::{AdditiveGroup, BitIteratorBE, Field, One, PrimeField, Zero}; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul}; +use educe::Educe; use non_zero_affine::NonZeroAffineVar; use crate::{ @@ -42,8 +43,8 @@ type BasePrimeField

= <

::BaseField as Field>::BasePrimeFiel /// An implementation of arithmetic for Short Weierstrass curves that relies on /// the complete formulae derived in the paper of /// [[Renes, Costello, Batina 2015]](). -#[derive(Derivative)] -#[derivative(Debug, Clone)] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct ProjectiveVar>> where @@ -55,13 +56,13 @@ where pub y: F, /// The z-coordinate. pub z: F, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } /// An affine representation of a curve point. -#[derive(Derivative)] -#[derivative(Debug(bound = "F: ark_std::fmt::Debug"), Clone(bound = "F: Clone"))] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct AffineVar>> where @@ -73,7 +74,7 @@ where pub y: F, /// Is `self` the point at infinity. pub infinity: Boolean>, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } diff --git a/src/groups/curves/short_weierstrass/non_zero_affine.rs b/src/groups/curves/short_weierstrass/non_zero_affine.rs index 7b894348..50448518 100644 --- a/src/groups/curves/short_weierstrass/non_zero_affine.rs +++ b/src/groups/curves/short_weierstrass/non_zero_affine.rs @@ -4,8 +4,8 @@ use ark_std::ops::Add; /// An affine representation of a prime order curve point that is guaranteed /// to *not* be the point at infinity. -#[derive(Derivative)] -#[derivative(Debug, Clone)] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct NonZeroAffineVar< P: SWCurveConfig, @@ -17,7 +17,7 @@ pub struct NonZeroAffineVar< pub x: F, /// The y-coordinate. pub y: F, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } diff --git a/src/groups/curves/twisted_edwards/mod.rs b/src/groups/curves/twisted_edwards/mod.rs index 82095bfd..4ffcc644 100644 --- a/src/groups/curves/twisted_edwards/mod.rs +++ b/src/groups/curves/twisted_edwards/mod.rs @@ -17,6 +17,7 @@ use crate::{ use crate::fields::fp::FpVar; use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul}; +use educe::Educe; type BasePrimeField

= <

::BaseField as Field>::BasePrimeField; @@ -26,8 +27,8 @@ type BasePrimeField

= <

::BaseField as Field>::BasePrimeFiel /// /// This is intended for use primarily for implementing efficient /// multi-scalar-multiplication in the Bowe-Hopwood-Pedersen hash. -#[derive(Derivative)] -#[derivative(Debug, Clone)] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct MontgomeryAffineVar>> where @@ -37,7 +38,7 @@ where pub x: F, /// The y-coordinate. pub y: F, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } @@ -233,8 +234,8 @@ mod montgomery_affine_impl { /// An implementation of arithmetic for Twisted Edwards curves that relies on /// the complete formulae for the affine model, as outlined in the /// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html). -#[derive(Derivative)] -#[derivative(Debug, Clone)] +#[derive(Educe)] +#[educe(Debug, Clone)] #[must_use] pub struct AffineVar>> where @@ -244,7 +245,7 @@ where pub x: F, /// The y-coordinate. pub y: F, - #[derivative(Debug = "ignore")] + #[educe(Debug(ignore))] _params: PhantomData

, } diff --git a/src/groups/mod.rs b/src/groups/mod.rs index 08edbd57..7144ee90 100644 --- a/src/groups/mod.rs +++ b/src/groups/mod.rs @@ -1,5 +1,5 @@ use crate::{ - convert::{ToBitsGadget, ToBytesGadget}, + convert::{ToBitsGadget, ToBytesGadget, ToConstraintFieldGadget}, fields::emulated_fp::EmulatedFpVar, prelude::*, }; @@ -41,6 +41,7 @@ pub trait CurveVar: + CondSelectGadget + AllocVar + AllocVar + + ToConstraintFieldGadget + for<'a> GroupOpsBounds<'a, C, Self> + for<'a> AddAssign<&'a Self> + for<'a> SubAssign<&'a Self> diff --git a/src/lib.rs b/src/lib.rs index 7f3e5274..ed0b1621 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,10 +19,6 @@ extern crate ark_ff; #[macro_use] extern crate ark_relations; -#[doc(hidden)] -#[macro_use] -extern crate derivative; - /// Some utility macros for making downstream impls easier. #[macro_use] pub mod macros; diff --git a/src/poly/evaluations/univariate/mod.rs b/src/poly/evaluations/univariate/mod.rs index c05cc092..7856b79a 100644 --- a/src/poly/evaluations/univariate/mod.rs +++ b/src/poly/evaluations/univariate/mod.rs @@ -64,8 +64,11 @@ impl EvaluationsVar { /// ready to interpolate pub fn generate_interpolation_cache(&mut self) { if self.domain.offset().is_constant() { - let poly_evaluations_val: Vec<_> = - self.evals.iter().map(|v| v.value().unwrap()).collect(); + let poly_evaluations_val: Vec<_> = self + .evals + .iter() + .map(|v| v.value().unwrap_or_default()) + .collect(); let domain = &self.domain; let lagrange_interpolator = if let &FpVar::Constant(x) = domain.offset() { LagrangeInterpolator::new(x, domain.gen, domain.dim, poly_evaluations_val) @@ -100,7 +103,7 @@ impl EvaluationsVar { .expect("lagrange interpolator has not been initialized. \ Call `self.generate_interpolation_cache` first or set `interpolate` to true in constructor. "); let lagrange_coeffs = - lagrange_interpolator.compute_lagrange_coefficients(t.value().unwrap()); + lagrange_interpolator.compute_lagrange_coefficients(t.value().unwrap_or_default()); let mut lagrange_coeffs_fg = Vec::new(); // Now we convert these lagrange coefficients to gadgets, and then constrain // them. The i-th lagrange coefficients constraint is: @@ -119,13 +122,15 @@ impl EvaluationsVar { Ok(lagrange_coeffs[i]) })?; // Enforce the actual constraint (A_element) * (lagrange_coeff) = 1/Z_I(t) - assert_eq!( - (lagrange_interpolator.v_inv_elems[i] * t.value().unwrap() - - lagrange_interpolator.v_inv_elems[i] - * lagrange_interpolator.all_domain_elems[i]) - * lagrange_coeffs[i], - vp_t.value().unwrap() - ); + if !cs.is_in_setup_mode() { + assert_eq!( + (lagrange_interpolator.v_inv_elems[i] * t.value().unwrap_or_default() + - lagrange_interpolator.v_inv_elems[i] + * lagrange_interpolator.all_domain_elems[i]) + * lagrange_coeffs[i], + vp_t.value().unwrap_or_default() + ); + } a_element.mul_equals(&lag_coeff, &vp_t)?; lagrange_coeffs_fg.push(lag_coeff); } @@ -158,11 +163,13 @@ impl EvaluationsVar { .as_ref() .expect("lagrange interpolator has not been initialized. "); let lagrange_coeffs = self.compute_lagrange_coefficients(interpolation_point)?; - let mut interpolation: FpVar = FpVar::zero(); - for i in 0..lagrange_interpolator.domain_order { - let intermediate = &lagrange_coeffs[i] * &self.evals[i]; - interpolation += &intermediate - } + + let interpolation = lagrange_coeffs + .iter() + .zip(&self.evals) + .take(lagrange_interpolator.domain_order) + .map(|(coeff, eval)| coeff * eval) + .sum::>(); Ok(interpolation) } @@ -208,31 +215,33 @@ impl EvaluationsVar { let alpha_coset_offset_inv = interpolation_point.mul_by_inverse_unchecked(&self.domain.offset())?; - // `res` stores the sum of all lagrange polynomials evaluated at alpha - let mut res = FpVar::::zero(); - let domain_size = self.domain.size() as usize; - for i in 0..domain_size { - // a'^{-1} where a is the base coset element - let subgroup_point_inv = subgroup_points[(domain_size - i) % domain_size]; - debug_assert_eq!(subgroup_points[i] * subgroup_point_inv, F::one()); - // alpha * offset^{-1} * a'^{-1} - 1 - let lag_denom = &alpha_coset_offset_inv * subgroup_point_inv - F::one(); - // lag_denom cannot be zero, so we use `unchecked`. - // - // Proof: lag_denom is zero if and only if alpha * (coset_offset * - // subgroup_point)^{-1} == 1. This can happen only if `alpha` is - // itself in the coset. - // - // Earlier we asserted that `lhs_numerator` is not zero. - // Since `lhs_numerator` is just the vanishing polynomial for the coset - // evaluated at `alpha`, and since this is non-zero, `alpha` is not - // in the coset. - let lag_coeff = lhs.mul_by_inverse_unchecked(&lag_denom)?; - - let lag_interpoland = &self.evals[i] * lag_coeff; - res += lag_interpoland - } + + // `evals` stores all lagrange polynomials evaluated at alpha + let evals = (0..domain_size) + .map(|i| { + // a'^{-1} where a is the base coset element + let subgroup_point_inv = subgroup_points[(domain_size - i) % domain_size]; + debug_assert_eq!(subgroup_points[i] * subgroup_point_inv, F::one()); + // alpha * offset^{-1} * a'^{-1} - 1 + let lag_denom = &alpha_coset_offset_inv * subgroup_point_inv - F::one(); + // lag_denom cannot be zero, so we use `unchecked`. + // + // Proof: lag_denom is zero if and only if alpha * (coset_offset * + // subgroup_point)^{-1} == 1. This can happen only if `alpha` is + // itself in the coset. + // + // Earlier we asserted that `lhs_numerator` is not zero. + // Since `lhs_numerator` is just the vanishing polynomial for the coset + // evaluated at `alpha`, and since this is non-zero, `alpha` is not + // in the coset. + let lag_coeff = lhs.mul_by_inverse_unchecked(&lag_denom)?; + + Ok(&self.evals[i] * lag_coeff) + }) + .collect::, _>>()?; + + let res = evals.iter().sum(); Ok(res) } @@ -340,12 +349,16 @@ impl<'a, F: PrimeField> DivAssign<&'a EvaluationsVar> for EvaluationsVar { ); let cs = self.evals[0].cs(); // the prover can generate result = (1 / other) * self offline - let mut result_val: Vec<_> = other.evals.iter().map(|x| x.value().unwrap()).collect(); + let mut result_val: Vec<_> = other + .evals + .iter() + .map(|x| x.value().unwrap_or_default()) + .collect(); batch_inversion(&mut result_val); result_val .iter_mut() .zip(&self.evals) - .for_each(|(a, self_var)| *a *= self_var.value().unwrap()); + .for_each(|(a, self_var)| *a *= self_var.value().unwrap_or_default()); let result_var: Vec<_> = result_val .iter() .map(|x| FpVar::new_witness(ns!(cs, "div result"), || Ok(*x)).unwrap()) @@ -378,87 +391,87 @@ mod tests { #[test] fn test_interpolate_constant_offset() { - let mut rng = test_rng(); - let poly = DensePolynomial::rand(15, &mut rng); - let gen = Fr::get_root_of_unity(1 << 4).unwrap(); - assert_eq!(gen.pow(&[1 << 4]), Fr::one()); - let domain = Radix2DomainVar::new( - gen, - 4, // 2^4 = 16 - FpVar::constant(Fr::rand(&mut rng)), - ) - .unwrap(); - let mut coset_point = domain.offset().value().unwrap(); - let mut oracle_evals = Vec::new(); - for _ in 0..(1 << 4) { - oracle_evals.push(poly.evaluate(&coset_point)); - coset_point *= gen; - } - let cs = ConstraintSystem::new_ref(); - let evaluations_fp: Vec<_> = oracle_evals - .iter() - .map(|x| FpVar::new_input(ns!(cs, "evaluations"), || Ok(x)).unwrap()) - .collect(); - let evaluations_var = EvaluationsVar::from_vec_and_domain(evaluations_fp, domain, true); - - let interpolate_point = Fr::rand(&mut rng); - let interpolate_point_fp = - FpVar::new_input(ns!(cs, "interpolate point"), || Ok(interpolate_point)).unwrap(); - - let expected = poly.evaluate(&interpolate_point); - - let actual = evaluations_var - .interpolate_and_evaluate(&interpolate_point_fp) - .unwrap() - .value() - .unwrap(); + for n in [11, 12, 13, 14] { + let mut rng = test_rng(); + + let poly = DensePolynomial::rand((1 << n) - 1, &mut rng); + let gen = Fr::get_root_of_unity(1 << n).unwrap(); + assert_eq!(gen.pow(&[1 << n]), Fr::one()); + let domain = Radix2DomainVar::new(gen, n, FpVar::constant(Fr::rand(&mut rng))).unwrap(); + let mut coset_point = domain.offset().value().unwrap(); + let mut oracle_evals = Vec::new(); + for _ in 0..(1 << n) { + oracle_evals.push(poly.evaluate(&coset_point)); + coset_point *= gen; + } + let cs = ConstraintSystem::new_ref(); + let evaluations_fp: Vec<_> = oracle_evals + .iter() + .map(|x| FpVar::new_input(ns!(cs, "evaluations"), || Ok(x)).unwrap()) + .collect(); + let evaluations_var = EvaluationsVar::from_vec_and_domain(evaluations_fp, domain, true); + + let interpolate_point = Fr::rand(&mut rng); + let interpolate_point_fp = + FpVar::new_input(ns!(cs, "interpolate point"), || Ok(interpolate_point)).unwrap(); + + let expected = poly.evaluate(&interpolate_point); + + let actual = evaluations_var + .interpolate_and_evaluate(&interpolate_point_fp) + .unwrap() + .value() + .unwrap(); - assert_eq!(actual, expected); - assert!(cs.is_satisfied().unwrap()); - println!("number of constraints: {}", cs.num_constraints()) + assert_eq!(actual, expected); + assert!(cs.is_satisfied().unwrap()); + println!("number of constraints: {}", cs.num_constraints()); + } } #[test] fn test_interpolate_non_constant_offset() { - let mut rng = test_rng(); - let poly = DensePolynomial::rand(15, &mut rng); - let gen = Fr::get_root_of_unity(1 << 4).unwrap(); - assert_eq!(gen.pow(&[1 << 4]), Fr::one()); - let cs = ConstraintSystem::new_ref(); - let domain = Radix2DomainVar::new( - gen, - 4, // 2^4 = 16 - FpVar::new_witness(ns!(cs, "offset"), || Ok(Fr::rand(&mut rng))).unwrap(), - ) - .unwrap(); - let mut coset_point = domain.offset().value().unwrap(); - let mut oracle_evals = Vec::new(); - for _ in 0..(1 << 4) { - oracle_evals.push(poly.evaluate(&coset_point)); - coset_point *= gen; - } + for n in [11, 12, 13, 14] { + let mut rng = test_rng(); + let poly = DensePolynomial::rand((1 << n) - 1, &mut rng); + let gen = Fr::get_root_of_unity(1 << n).unwrap(); + assert_eq!(gen.pow(&[1 << n]), Fr::one()); + let cs = ConstraintSystem::new_ref(); + let domain = Radix2DomainVar::new( + gen, + n, + FpVar::new_witness(ns!(cs, "offset"), || Ok(Fr::rand(&mut rng))).unwrap(), + ) + .unwrap(); + let mut coset_point = domain.offset().value().unwrap(); + let mut oracle_evals = Vec::new(); + for _ in 0..(1 << n) { + oracle_evals.push(poly.evaluate(&coset_point)); + coset_point *= gen; + } - let evaluations_fp: Vec<_> = oracle_evals - .iter() - .map(|x| FpVar::new_input(ns!(cs, "evaluations"), || Ok(x)).unwrap()) - .collect(); - let evaluations_var = EvaluationsVar::from_vec_and_domain(evaluations_fp, domain, true); + let evaluations_fp: Vec<_> = oracle_evals + .iter() + .map(|x| FpVar::new_input(ns!(cs, "evaluations"), || Ok(x)).unwrap()) + .collect(); + let evaluations_var = EvaluationsVar::from_vec_and_domain(evaluations_fp, domain, true); - let interpolate_point = Fr::rand(&mut rng); - let interpolate_point_fp = - FpVar::new_input(ns!(cs, "interpolate point"), || Ok(interpolate_point)).unwrap(); + let interpolate_point = Fr::rand(&mut rng); + let interpolate_point_fp = + FpVar::new_input(ns!(cs, "interpolate point"), || Ok(interpolate_point)).unwrap(); - let expected = poly.evaluate(&interpolate_point); + let expected = poly.evaluate(&interpolate_point); - let actual = evaluations_var - .interpolate_and_evaluate(&interpolate_point_fp) - .unwrap() - .value() - .unwrap(); + let actual = evaluations_var + .interpolate_and_evaluate(&interpolate_point_fp) + .unwrap() + .value() + .unwrap(); - assert_eq!(actual, expected); - assert!(cs.is_satisfied().unwrap()); - println!("number of constraints: {}", cs.num_constraints()) + assert_eq!(actual, expected); + assert!(cs.is_satisfied().unwrap()); + println!("number of constraints: {}", cs.num_constraints()); + } } #[test]