diff --git a/r1cs-std/src/fields/cubic_extension.rs b/r1cs-std/src/fields/cubic_extension.rs new file mode 100644 index 0000000..a6e7f6d --- /dev/null +++ b/r1cs-std/src/fields/cubic_extension.rs @@ -0,0 +1,521 @@ +use algebra::{ + fields::{CubicExtField, CubicExtParameters, Field}, + One, +}; +use core::{borrow::Borrow, marker::PhantomData}; +use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; + +use crate::{ + fields::{FieldOpsBounds, FieldVar}, + prelude::*, + Assignment, Vec, +}; + +#[derive(Derivative)] +#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))] +#[must_use] +pub struct CubicExtVar, P: CubicExtVarParams> +where + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, +{ + pub c0: BF, + pub c1: BF, + pub c2: BF, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +pub trait CubicExtVarParams>: + CubicExtParameters +where + for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>, +{ + fn mul_base_field_vars_by_frob_coeff(c1: &mut BF, c2: &mut BF, power: usize); +} + +impl, P: CubicExtVarParams> CubicExtVar +where + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, +{ + #[inline] + pub fn new(c0: BF, c1: BF, c2: BF) -> Self { + let _params = PhantomData; + Self { + c0, + c1, + c2, + _params, + } + } + + /// Multiply a BF by cubic nonresidue P::NONRESIDUE. + #[inline] + pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result { + Ok(fe * P::NONRESIDUE) + } + + /// Multiply a CubicExtVar by an element of `P::BaseField`. + #[inline] + pub fn mul_by_base_field_constant(&self, fe: P::BaseField) -> Self { + let c0 = &self.c0 * fe; + let c1 = &self.c1 * fe; + let c2 = &self.c2 * fe; + Self::new(c0, c1, c2) + } + + #[inline] + pub fn mul_assign_by_base_field_constant(&mut self, fe: P::BaseField) { + *self = (&*self).mul_by_base_field_constant(fe); + } +} + +impl R1CSVar for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + type Value = CubicExtField

; + + fn cs(&self) -> Option> { + [&self.c0, &self.c1, &self.c2].cs() + } + + #[inline] + fn value(&self) -> Result { + match (self.c0.value(), self.c1.value(), self.c2.value()) { + (Ok(c0), Ok(c1), Ok(c2)) => Ok(CubicExtField::new(c0, c1, c2)), + (..) => Err(SynthesisError::AssignmentMissing), + } + } +} + +impl From> for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + fn from(other: Boolean) -> Self { + let c0 = BF::from(other); + let c1 = BF::zero(); + let c2 = BF::zero(); + Self::new(c0, c1, c2) + } +} + +impl<'a, BF, P> FieldOpsBounds<'a, CubicExtField

, CubicExtVar> for CubicExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: CubicExtVarParams, +{ +} +impl<'a, BF, P> FieldOpsBounds<'a, CubicExtField

, CubicExtVar> for &'a CubicExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: CubicExtVarParams, +{ +} + +impl FieldVar, P::BasePrimeField> for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + fn constant(other: CubicExtField

) -> Self { + let c0 = BF::constant(other.c0); + let c1 = BF::constant(other.c1); + let c2 = BF::constant(other.c2); + Self::new(c0, c1, c2) + } + + fn zero() -> Self { + let c0 = BF::zero(); + let c1 = BF::zero(); + let c2 = BF::zero(); + Self::new(c0, c1, c2) + } + + fn one() -> Self { + let c0 = BF::one(); + let c1 = BF::zero(); + let c2 = BF::zero(); + Self::new(c0, c1, c2) + } + + #[inline] + fn double(&self) -> Result { + let c0 = self.c0.double()?; + let c1 = self.c1.double()?; + let c2 = self.c2.double()?; + Ok(Self::new(c0, c1, c2)) + } + + #[inline] + fn negate(&self) -> Result { + let mut result = self.clone(); + result.c0.negate_in_place()?; + result.c1.negate_in_place()?; + result.c2.negate_in_place()?; + Ok(result) + } + + /// Use the Chung-Hasan asymmetric squaring formula. + /// + /// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on + /// Abstract Pairing-Friendly + /// Fields.pdf; Section 4 (CH-SQR2)) + #[inline] + fn square(&self) -> Result { + let a = self.c0.clone(); + let b = self.c1.clone(); + let c = self.c2.clone(); + + let s0 = a.square()?; + let ab = &a * &b; + let s1 = ab.double()?; + let s2 = (&a - &b + &c).square()?; + let s3 = (&b * &c).double()?; + let s4 = c.square()?; + + let c0 = Self::mul_base_field_by_nonresidue(&s3)? + &s0; + let c1 = Self::mul_base_field_by_nonresidue(&s4)? + &s1; + let c2 = s1 + &s2 + &s3 - &s0 - &s4; + + Ok(Self::new(c0, c1, c2)) + } + + fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { + // Karatsuba multiplication for cubic extensions: + // v0 = A.c0 * B.c0 + // v1 = A.c1 * B.c1 + // v2 = A.c2 * B.c2 + // result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2) + // result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2 + // result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2, + // We enforce this with six constraints: + // + // v0 = A.c0 * B.c0 + // v1 = A.c1 * B.c1 + // v2 = A.c2 * B.c2 + // + // result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2)) + // result.c1 + v0 + v1 - βv2 = (a0 + a1)(b0 + b1) + // result.c2 + v0 - v1 + v2 = (a0 + a2)(b0 + b2) + // Reference: + // "Multiplication and Squaring on Pairing-Friendly Fields" + // Devegili, OhEigeartaigh, Scott, Dahab + // + // This implementation adapted from + // https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs + let v0 = &self.c0 * &other.c0; + let v1 = &self.c1 * &other.c1; + let v2 = &self.c2 * &other.c2; + + // Check c0 + let nr_a1_plus_a2 = (&self.c1 + &self.c2) * P::NONRESIDUE; + let b1_plus_b2 = &other.c1 + &other.c2; + let nr_v1 = &v1 * P::NONRESIDUE; + let nr_v2 = &v2 * P::NONRESIDUE; + let to_check = &result.c0 - &v0 + &nr_v1 + &nr_v2; + nr_a1_plus_a2.mul_equals(&b1_plus_b2, &to_check)?; + + // Check c1 + let a0_plus_a1 = &self.c0 + &self.c1; + let b0_plus_b1 = &other.c0 + &other.c1; + let to_check = &result.c1 - &nr_v2 + &v0 + &v1; + a0_plus_a1.mul_equals(&b0_plus_b1, &to_check)?; + + // Check c2 + let a0_plus_a2 = &self.c0 + &self.c2; + let b0_plus_b2 = &other.c0 + &other.c2; + let to_check = &result.c2 + &v0 - &v1 + &v2; + a0_plus_a2.mul_equals(&b0_plus_b2, &to_check)?; + Ok(()) + } + + fn frobenius_map(&self, power: usize) -> Result { + let mut result = self.clone(); + result.c0.frobenius_map_in_place(power)?; + result.c1.frobenius_map_in_place(power)?; + result.c2.frobenius_map_in_place(power)?; + + P::mul_base_field_vars_by_frob_coeff(&mut result.c1, &mut result.c2, power); + Ok(result) + } + + fn inverse(&self) -> Result { + let cs = self.cs().get()?.clone(); + let one = Self::new_constant(cs.clone(), CubicExtField::one())?; + let inverse = Self::new_witness(cs, || self.value().and_then(|v| v.inverse().get()))?; + self.mul_equals(&inverse, &one)?; + Ok(inverse) + } +} + +impl_bounded_ops!( + CubicExtVar, + CubicExtField

, + Add, + add, + AddAssign, + add_assign, + |this: &'a CubicExtVar, other: &'a CubicExtVar| { + let c0 = &this.c0 + &other.c0; + let c1 = &this.c1 + &other.c1; + let c2 = &this.c2 + &other.c2; + CubicExtVar::new(c0, c1, c2) + }, + |this: &'a CubicExtVar, other: CubicExtField

| { + this + CubicExtVar::constant(other) + }, + (BF: FieldVar, P: CubicExtVarParams), + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, +); +impl_bounded_ops!( + CubicExtVar, + CubicExtField

, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a CubicExtVar, other: &'a CubicExtVar| { + let c0 = &this.c0 - &other.c0; + let c1 = &this.c1 - &other.c1; + let c2 = &this.c2 - &other.c2; + CubicExtVar::new(c0, c1, c2) + }, + |this: &'a CubicExtVar, other: CubicExtField

| { + this - CubicExtVar::constant(other) + }, + (BF: FieldVar, P: CubicExtVarParams), + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, +); +impl_bounded_ops!( + CubicExtVar, + CubicExtField

, + Mul, + mul, + MulAssign, + mul_assign, + |this: &'a CubicExtVar, other: &'a CubicExtVar| { + // Karatsuba multiplication for cubic extensions: + // v0 = A.c0 * B.c0 + // v1 = A.c1 * B.c1 + // v2 = A.c2 * B.c2 + // result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2) + // result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2 + // result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2, + // + // Reference: + // "Multiplication and Squaring on Pairing-Friendly Fields" + // Devegili, OhEigeartaigh, Scott, Dahab + let v0 = &this.c0 * &other.c0; + let v1 = &this.c1 * &other.c1; + let v2 = &this.c2 * &other.c2; + let c0 = + (((&this.c1 + &this.c2) * (&other.c1 + &other.c2) - &v1 - &v2) * P::NONRESIDUE) + &v0 ; + let c1 = + (&this.c0 + &this.c1) * (&other.c0 + &other.c1) - &v0 - &v1 + (&v2 * P::NONRESIDUE); + let c2 = + (&this.c0 + &this.c2) * (&other.c0 + &other.c2) - &v0 + &v1 - &v2; + + CubicExtVar::new(c0, c1, c2) + }, + |this: &'a CubicExtVar, other: CubicExtField

| { + this * CubicExtVar::constant(other) + }, + (BF: FieldVar, P: CubicExtVarParams), + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, +); + +impl EqGadget for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + fn is_eq(&self, other: &Self) -> Result, SynthesisError> { + let b0 = self.c0.is_eq(&other.c0)?; + let b1 = self.c1.is_eq(&other.c1)?; + let b2 = self.c2.is_eq(&other.c2)?; + b0.and(&b1)?.and(&b2) + } + + #[inline] + fn conditional_enforce_equal( + &self, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + self.c0.conditional_enforce_equal(&other.c0, condition)?; + self.c1.conditional_enforce_equal(&other.c1, condition)?; + self.c2.conditional_enforce_equal(&other.c2, condition)?; + Ok(()) + } + + #[inline] + fn conditional_enforce_not_equal( + &self, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + let is_equal = self.is_eq(other)?; + is_equal + .and(condition)? + .enforce_equal(&Boolean::Constant(false)) + } +} + +impl ToBitsGadget for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + fn to_bits(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_bits()?; + let mut c1 = self.c1.to_bits()?; + let mut c2 = self.c2.to_bits()?; + c0.append(&mut c1); + c0.append(&mut c2); + Ok(c0) + } + + fn to_non_unique_bits(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_non_unique_bits()?; + let mut c1 = self.c1.to_non_unique_bits()?; + let mut c2 = self.c2.to_non_unique_bits()?; + c0.append(&mut c1); + c0.append(&mut c2); + Ok(c0) + } +} + +impl ToBytesGadget for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_bytes()?; + let mut c1 = self.c1.to_bytes()?; + let mut c2 = self.c2.to_bytes()?; + c0.append(&mut c1); + c0.append(&mut c2); + + Ok(c0) + } + + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_non_unique_bytes()?; + let mut c1 = self.c1.to_non_unique_bytes()?; + let mut c2 = self.c2.to_non_unique_bytes()?; + + c0.append(&mut c1); + c0.append(&mut c2); + + Ok(c0) + } +} + +impl CondSelectGadget for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + #[inline] + fn conditionally_select( + cond: &Boolean, + true_value: &Self, + false_value: &Self, + ) -> Result { + let c0 = BF::conditionally_select(cond, &true_value.c0, &false_value.c0)?; + let c1 = BF::conditionally_select(cond, &true_value.c1, &false_value.c1)?; + let c2 = BF::conditionally_select(cond, &true_value.c2, &false_value.c2)?; + Ok(Self::new(c0, c1, c2)) + } +} + +impl TwoBitLookupGadget for CubicExtVar +where + BF: FieldVar + + TwoBitLookupGadget, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + type TableConstant = CubicExtField

; + + fn two_bit_lookup( + b: &[Boolean], + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c2s = c.iter().map(|f| f.c2).collect::>(); + let c0 = BF::two_bit_lookup(b, &c0s)?; + let c1 = BF::two_bit_lookup(b, &c1s)?; + let c2 = BF::two_bit_lookup(b, &c2s)?; + Ok(Self::new(c0, c1, c2)) + } +} + +impl ThreeBitCondNegLookupGadget for CubicExtVar +where + BF: FieldVar + + ThreeBitCondNegLookupGadget, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + type TableConstant = CubicExtField

; + + fn three_bit_cond_neg_lookup( + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c2s = c.iter().map(|f| f.c2).collect::>(); + let c0 = BF::three_bit_cond_neg_lookup(b, b0b1, &c0s)?; + let c1 = BF::three_bit_cond_neg_lookup(b, b0b1, &c1s)?; + let c2 = BF::three_bit_cond_neg_lookup(b, b0b1, &c2s)?; + Ok(Self::new(c0, c1, c2)) + } +} + +impl AllocVar, P::BasePrimeField> for CubicExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: CubicExtVarParams, +{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + + use SynthesisError::*; + let (c0, c1, c2) = match f() { + Ok(fe) => (Ok(fe.borrow().c0), Ok(fe.borrow().c1), Ok(fe.borrow().c2)), + Err(_) => ( + Err(AssignmentMissing), + Err(AssignmentMissing), + Err(AssignmentMissing), + ), + }; + + let c0 = BF::new_variable(cs.ns("c0"), || c0, mode)?; + let c1 = BF::new_variable(cs.ns("c1"), || c1, mode)?; + let c2 = BF::new_variable(cs.ns("c2"), || c2, mode)?; + Ok(Self::new(c0, c1, c2)) + } +} diff --git a/r1cs-std/src/fields/fp/cmp.rs b/r1cs-std/src/fields/fp/cmp.rs index 662d1f1..7f53828 100644 --- a/r1cs-std/src/fields/fp/cmp.rs +++ b/r1cs-std/src/fields/fp/cmp.rs @@ -1,127 +1,89 @@ use crate::{ boolean::Boolean, - fields::{fp::FpGadget, FieldGadget}, + fields::{fp::FpVar, FieldVar}, + prelude::*, ToBitsGadget, }; use algebra::PrimeField; use core::cmp::Ordering; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use r1cs_core::{lc, SynthesisError, Variable}; -impl FpGadget { - /// This function enforces the ordering between `self` and `b`. The +impl FpVar { + /// This function enforces the ordering between `self` and `other`. The /// constraint system will not be satisfied otherwise. If `self` should - /// also be checked for equality, e.g. `a <= b` instead of `a < b`, set - /// `should_also_check_quality` to `true`. This variant verifies `a` and `b` + /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set + /// `should_also_check_quality` to `true`. This variant verifies `self` and `other` /// are `<= (p-1)/2`. - pub fn enforce_cmp>( + pub fn enforce_cmp( &self, - mut cs: CS, - b: &FpGadget, + other: &FpVar, ordering: Ordering, should_also_check_equality: bool, ) -> Result<(), SynthesisError> { - let (left, right) = Self::process_cmp_inputs( - cs.ns(|| "process cmp inputs"), - &self, - b, - ordering, - should_also_check_equality, - )?; - Self::enforce_smaller_than(cs.ns(|| "enforce smaller than"), &left, &right) + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.enforce_smaller_than(&right) } - /// This function enforces the ordering between `self` and `b`. The + /// This function enforces the ordering between `self` and `other`. The /// constraint system will not be satisfied otherwise. If `self` should - /// also be checked for equality, e.g. `a <= b` instead of `a < b`, set - /// `should_also_check_quality` to `true`. This variant assumes `a` and `b` + /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set + /// `should_also_check_quality` to `true`. This variant assumes `self` and `other` /// are `<= (p-1)/2` and does not generate constraints to verify that. - pub fn enforce_cmp_unchecked>( + pub fn enforce_cmp_unchecked( &self, - mut cs: CS, - b: &FpGadget, + other: &FpVar, ordering: Ordering, should_also_check_equality: bool, ) -> Result<(), SynthesisError> { - let (left, right) = Self::process_cmp_inputs( - cs.ns(|| "process cmp inputs"), - &self, - b, - ordering, - should_also_check_equality, - )?; - Self::enforce_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), &left, &right) + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.enforce_smaller_than_unchecked(&right) } - /// This function checks the ordering between `self` and `b`. It outputs a + /// This function checks the ordering between `self` and `other`. It outputs self /// `Boolean` that contains the result - `1` if true, `0` otherwise. The /// constraint system will be satisfied in any case. If `self` should - /// also be checked for equality, e.g. `a <= b` instead of `a < b`, set - /// `should_also_check_quality` to `true`. This variant verifies `a` and `b` + /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set + /// `should_also_check_quality` to `true`. This variant verifies `self` and `other` /// are `<= (p-1)/2`. - pub fn is_cmp>( + pub fn is_cmp( &self, - mut cs: CS, - b: &FpGadget, + other: &FpVar, ordering: Ordering, should_also_check_equality: bool, - ) -> Result { - let (left, right) = Self::process_cmp_inputs( - cs.ns(|| "process cmp inputs"), - &self, - b, - ordering, - should_also_check_equality, - )?; - Self::is_smaller_than(cs.ns(|| "enforce smaller than"), &left, &right) + ) -> Result, SynthesisError> { + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.is_smaller_than(&right) } - /// This function checks the ordering between `self` and `b`. It outputs a + /// This function checks the ordering between `self` and `other`. It outputs a /// `Boolean` that contains the result - `1` if true, `0` otherwise. The /// constraint system will be satisfied in any case. If `self` should - /// also be checked for equality, e.g. `a <= b` instead of `a < b`, set - /// `should_also_check_quality` to `true`. This variant assumes `a` and `b` + /// also be checked for equality, e.g. `self <= other` instead of `self < other`, set + /// `should_also_check_quality` to `true`. This variant assumes `self` and `other` /// are `<= (p-1)/2` and does not generate constraints to verify that. - pub fn is_cmp_unchecked>( + pub fn is_cmp_unchecked( &self, - mut cs: CS, - b: &FpGadget, + other: &FpVar, ordering: Ordering, should_also_check_equality: bool, - ) -> Result { - let (left, right) = Self::process_cmp_inputs( - cs.ns(|| "process cmp inputs"), - &self, - b, - ordering, - should_also_check_equality, - )?; - Self::is_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), &left, &right) + ) -> Result, SynthesisError> { + let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?; + left.is_smaller_than_unchecked(&right) } - fn process_cmp_inputs>( - mut cs: CS, - a: &FpGadget, - b: &FpGadget, + fn process_cmp_inputs( + &self, + other: &Self, ordering: Ordering, should_also_check_equality: bool, - ) -> Result<(FpGadget, FpGadget), SynthesisError> { - let left; - let right; - match ordering { - Ordering::Less => { - left = a; - right = b; - } - Ordering::Greater => { - left = b; - right = a; - } - Ordering::Equal => { - return Err(SynthesisError::Unsatisfiable); - } + ) -> Result<(Self, Self), SynthesisError> { + let (left, right) = match ordering { + Ordering::Less => (self, other), + Ordering::Greater => (other, self), + Ordering::Equal => Err(SynthesisError::Unsatisfiable)?, }; let right_for_check = if should_also_check_equality { - right.add_constant(cs.ns(|| "plus one"), &F::one())? + right + F::one() } else { right.clone() }; @@ -129,77 +91,47 @@ impl FpGadget { Ok((left.clone(), right_for_check)) } - // Helper function to enforce `a <= (p-1)/2`. - pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two>( - mut cs: CS, - a: &FpGadget, + // Helper function to enforce `self <= (p-1)/2`. + pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two( + &self, ) -> Result<(), SynthesisError> { - let a_bits = a.to_bits(cs.ns(|| "a to bits"))?; - Boolean::enforce_smaller_or_equal_than::<_, _, F, _>( - cs.ns(|| "enforce smaller than modulus minus one div two"), - &a_bits, + let _ = Boolean::enforce_smaller_or_equal_than( + &self.to_bits()?, F::modulus_minus_one_div_two(), )?; - Ok(()) } - /// Helper function to check `a < b` and output a result bit. This function - /// verifies `a` and `b` are `<= (p-1)/2`. - fn is_smaller_than>( - mut cs: CS, - a: &FpGadget, - b: &FpGadget, - ) -> Result { - Self::enforce_smaller_or_equal_than_mod_minus_one_div_two(cs.ns(|| "check a in range"), a)?; - Self::enforce_smaller_or_equal_than_mod_minus_one_div_two(cs.ns(|| "check b in range"), b)?; - Self::is_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), a, b) + /// Helper function to check `self < other` and output a result bit. This function + /// verifies `self` and `other` are `<= (p-1)/2`. + fn is_smaller_than(&self, other: &FpVar) -> Result, SynthesisError> { + self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + self.is_smaller_than_unchecked(other) } - /// Helper function to check `a < b` and output a result bit. This function - /// assumes `a` and `b` are `<= (p-1)/2` and does not generate constraints + /// Helper function to check `self < other` and output a result bit. This function + /// assumes `self` and `other` are `<= (p-1)/2` and does not generate constraints /// to verify that. - fn is_smaller_than_unchecked>( - mut cs: CS, - a: &FpGadget, - b: &FpGadget, - ) -> Result { - let two = F::one() + F::one(); - let d0 = a.sub(cs.ns(|| "a - b"), b)?; - let d = d0.mul_by_constant(cs.ns(|| "mul 2"), &two)?; - let d_bits = d.to_bits(cs.ns(|| "d to bits"))?; - let d_bits_len = d_bits.len(); - Ok(d_bits[d_bits_len - 1]) + fn is_smaller_than_unchecked(&self, other: &FpVar) -> Result, SynthesisError> { + Ok((self - other).double()?.to_bits()?.last().unwrap().clone()) } - /// Helper function to enforce `a < b`. This function verifies `a` and `b` + /// Helper function to enforce `self < other`. This function verifies `self` and `other` /// are `<= (p-1)/2`. - fn enforce_smaller_than>( - mut cs: CS, - a: &FpGadget, - b: &FpGadget, - ) -> Result<(), SynthesisError> { - Self::enforce_smaller_or_equal_than_mod_minus_one_div_two(cs.ns(|| "check a in range"), a)?; - Self::enforce_smaller_or_equal_than_mod_minus_one_div_two(cs.ns(|| "check b in range"), b)?; - Self::enforce_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), a, b) + fn enforce_smaller_than(&self, other: &FpVar) -> Result<(), SynthesisError> { + self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?; + self.enforce_smaller_than_unchecked(other) } - /// Helper function to enforce `a < b`. This function assumes `a` and `b` + /// Helper function to enforce `self < other`. This function assumes `self` and `other` /// are `<= (p-1)/2` and does not generate constraints to verify that. - fn enforce_smaller_than_unchecked>( - mut cs: CS, - a: &FpGadget, - b: &FpGadget, - ) -> Result<(), SynthesisError> { - let is_smaller_than = Self::is_smaller_than_unchecked(cs.ns(|| "is smaller than"), a, b)?; - cs.enforce( - || "enforce smaller than", - |_| is_smaller_than.lc(CS::one(), F::one()), - |lc| lc + (F::one(), CS::one()), - |lc| lc + (F::one(), CS::one()), - ); - - Ok(()) + fn enforce_smaller_than_unchecked(&self, other: &FpVar) -> Result<(), SynthesisError> { + let cs = [self, other].cs().unwrap(); + let is_smaller_than = self.is_smaller_than_unchecked(other)?; + let lc_one = lc!() + Variable::One; + cs.enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one) } } @@ -209,9 +141,7 @@ mod test { use rand_xorshift::XorShiftRng; use std::cmp::Ordering; - use crate::{ - alloc::AllocGadget, fields::fp::FpGadget, test_constraint_system::TestConstraintSystem, - }; + use crate::{alloc::AllocVar, fields::fp::FpVar}; use algebra::{bls12_381::Fr, PrimeField, UniformRand}; use r1cs_core::ConstraintSystem; @@ -233,43 +163,20 @@ mod test { r } for i in 0..10 { - let mut cs = TestConstraintSystem::::new(); + let cs = ConstraintSystem::::new_ref(); let a = rand_in_range(&mut rng); - let a_var = FpGadget::::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); + let a_var = FpVar::::new_witness(cs.ns("a"), || Ok(a)).unwrap(); let b = rand_in_range(&mut rng); - let b_var = FpGadget::::alloc(cs.ns(|| "b"), || Ok(b)).unwrap(); + let b_var = FpVar::::new_witness(cs.ns("b"), || Ok(b)).unwrap(); match a.cmp(&b) { Ordering::Less => { - a_var - .enforce_cmp(cs.ns(|| "smaller than test"), &b_var, Ordering::Less, false) - .unwrap(); - a_var - .enforce_cmp( - cs.ns(|| "smaller than test 2"), - &b_var, - Ordering::Less, - true, - ) - .unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap(); } Ordering::Greater => { - a_var - .enforce_cmp( - cs.ns(|| "smaller than test"), - &b_var, - Ordering::Greater, - false, - ) - .unwrap(); - a_var - .enforce_cmp( - cs.ns(|| "smaller than test 2"), - &b_var, - Ordering::Greater, - true, - ) - .unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap(); } _ => {} } @@ -277,79 +184,46 @@ mod test { if i == 0 { println!("number of constraints: {}", cs.num_constraints()); } - assert!(cs.is_satisfied()); + assert!(cs.is_satisfied().unwrap()); } for _i in 0..10 { - let mut cs = TestConstraintSystem::::new(); + let cs = ConstraintSystem::::new_ref(); let a = rand_in_range(&mut rng); - let a_var = FpGadget::::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); + let a_var = FpVar::::new_witness(cs.ns("a"), || Ok(a)).unwrap(); let b = rand_in_range(&mut rng); - let b_var = FpGadget::::alloc(cs.ns(|| "b"), || Ok(b)).unwrap(); + let b_var = FpVar::::new_witness(cs.ns("b"), || Ok(b)).unwrap(); match b.cmp(&a) { Ordering::Less => { - a_var - .enforce_cmp(cs.ns(|| "smaller than test"), &b_var, Ordering::Less, false) - .unwrap(); - a_var - .enforce_cmp( - cs.ns(|| "smaller than test 2"), - &b_var, - Ordering::Less, - true, - ) - .unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap(); } Ordering::Greater => { - a_var - .enforce_cmp( - cs.ns(|| "smaller than test"), - &b_var, - Ordering::Greater, - false, - ) - .unwrap(); - a_var - .enforce_cmp( - cs.ns(|| "smaller than test 2"), - &b_var, - Ordering::Greater, - true, - ) - .unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap(); + a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap(); } _ => {} } - assert!(!cs.is_satisfied()); + assert!(!cs.is_satisfied().unwrap()); } for _i in 0..10 { - let mut cs = TestConstraintSystem::::new(); + let cs = ConstraintSystem::::new_ref(); let a = rand_in_range(&mut rng); - let a_var = FpGadget::::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); - a_var - .enforce_cmp(cs.ns(|| "smaller than test"), &a_var, Ordering::Less, false) - .unwrap(); + let a_var = FpVar::::new_witness(cs.ns("a"), || Ok(a)).unwrap(); + a_var.enforce_cmp(&a_var, Ordering::Less, false).unwrap(); - assert!(!cs.is_satisfied()); + assert!(!cs.is_satisfied().unwrap()); } for _i in 0..10 { - let mut cs = TestConstraintSystem::::new(); + let cs = ConstraintSystem::::new_ref(); let a = rand_in_range(&mut rng); - let a_var = FpGadget::::alloc(cs.ns(|| "a"), || Ok(a)).unwrap(); - a_var - .enforce_cmp( - cs.ns(|| "smaller than or equal to test"), - &a_var, - Ordering::Less, - true, - ) - .unwrap(); - - assert!(cs.is_satisfied()); + let a_var = FpVar::::new_witness(cs.ns("a"), || Ok(a)).unwrap(); + a_var.enforce_cmp(&a_var, Ordering::Less, true).unwrap(); + assert!(cs.is_satisfied().unwrap()); } } } diff --git a/r1cs-std/src/fields/fp/mod.rs b/r1cs-std/src/fields/fp/mod.rs index da8192c..672e29b 100644 --- a/r1cs-std/src/fields/fp/mod.rs +++ b/r1cs-std/src/fields/fp/mod.rs @@ -1,382 +1,351 @@ use algebra::{bytes::ToBytes, FpParameters, PrimeField}; -use r1cs_core::{ - ConstraintSystem, - ConstraintVar::{self, *}, - LinearCombination, SynthesisError, -}; +use r1cs_core::{lc, ConstraintSystemRef, LinearCombination, Namespace, SynthesisError, Variable}; use core::borrow::Borrow; +use crate::fields::{FieldOpsBounds, FieldVar}; use crate::{boolean::AllocatedBit, prelude::*, Assignment, Vec}; pub mod cmp; -#[derive(Debug)] -pub struct FpGadget { - pub value: Option, - pub variable: ConstraintVar, +#[derive(Debug, Clone)] +#[must_use] +pub struct AllocatedFp { + pub(crate) value: Option, + pub variable: Variable, + pub cs: ConstraintSystemRef, } -impl FpGadget { - #[inline] - pub fn from>(mut cs: CS, value: &F) -> Self { - Self::alloc(cs.ns(|| "from"), || Ok(*value)).unwrap() - } - - fn is_constant(&self) -> bool { - match &self.variable { - // If you don't do alloc_constant, you are guaranteed to get a variable, - // hence we assume that all variables are not the constant variable. - // Technically this omits recognizing some constants. - // E.g. given variables w,x,y,z with constraints: - // w = x + 1 - // y = -x + 1 - // and then created the variable z = w + y, - // this would not recognize that z is in fact a constant. - // Since this is an edge case, this is left as a TODO. - Var(_v) => false, - LC(l) => l.is_constant(), +impl AllocatedFp { + pub fn new(value: Option, variable: Variable, cs: ConstraintSystemRef) -> Self { + Self { + value, + variable, + cs, } } } -impl ToConstraintFieldGadget for FpGadget { - fn to_constraint_field>( - &self, - _cs: CS, - ) -> Result>, SynthesisError> { - Ok(vec![self.clone()]) - } +/// Represent variables corresponding to the field `F`. +#[derive(Clone, Debug)] +#[must_use] +pub enum FpVar { + Constant(F), + Var(AllocatedFp), } -impl FieldGadget for FpGadget { - type Variable = ConstraintVar; +impl R1CSVar for FpVar { + type Value = F; - #[inline] - fn get_value(&self) -> Option { - self.value + fn cs(&self) -> Option> { + match self { + Self::Constant(_) => None, + Self::Var(a) => Some(a.cs.clone()), + } } - #[inline] - fn get_variable(&self) -> Self::Variable { - self.variable.clone() + fn value(&self) -> Result { + match self { + Self::Constant(v) => Ok(*v), + Self::Var(v) => v.value(), + } } +} - #[inline] - fn zero>(_cs: CS) -> Result { - let value = Some(F::zero()); - Ok(FpGadget { - value, - variable: ConstraintVar::zero(), - }) +impl From> for FpVar { + fn from(other: Boolean) -> Self { + if let Boolean::Constant(b) = other { + Self::Constant(F::from(b as u8)) + } else { + // `other` is a variable + let cs = other.cs().unwrap(); + let variable = cs.new_lc(other.lc()).unwrap(); + Self::Var(AllocatedFp::new( + other.value().ok().map(|b| F::from(b as u8)), + variable, + cs, + )) + } } +} - #[inline] - fn one>(_cs: CS) -> Result { - let value = Some(F::one()); - Ok(FpGadget { - value, - variable: CS::one().into(), - }) +impl From> for FpVar { + fn from(other: AllocatedFp) -> Self { + Self::Var(other) } +} - #[inline] - fn conditionally_add_constant>( - &self, - mut _cs: CS, - bit: &Boolean, - coeff: F, - ) -> Result { - let value = match (self.value, bit.get_value()) { - (Some(v), Some(b)) => Some(if b { v + &coeff } else { v }), - (..) => None, - }; - Ok(FpGadget { - value, - variable: LC(bit.lc(CS::one(), coeff)) + &self.variable, - }) +impl<'a, F: PrimeField> FieldOpsBounds<'a, F, Self> for FpVar {} +impl<'a, F: PrimeField> FieldOpsBounds<'a, F, FpVar> for &'a FpVar {} + +impl AllocatedFp { + fn from(other: Boolean) -> Self { + if let Some(cs) = other.cs() { + let variable = cs.new_lc(other.lc()).unwrap(); + Self::new(other.value().ok().map(|b| F::from(b as u8)), variable, cs) + } else { + unreachable!("Cannot create a constant value") + } } - #[inline] - fn add>( - &self, - mut _cs: CS, - other: &Self, - ) -> Result { + fn value(&self) -> Result { + self.cs.assigned_value(self.variable).get() + } + + fn add(&self, other: &Self) -> Self { let value = match (self.value, other.value) { (Some(val1), Some(val2)) => Some(val1 + &val2), (..) => None, }; - Ok(FpGadget { - value, - variable: &self.variable + &other.variable, - }) - } - - fn double>(&self, _cs: CS) -> Result { - let value = self.value.map(|val| val.double()); - let mut variable = self.variable.clone(); - variable.double_in_place(); - Ok(FpGadget { value, variable }) + let variable = self + .cs + .new_lc(lc!() + self.variable + other.variable) + .unwrap(); + AllocatedFp::new(value, variable, self.cs.clone()) } - fn double_in_place>( - &mut self, - _cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.value.as_mut().map(|val| val.double_in_place()); - self.variable.double_in_place(); - Ok(self) - } - - #[inline] - fn sub>( - &self, - mut _cs: CS, - other: &Self, - ) -> Result { + fn sub(&self, other: &Self) -> Self { let value = match (self.value, other.value) { (Some(val1), Some(val2)) => Some(val1 - &val2), (..) => None, }; - Ok(FpGadget { - value, - variable: &self.variable - &other.variable, - }) + let variable = self + .cs + .new_lc(lc!() + self.variable - other.variable) + .unwrap(); + AllocatedFp::new(value, variable, self.cs.clone()) } - #[inline] - fn negate>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.negate_in_place(cs)?; - Ok(result) + fn mul(&self, other: &Self) -> Self { + let product = AllocatedFp::new_witness(self.cs.clone(), || { + Ok(self.value.get()? * &other.value.get()?) + }) + .unwrap(); + self.cs + .enforce_constraint( + lc!() + self.variable, + lc!() + other.variable, + lc!() + product.variable, + ) + .unwrap(); + product + } + + fn add_constant(&self, other: F) -> Self { + if other.is_zero() { + self.clone() + } else { + let value = self.value.map(|val| val + other); + let variable = self + .cs + .new_lc(lc!() + self.variable + (other, Variable::One)) + .unwrap(); + AllocatedFp::new(value, variable, self.cs.clone()) + } } - #[inline] - fn negate_in_place>( - &mut self, - _cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.value.as_mut().map(|val| *val = -(*val)); - self.variable.negate_in_place(); - Ok(self) + fn sub_constant(&self, other: F) -> Self { + self.add_constant(-other) } - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Apply constant folding if it applies - // unwrap is used, because these values are guaranteed to exist. - if other.is_constant() { - return self.mul_by_constant(cs, &other.get_value().unwrap()); - } else if self.is_constant() { - return other.mul_by_constant(cs, &self.get_value().unwrap()); + fn mul_constant(&self, other: F) -> Self { + if other.is_one() { + self.clone() + } else { + let value = self.value.map(|val| val * other); + let variable = self.cs.new_lc(lc!() + (other, self.variable)).unwrap(); + AllocatedFp::new(value, variable, self.cs.clone()) } - - let product = Self::alloc(cs.ns(|| "mul"), || { - Ok(self.value.get()? * &other.value.get()?) - })?; - cs.enforce( - || "mul_constraint", - |lc| &self.variable + lc, - |lc| &other.variable + lc, - |lc| &product.variable + lc, - ); - Ok(product) } - #[inline] - fn add_constant>( - &self, - _cs: CS, - other: &F, - ) -> Result { - let value = self.value.map(|val| val + other); - Ok(FpGadget { - value, - variable: self.variable.clone() + (*other, CS::one()), - }) + fn double(&self) -> Result { + let value = self.value.map(|val| val.double()); + let variable = self.cs.new_lc(lc!() + self.variable + self.variable)?; + Ok(Self::new(value, variable, self.cs.clone())) } #[inline] - fn add_constant_in_place>( - &mut self, - _cs: CS, - other: &F, - ) -> Result<&mut Self, SynthesisError> { - self.value.as_mut().map(|val| *val += other); - self.variable += (*other, CS::one()); - Ok(self) + fn negate(&self) -> Self { + let mut result = self.clone(); + result.negate_in_place(); + result } #[inline] - fn mul_by_constant>( - &self, - cs: CS, - other: &F, - ) -> Result { - let mut result = self.clone(); - result.mul_by_constant_in_place(cs, other)?; - Ok(result) + fn negate_in_place(&mut self) -> &mut Self { + self.value.as_mut().map(|val| *val = -(*val)); + self.variable = self.cs.new_lc(lc!() - self.variable).unwrap(); + self } - #[inline] - fn mul_by_constant_in_place>( - &mut self, - mut _cs: CS, - other: &F, - ) -> Result<&mut Self, SynthesisError> { - self.value.as_mut().map(|val| *val *= other); - self.variable *= *other; - Ok(self) + fn square(&self) -> Result { + Ok(self.mul(self)) } #[inline] - fn inverse>(&self, mut cs: CS) -> Result { - let inverse = Self::alloc(cs.ns(|| "inverse"), || { - let result = self.value.get()?; - let inv = result.inverse().expect("Inverse doesn't exist!"); - Ok(inv) - })?; + fn inverse(&self) -> Result { + let inverse = Self::new_witness(self.cs.clone(), || self.value.get()?.inverse().get())?; - let one = CS::one(); - cs.enforce( - || "inv_constraint", - |lc| &self.variable + lc, - |lc| &inverse.variable + lc, - |lc| lc + one, - ); + self.cs.enforce_constraint( + lc!() + self.variable, + lc!() + inverse.variable, + lc!() + Variable::One, + )?; Ok(inverse) } - fn frobenius_map>( - &self, - _: CS, - _: usize, - ) -> Result { + fn frobenius_map(&self, _: usize) -> Result { Ok(self.clone()) } - fn frobenius_map_in_place>( - &mut self, - _: CS, - _: usize, - ) -> Result<&mut Self, SynthesisError> { - Ok(self) + fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { + self.cs.enforce_constraint( + lc!() + self.variable, + lc!() + other.variable, + lc!() + result.variable, + ) } - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - cs.enforce( - || "mul_constraint", - |lc| &self.variable + lc, - |lc| &other.variable + lc, - |lc| &result.variable + lc, - ); - Ok(()) + fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> { + self.cs.enforce_constraint( + lc!() + self.variable, + lc!() + self.variable, + lc!() + result.variable, + ) } - fn square_equals>( - &self, - mut cs: CS, - result: &Self, - ) -> Result<(), SynthesisError> { - cs.enforce( - || "sqr_constraint", - |lc| &self.variable + lc, - |lc| &self.variable + lc, - |lc| &result.variable + lc, - ); - Ok(()) - } - - fn cost_of_mul() -> usize { - 1 - } - - fn cost_of_mul_equals() -> usize { - 1 + /// Outputs the bit `self == other`. + /// + /// # Constraint cost + /// + /// Consumes three constraints + fn is_eq(&self, other: &Self) -> Result, SynthesisError> { + Ok(self.is_neq(other)?.not()) } - fn cost_of_inv() -> usize { - 1 - } -} + /// Outputs the bit `self != other`. + /// + /// # Constraint cost + /// + /// Consumes three constraints + fn is_neq(&self, other: &Self) -> Result, SynthesisError> { + let is_not_equal = Boolean::new_witness(self.cs.clone(), || { + Ok(self.value.get()? != other.value.get()?) + })?; + let multiplier = self.cs.new_witness_variable(|| { + if is_not_equal.value()? { + (self.value.get()? - other.value.get()?).inverse().get() + } else { + Ok(F::one()) + } + })?; -impl PartialEq for FpGadget { - fn eq(&self, other: &Self) -> bool { - self.value.is_some() && other.value.is_some() && self.value == other.value + // Completeness: + // Case 1: self != other: + // ---------------------- + // constraint 1: + // (self - other) * multiplier = is_not_equal + // => (non_zero) * multiplier = 1 (satisfied, because multiplier = 1/(self - other) + // + // constraint 2: + // (self - other) * not(is_not_equal) = 0 + // => (non_zero) * not(1) = 0 + // => (non_zero) * 0 = 0 + // + // Case 2: self == other: + // ---------------------- + // constraint 1: + // (self - other) * multiplier = is_not_equal + // => 0 * multiplier = 0 (satisfied, because multiplier = 1 + // + // constraint 2: + // (self - other) * not(is_not_equal) = 0 + // => 0 * not(0) = 0 + // => 0 * 1 = 0 + // + // -------------------------------------------------------------------- + // + // Soundness: + // Case 1: self != other, but is_not_equal = 0. + // -------------------------------------------- + // constraint 1: + // (self - other) * multiplier = is_not_equal + // => non_zero * multiplier = 0 (only satisfiable if multiplier == 0) + // + // constraint 2: + // (self - other) * not(is_not_equal) = 0 + // => (non_zero) * 1 = 0 (impossible) + // + // Case 2: self == other, but is_not_equal = 1. + // -------------------------------------------- + // constraint 1: + // (self - other) * multiplier = is_not_equal + // 0 * multiplier = 1 (unsatisfiable) + self.cs.enforce_constraint( + lc!() + self.variable - other.variable, + lc!() + multiplier, + is_not_equal.lc(), + )?; + self.cs.enforce_constraint( + lc!() + self.variable - other.variable, + is_not_equal.not().lc(), + lc!(), + )?; + Ok(is_not_equal) } -} - -impl Eq for FpGadget {} -impl EqGadget for FpGadget {} - -impl ConditionalEqGadget for FpGadget { #[inline] - fn conditional_enforce_equal>( + fn conditional_enforce_equal( &self, - mut cs: CS, other: &Self, - condition: &Boolean, + should_enforce: &Boolean, ) -> Result<(), SynthesisError> { - let difference = self.sub(cs.ns(|| "difference"), other)?; - let one = CS::one(); - let one_const = F::one(); - cs.enforce( - || "conditional_equals", - |lc| &difference.variable + lc, - |lc| lc + &condition.lc(one, one_const), - |lc| lc, - ); - Ok(()) + self.cs.enforce_constraint( + lc!() + self.variable - other.variable, + lc!() + should_enforce.lc(), + lc!(), + ) } - fn cost() -> usize { - 1 - } -} - -impl NEqGadget for FpGadget { #[inline] - fn enforce_not_equal>( + fn conditional_enforce_not_equal( &self, - mut cs: CS, other: &Self, + should_enforce: &Boolean, ) -> Result<(), SynthesisError> { - let a_minus_b = self.sub(cs.ns(|| "A - B"), other)?; - a_minus_b.inverse(cs.ns(|| "Enforce inverse exists"))?; - Ok(()) - } + let multiplier = Self::new_witness(self.cs.clone(), || { + if should_enforce.value()? { + (self.value.get()? - other.value.get()?).inverse().get() + } else { + Ok(F::zero()) + } + })?; - fn cost() -> usize { - 1 + self.cs.enforce_constraint( + lc!() + self.variable - other.variable, + lc!() + multiplier.variable, + should_enforce.lc(), + )?; + Ok(()) } } -impl ToBitsGadget for FpGadget { +/****************************************************************************/ +/****************************************************************************/ + +impl ToBitsGadget for AllocatedFp { /// Outputs the unique bit-wise decomposition of `self` in *big-endian* /// form. - fn to_bits>(&self, mut cs: CS) -> Result, SynthesisError> { - let bits = self.to_non_unique_bits(&mut cs)?; - Boolean::enforce_in_field::<_, _, F>(&mut cs, &bits)?; + fn to_bits(&self) -> Result>, SynthesisError> { + let bits = self.to_non_unique_bits()?; + Boolean::enforce_in_field(&bits)?; Ok(bits) } - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { + fn to_non_unique_bits(&self) -> Result>, SynthesisError> { + let cs = self.cs.clone(); let num_bits = F::Params::MODULUS_BITS; use algebra::BitIterator; let bit_values = match self.value { @@ -402,38 +371,35 @@ impl ToBitsGadget for FpGadget { }; let mut bits = vec![]; - for (i, b) in bit_values.into_iter().enumerate() { - bits.push(AllocatedBit::alloc(cs.ns(|| format!("bit {}", i)), || { - b.get() - })?); + for b in bit_values { + bits.push(AllocatedBit::new_witness(cs.clone(), || b.get())?); } let mut lc = LinearCombination::zero(); let mut coeff = F::one(); for bit in bits.iter().rev() { - lc += (coeff, bit.get_variable()); + lc += (coeff, bit.variable()); coeff.double_in_place(); } - lc = &self.variable - lc; + lc = lc - &self.variable; - cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc); + cs.enforce_constraint(lc!(), lc!(), lc)?; Ok(bits.into_iter().map(Boolean::from).collect()) } } -impl ToBytesGadget for FpGadget { +impl ToBytesGadget for AllocatedFp { /// Outputs the unique byte decomposition of `self` in *little-endian* /// form. - fn to_bytes>(&self, mut cs: CS) -> Result, SynthesisError> { - let bytes = self.to_non_unique_bytes(&mut cs)?; - Boolean::enforce_in_field::<_, _, F>( - &mut cs, + fn to_bytes(&self) -> Result>, SynthesisError> { + let bytes = self.to_non_unique_bytes()?; + Boolean::enforce_in_field( &bytes.iter() - .flat_map(|byte_gadget| byte_gadget.into_bits_le()) + .flat_map(|b| b.into_bits_le()) // This reverse maps the bits into big-endian form, as required by `enforce_in_field`. .rev() .collect::>(), @@ -442,12 +408,11 @@ impl ToBytesGadget for FpGadget { Ok(bytes) } - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let cs = self.cs.clone(); let byte_values = match self.value { - Some(value) => to_bytes![&value.into_repr()]? + Some(value) => to_bytes![&value.into_repr()] + .unwrap() .into_iter() .map(Some) .collect::>(), @@ -458,222 +423,519 @@ impl ToBytesGadget for FpGadget { } }; - let bytes = UInt8::alloc_vec(cs.ns(|| "Alloc bytes"), &byte_values)?; + let bytes = UInt8::new_witness_vec(cs.clone(), &byte_values)?; let mut lc = LinearCombination::zero(); let mut coeff = F::one(); - for bit in bytes - .iter() - .flat_map(|byte_gadget| byte_gadget.bits.clone()) - { + for bit in bytes.iter().flat_map(|b| b.bits.clone()) { match bit { Boolean::Is(bit) => { - lc += (coeff, bit.get_variable()); + lc += (coeff, bit.variable()); coeff.double_in_place(); } Boolean::Constant(_) | Boolean::Not(_) => unreachable!(), } } - lc = &self.variable - lc; + lc = lc - &self.variable; - cs.enforce(|| "unpacking_constraint", |lc| lc, |lc| lc, |_| lc); + cs.enforce_constraint(lc!(), lc!(), lc)?; Ok(bytes) } } -impl CondSelectGadget for FpGadget { +impl CondSelectGadget for AllocatedFp { #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - true_value: &Self, - false_value: &Self, + fn conditionally_select( + cond: &Boolean, + true_val: &Self, + false_val: &Self, ) -> Result { - if let Boolean::Constant(cond) = *cond { - if cond { - Ok(true_value.clone()) - } else { - Ok(false_value.clone()) + match cond { + Boolean::Constant(true) => Ok(true_val.clone()), + Boolean::Constant(false) => Ok(false_val.clone()), + _ => { + let cs = cond.cs().unwrap(); + let result = Self::new_witness(cs.clone(), || { + cond.value() + .and_then(|c| if c { true_val } else { false_val }.value.get()) + })?; + // a = self; b = other; c = cond; + // + // r = c * a + (1 - c) * b + // r = b + c * (a - b) + // c * (a - b) = r - b + cs.enforce_constraint( + cond.lc(), + lc!() + true_val.variable - false_val.variable, + lc!() + result.variable - false_val.variable, + )?; + + Ok(result) } - } else { - let result = Self::alloc(cs.ns(|| ""), || { - cond.get_value() - .and_then(|cond| if cond { true_value } else { false_value }.get_value()) - .get() + } + } +} + +/// Uses two bits to perform a lookup into a table +/// `b` is little-endian: `b[0]` is LSB. +impl TwoBitLookupGadget for AllocatedFp { + type TableConstant = F; + fn two_bit_lookup(b: &[Boolean], c: &[Self::TableConstant]) -> Result { + debug_assert_eq!(b.len(), 2); + debug_assert_eq!(c.len(), 4); + if let Some(cs) = b.cs() { + let result = Self::new_witness(cs.clone(), || { + let lsb = b[0].value()? as usize; + let msb = b[1].value()? as usize; + let index = lsb + (msb << 1); + Ok(c[index]) })?; - // a = self; b = other; c = cond; - // - // r = c * a + (1 - c) * b - // r = b + c * (a - b) - // c * (a - b) = r - b - let one = CS::one(); - cs.enforce( - || "conditionally_select", - |_| cond.lc(one, F::one()), - |lc| (&true_value.variable - &false_value.variable) + lc, - |lc| (&result.variable - &false_value.variable) + lc, - ); + let one = Variable::One; + cs.enforce_constraint( + lc!() + b[1].lc() * (c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one), + lc!() + b[0].lc(), + lc!() + result.variable - (c[0], one) + b[1].lc() * (c[0] - &c[2]), + )?; Ok(result) + } else { + unreachable!("must provide a way to obtain a ConstraintSystemRef") } } - - fn cost() -> usize { - 1 - } } -/// Uses two bits to perform a lookup into a table -/// `b` is little-endian: `b[0]` is LSB. -impl TwoBitLookupGadget for FpGadget { + +impl ThreeBitCondNegLookupGadget for AllocatedFp { type TableConstant = F; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], + + fn three_bit_cond_neg_lookup( + b: &[Boolean], + b0b1: &Boolean, c: &[Self::TableConstant], ) -> Result { - debug_assert!(b.len() == 2); - debug_assert!(c.len() == 4); - - let result = Self::alloc(cs.ns(|| "Allocate lookup result"), || { - match (b[0].get_value().get()?, b[1].get_value().get()?) { - (false, false) => Ok(c[0]), - (false, true) => Ok(c[2]), - (true, false) => Ok(c[1]), - (true, true) => Ok(c[3]), - } - })?; - let one = CS::one(); - cs.enforce( - || "Enforce lookup", - |lc| lc + b[1].lc(one, c[3] - &c[2] - &c[1] + &c[0]) + (c[1] - &c[0], one), - |lc| lc + b[0].lc(one, F::one()), - |lc| result.get_variable() + lc + (-c[0], one) + b[1].lc(one, c[0] - &c[2]), - ); + debug_assert_eq!(b.len(), 3); + debug_assert_eq!(c.len(), 4); + + if let Some(cs) = b.cs() { + let result = Self::new_witness(cs.clone(), || { + let lsb = b[0].value()? as usize; + let msb = b[1].value()? as usize; + let index = lsb + (msb << 1); + let intermediate = c[index]; + + let is_negative = b[2].value()?; + let y = if is_negative { + -intermediate + } else { + intermediate + }; + Ok(y) + })?; - Ok(result) - } + let y_lc = b0b1.lc() * (c[3] - &c[2] - &c[1] + &c[0]) + + b[0].lc() * (c[1] - &c[0]) + + b[1].lc() * (c[2] - &c[0]) + + (c[0], Variable::One); + cs.enforce_constraint( + y_lc.clone() + y_lc.clone(), + b[2].lc(), + y_lc.clone() - result.variable, + )?; - fn cost() -> usize { - 1 + Ok(result) + } else { + unreachable!("must provide a way to obtain a ConstraintSystemRef") + } } } -impl ThreeBitCondNegLookupGadget for FpGadget { - type TableConstant = F; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], +impl AllocVar for AllocatedFp { + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, ) -> Result { - debug_assert!(b.len() == 3); - debug_assert!(c.len() == 4); - - let result = Self::alloc(cs.ns(|| "Allocate lookup result"), || { - let y = match (b[0].get_value().get()?, b[1].get_value().get()?) { - (false, false) => c[0], - (false, true) => c[2], - (true, false) => c[1], - (true, true) => c[3], + let ns = cs.into(); + let cs = ns.cs(); + if mode == AllocationMode::Constant { + let v = *f()?.borrow(); + let lc = cs.new_lc(lc!() + (v, Variable::One))?; + Ok(Self::new(Some(v), lc, cs)) + } else { + let mut value = None; + let value_generator = || { + value = Some(*f()?.borrow()); + value.ok_or(SynthesisError::AssignmentMissing) }; - if b[2].get_value().get()? { - Ok(-y) + let variable = if mode == AllocationMode::Input { + cs.new_input_variable(value_generator)? } else { - Ok(y) + cs.new_witness_variable(value_generator)? + }; + Ok(Self::new(value, variable, cs.clone())) + } + } +} + +impl FieldVar for FpVar { + fn constant(f: F) -> Self { + Self::Constant(f) + } + + fn zero() -> Self { + Self::Constant(F::zero()) + } + + fn one() -> Self { + Self::Constant(F::one()) + } + + fn double(&self) -> Result { + match self { + Self::Constant(c) => Ok(Self::Constant(c.double())), + Self::Var(v) => Ok(Self::Var(v.double()?)), + } + } + + fn negate(&self) -> Result { + match self { + Self::Constant(c) => Ok(Self::Constant(-*c)), + Self::Var(v) => Ok(Self::Var(v.negate())), + } + } + + fn square(&self) -> Result { + match self { + Self::Constant(c) => Ok(Self::Constant(c.square())), + Self::Var(v) => Ok(Self::Var(v.square()?)), + } + } + + /// Enforce that `self * other == result`. + fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { + use FpVar::*; + match (self, other, result) { + (Constant(_), Constant(_), Constant(_)) => Ok(()), + (Constant(_), Constant(_), _) | (Constant(_), Var(_), _) | (Var(_), Constant(_), _) => { + result.enforce_equal(&(self * other)) + } // this multiplication should be free + (Var(v1), Var(v2), Var(v3)) => v1.mul_equals(v2, v3), + (Var(v1), Var(v2), Constant(f)) => { + let cs = v1.cs.clone(); + let v3 = AllocatedFp::new_constant(cs.clone(), f).unwrap(); + v1.mul_equals(v2, &v3) } - })?; + } + } - let one = CS::one(); - let y_lc = b0b1.lc(one, c[3] - &c[2] - &c[1] + &c[0]) - + b[0].lc(one, c[1] - &c[0]) - + b[1].lc(one, c[2] - &c[0]) - + (c[0], one); - cs.enforce( - || "Enforce lookup", - |_| y_lc.clone() + y_lc.clone(), - |lc| lc + b[2].lc(one, F::one()), - |_| -result.get_variable() + y_lc.clone(), - ); + /// Enforce that `self * self == result`. + fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> { + use FpVar::*; + match (self, result) { + (Constant(_), Constant(_)) => Ok(()), + (Constant(f), Var(r)) => { + let cs = r.cs.clone(); + let v = AllocatedFp::new_witness(cs, || Ok(f))?; + v.square_equals(&r) + } + (Var(v), Constant(f)) => { + let cs = v.cs.clone(); + let r = AllocatedFp::new_witness(cs, || Ok(f))?; + v.square_equals(&r) + } + (Var(v1), Var(v2)) => v1.square_equals(v2), + } + } - Ok(result) + fn inverse(&self) -> Result { + match self { + FpVar::Var(v) => v.inverse().map(FpVar::Var), + FpVar::Constant(f) => f.inverse().get().map(FpVar::Constant), + } } - fn cost() -> usize { - 2 + /// Returns (self / denominator), but requires fewer constraints than + /// self * denominator.inverse() + /// It is up to the caller to ensure that denominator is non-zero, + /// since in that case the result is unconstrained. + fn mul_by_inverse(&self, denominator: &Self) -> Result { + use FpVar::*; + match (self, denominator) { + (Constant(s), Constant(d)) => Ok(Constant(*s / *d)), + (Var(s), Constant(d)) => Ok(Var(s.mul_constant(d.inverse().get()?))), + (Constant(s), Var(d)) => Ok(Var(d.inverse()?.mul_constant(*s))), + (Var(s), Var(d)) => Ok(Var(d.inverse()?.mul(s))), + } } -} -impl Clone for FpGadget { - fn clone(&self) -> Self { - Self { - value: self.value.clone(), - variable: self.variable.clone(), + fn frobenius_map(&self, power: usize) -> Result { + match self { + FpVar::Var(v) => v.frobenius_map(power).map(FpVar::Var), + FpVar::Constant(f) => { + let mut f = *f; + f.frobenius_map(power); + Ok(FpVar::Constant(f)) + } } } + + fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> { + *self = self.frobenius_map(power)?; + Ok(self) + } } -impl AllocGadget for FpGadget { +/****************************************************************************/ +/****************************************************************************/ + +impl_ops!( + FpVar, + F, + Add, + add, + AddAssign, + add_assign, + |this: &'a FpVar, other: &'a FpVar| { + use FpVar::*; + match (this, other) { + (Constant(c1), Constant(c2)) => Constant(*c1 + *c2), + (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(*c)), + (Var(v1), Var(v2)) => Var(v1.add(v2)), + } + }, + |this: &'a FpVar, other: F| { this + &FpVar::Constant(other) }, + F: PrimeField, +); + +impl_ops!( + FpVar, + F, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a FpVar, other: &'a FpVar| { + use FpVar::*; + match (this, other) { + (Constant(c1), Constant(c2)) => Constant(*c1 - *c2), + (Var(v), Constant(c)) => Var(v.sub_constant(*c)), + (Constant(c), Var(v)) => Var(v.sub_constant(*c).negate()), + (Var(v1), Var(v2)) => Var(v1.sub(v2)), + } + }, + |this: &'a FpVar, other: F| { this - &FpVar::Constant(other) }, + F: PrimeField +); + +impl_ops!( + FpVar, + F, + Mul, + mul, + MulAssign, + mul_assign, + |this: &'a FpVar, other: &'a FpVar| { + use FpVar::*; + match (this, other) { + (Constant(c1), Constant(c2)) => Constant(*c1 * *c2), + (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(*c)), + (Var(v1), Var(v2)) => Var(v1.mul(v2)), + } + }, + |this: &'a FpVar, other: F| { + if other.is_zero() { + FpVar::zero() + } else { + this * &FpVar::Constant(other) + } + }, + F: PrimeField +); + +/****************************************************************************/ +/****************************************************************************/ + +impl EqGadget for FpVar { + fn is_eq(&self, other: &Self) -> Result, SynthesisError> { + match (self, other) { + (Self::Constant(c1), Self::Constant(c2)) => Ok(Boolean::Constant(c1 == c2)), + (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { + let cs = v.cs.clone(); + let c = AllocatedFp::new_constant(cs, c)?; + c.is_eq(v) + } + (Self::Var(v1), Self::Var(v2)) => v1.is_eq(v2), + } + } + #[inline] - fn alloc_constant>(_cs: CS, t: T) -> Result - where - T: Borrow, - { - let value = t.borrow().clone(); - Ok(Self { - value: Some(value), - variable: LinearCombination::from((value, CS::one())).into(), - }) + fn conditional_enforce_equal( + &self, + other: &Self, + should_enforce: &Boolean, + ) -> Result<(), SynthesisError> { + match (self, other) { + (Self::Constant(_), Self::Constant(_)) => Ok(()), + (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { + let cs = v.cs.clone(); + let c = AllocatedFp::new_constant(cs, c)?; + c.conditional_enforce_equal(v, should_enforce) + } + (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce), + } } #[inline] - fn alloc>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow, - { - let mut value = None; - let variable = cs.alloc( - || "alloc", - || { - let tmp = *value_gen()?.borrow(); - value = Some(tmp); - Ok(tmp) - }, - )?; - Ok(FpGadget { - value, - variable: Var(variable), - }) + fn conditional_enforce_not_equal( + &self, + other: &Self, + should_enforce: &Boolean, + ) -> Result<(), SynthesisError> { + match (self, other) { + (Self::Constant(_), Self::Constant(_)) => Ok(()), + (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { + let cs = v.cs.clone(); + let c = AllocatedFp::new_constant(cs, c)?; + c.conditional_enforce_not_equal(v, should_enforce) + } + (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce), + } + } +} + +impl ToBitsGadget for FpVar { + /// Outputs the unique bit-wise decomposition of `self` in *big-endian* + /// form. + fn to_bits(&self) -> Result>, SynthesisError> { + match self { + Self::Constant(c) => UInt8::constant_vec(&to_bytes![c].unwrap()).to_bits(), + Self::Var(v) => v.to_bits(), + } + } + + fn to_non_unique_bits(&self) -> Result>, SynthesisError> { + match self { + Self::Constant(c) => UInt8::constant_vec(&to_bytes![c].unwrap()).to_non_unique_bits(), + Self::Var(v) => v.to_non_unique_bits(), + } } +} +impl ToBytesGadget for FpVar { + /// Outputs the unique byte decomposition of `self` in *little-endian* + /// form. + fn to_bytes(&self) -> Result>, SynthesisError> { + match self { + Self::Constant(c) => Ok(UInt8::constant_vec(&to_bytes![c].unwrap())), + Self::Var(v) => v.to_bytes(), + } + } + + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + match self { + Self::Constant(c) => Ok(UInt8::constant_vec(&to_bytes![c].unwrap())), + Self::Var(v) => v.to_non_unique_bytes(), + } + } +} + +impl CondSelectGadget for FpVar { #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: FN, - ) -> Result - where - FN: FnOnce() -> Result, - T: Borrow, - { - let mut value = None; - let variable = cs.alloc_input( - || "alloc", - || { - let tmp = *value_gen()?.borrow(); - value = Some(tmp); - Ok(tmp) - }, - )?; - Ok(FpGadget { - value, - variable: Var(variable), - }) + fn conditionally_select( + cond: &Boolean, + true_value: &Self, + false_value: &Self, + ) -> Result { + match cond { + Boolean::Constant(true) => Ok(true_value.clone()), + Boolean::Constant(false) => Ok(false_value.clone()), + _ => { + match (true_value, false_value) { + (Self::Constant(t), Self::Constant(f)) => { + let is = AllocatedFp::from(cond.clone()); + let not = AllocatedFp::from(cond.not()); + // cond * t + (1 - cond) * f + Ok(is.mul_constant(*t).add(¬.mul_constant(*f)).into()) + } + (_, _) => { + let cs = cond.cs().unwrap(); + let true_value = match true_value { + Self::Constant(f) => AllocatedFp::new_constant(cs.clone(), f)?, + Self::Var(v) => v.clone(), + }; + let false_value = match false_value { + Self::Constant(f) => AllocatedFp::new_constant(cs.clone(), f)?, + Self::Var(v) => v.clone(), + }; + cond.select(&true_value, &false_value).map(Self::Var) + } + } + } + } + } +} + +/// Uses two bits to perform a lookup into a table +/// `b` is little-endian: `b[0]` is LSB. +impl TwoBitLookupGadget for FpVar { + type TableConstant = F; + + fn two_bit_lookup(b: &[Boolean], c: &[Self::TableConstant]) -> Result { + debug_assert_eq!(b.len(), 2); + debug_assert_eq!(c.len(), 4); + if b.cs().is_some() { + AllocatedFp::two_bit_lookup(b, c).map(Self::Var) + } else { + let lsb = b[0].value()? as usize; + let msb = b[1].value()? as usize; + let index = lsb + (msb << 1); + Ok(Self::Constant(c[index])) + } + } +} + +impl ThreeBitCondNegLookupGadget for FpVar { + type TableConstant = F; + + fn three_bit_cond_neg_lookup( + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + debug_assert_eq!(b.len(), 3); + debug_assert_eq!(c.len(), 4); + + if b.cs().or(b0b1.cs()).is_some() { + AllocatedFp::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var) + } else { + let lsb = b[0].value()? as usize; + let msb = b[1].value()? as usize; + let index = lsb + (msb << 1); + let intermediate = c[index]; + + let is_negative = b[2].value()?; + let y = if is_negative { + -intermediate + } else { + intermediate + }; + Ok(Self::Constant(y)) + } + } +} + +impl AllocVar for FpVar { + fn new_variable>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + if mode == AllocationMode::Constant { + Ok(Self::Constant(*f()?.borrow())) + } else { + AllocatedFp::new_variable(cs, f, mode).map(Self::Var) + } } } diff --git a/r1cs-std/src/fields/fp12.rs b/r1cs-std/src/fields/fp12.rs index b2248b0..914553c 100644 --- a/r1cs-std/src/fields/fp12.rs +++ b/r1cs-std/src/fields/fp12.rs @@ -1,918 +1,165 @@ -use r1cs_core::{ConstraintSystem, SynthesisError}; +use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, FieldVar}; +use algebra::fields::{fp12_2over3over2::*, fp6_3over2::Fp6Parameters, Field, QuadExtParameters}; +use r1cs_core::SynthesisError; -use algebra::{ - fields::{ - fp12_2over3over2::{Fp12, Fp12Parameters}, - fp6_3over2::{Fp6, Fp6Parameters}, - Fp2Parameters, - }, - BitIterator, PrimeField, -}; -use core::{borrow::Borrow, marker::PhantomData}; +pub type Fp12Var

= QuadExtVar::Fp6Params>, Fp12ParamsWrapper

>; -use crate::{prelude::*, Vec}; +type Fp2Params

= <

::Fp6Params as Fp6Parameters>::Fp2Params; -type Fp2Gadget = super::fp2::Fp2Gadget< - <

::Fp6Params as Fp6Parameters>::Fp2Params, - ConstraintF, ->; -type Fp6Gadget = - super::fp6_3over2::Fp6Gadget<

::Fp6Params, ConstraintF>; -type Fp6GadgetVariable = as FieldGadget< - Fp6<

::Fp6Params>, - ConstraintF, ->>::Variable; - -#[derive(Derivative)] -#[derivative(Debug(bound = "ConstraintF: PrimeField"))] -#[must_use] -pub struct Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - pub c0: Fp6Gadget, - pub c1: Fp6Gadget, - #[derivative(Debug = "ignore")] - _params: PhantomData

, -} - -impl ToConstraintFieldGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?; - let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?; - - res.append(&mut c0_gadget); - res.append(&mut c1_gadget); - - Ok(res) +impl QuadExtVarParams> for Fp12ParamsWrapper

{ + fn mul_base_field_var_by_frob_coeff(fe: &mut Fp6Var, power: usize) { + fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; } } -impl Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - #[inline] - pub fn new(c0: Fp6Gadget, c1: Fp6Gadget) -> Self { - Self { - c0, - c1, - _params: PhantomData, - } - } - - /// Multiply by quadratic nonresidue v. - #[inline] - pub(crate) fn mul_fp6_by_nonresidue>( - cs: CS, - fe: &Fp6Gadget, - ) -> Result, SynthesisError> { - let new_c0 = Fp6Gadget::::mul_fp2_gadget_by_nonresidue(cs, &fe.c2)?; - let new_c1 = fe.c0.clone(); - let new_c2 = fe.c1.clone(); - Ok(Fp6Gadget::::new(new_c0, new_c1, new_c2)) - } - - #[inline] - pub fn conjugate_in_place>( - &mut self, - cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c1.negate_in_place(cs)?; - Ok(self) - } - +impl Fp12Var

{ /// Multiplies by an element of the form (c0 = (c0, c1, 0), c1 = (0, d1, 0)) #[inline] - pub fn mul_by_014>( + pub fn mul_by_014( &self, - mut cs: CS, - c0: &Fp2Gadget, - c1: &Fp2Gadget, - d1: &Fp2Gadget, + c0: &Fp2Var>, + c1: &Fp2Var>, + d1: &Fp2Var>, ) -> Result { - let v0 = self.c0.mul_by_c0_c1_0(cs.ns(|| "v0"), &c0, &c1)?; - let v1 = self.c1.mul_by_0_c1_0(cs.ns(|| "v1"), &d1)?; - let new_c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)? - .add(cs.ns(|| "v0 + nonresidue * v1"), &v0)?; + let v0 = self.c0.mul_by_c0_c1_0(&c0, &c1)?; + let v1 = self.c1.mul_by_0_c1_0(&d1)?; + let new_c0 = Self::mul_base_field_by_nonresidue(&v1)? + &v0; - let c1 = { - let tmp = c1.add(cs.ns(|| "c1 + d1"), &d1)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - a0_plus_a1 - .mul_by_c0_c1_0(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &c0, &tmp)? - .sub(cs.ns(|| "sub v0"), &v0)? - .sub(cs.ns(|| "sub v1"), &v1)? - }; - Ok(Self::new(new_c0, c1)) + let new_c1 = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &(c1 + d1))? - &v0 - &v1; + Ok(Self::new(new_c0, new_c1)) } /// Multiplies by an element of the form (c0 = (c0, 0, 0), c1 = (d0, d1, 0)) #[inline] - pub fn mul_by_034>( + pub fn mul_by_034( &self, - mut cs: CS, - c0: &Fp2Gadget, - d0: &Fp2Gadget, - d1: &Fp2Gadget, + c0: &Fp2Var>, + d0: &Fp2Var>, + d1: &Fp2Var>, ) -> Result { - let a0 = self.c0.c0.mul(cs.ns(|| "a0"), &c0)?; - let a1 = self.c0.c1.mul(cs.ns(|| "a1"), &c0)?; - let a2 = self.c0.c2.mul(cs.ns(|| "a2"), &c0)?; - let a = Fp6Gadget::::new(a0, a1, a2); - let b = self.c1.mul_by_c0_c1_0(cs.ns(|| "b"), &d0, &d1)?; + let a0 = &self.c0.c0 * c0; + let a1 = &self.c0.c1 * c0; + let a2 = &self.c0.c2 * c0; + let a = Fp6Var::new(a0, a1, a2); + let b = self.c1.mul_by_c0_c1_0(&d0, &d1)?; - let c0 = c0.add(cs.ns(|| "c0 + d0"), &d0)?; + let c0 = c0 + d0; let c1 = d1; - let e = self - .c0 - .add(cs.ns(|| "self.c0 + self.c1"), &self.c1)? - .mul_by_c0_c1_0(cs.ns(|| "compute e"), &c0, &c1)?; - let a_plus_b = a.add(cs.ns(|| "a + b"), &b)?; - let c1 = e.sub(cs.ns(|| "e - (a + b)"), &a_plus_b)?; - let c0 = Self::mul_fp6_by_nonresidue(cs.ns(|| "b *nr"), &b)?.add(cs.ns(|| "plus a"), &a)?; - - Ok(Self::new(c0, c1)) - } - - pub fn cyclotomic_square>( - &self, - mut cs: CS, - ) -> Result { - let mut result = Self::zero(cs.ns(|| "alloc result"))?; - let fp2_nr = ::NONRESIDUE; - - let z0 = &self.c0.c0; - let z4 = &self.c0.c1; - let z3 = &self.c0.c2; - let z2 = &self.c1.c0; - let z1 = &self.c1.c1; - let z5 = &self.c1.c2; - - // t0 + t1*y = (z0 + z1*y)^2 = a^2 - let tmp = z0.mul(cs.ns(|| "first mul"), &z1)?; - let t0 = { - // (z0 + &z1) * &(z0 + &(fp2_nr * &z1)) - &tmp - &(tmp * &fp2_nr); - let mut cs = cs.ns(|| "t0"); - let tmp1 = z0.add(cs.ns(|| "tmp1"), &z1)?; - let tmp2 = z1 - .mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)? - .add(cs.ns(|| "tmp2.2"), &z0)?; - let tmp4 = tmp - .mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)? - .add(cs.ns(|| "tmp4.2"), &tmp)?; - tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)? - .sub(cs.ns(|| "tmp3.2"), &tmp4)? - }; - let t1 = tmp.double(cs.ns(|| "t1"))?; - - // t2 + t3*y = (z2 + z3*y)^2 = b^2 - let tmp = z2.mul(cs.ns(|| "second mul"), &z3)?; - let t2 = { - // (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr); - let mut cs = cs.ns(|| "t2"); - let tmp1 = z2.add(cs.ns(|| "tmp1"), &z3)?; - let tmp2 = z3 - .mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)? - .add(cs.ns(|| "tmp2.2"), &z2)?; - let tmp4 = tmp - .mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)? - .add(cs.ns(|| "tmp4.2"), &tmp)?; - tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)? - .sub(cs.ns(|| "tmp3.2"), &tmp4)? - }; - let t3 = tmp.double(cs.ns(|| "t3"))?; - - // t4 + t5*y = (z4 + z5*y)^2 = c^2 - let tmp = z4.mul(cs.ns(|| "third mul"), &z5)?; - let t4 = { - // (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr); - let mut cs = cs.ns(|| "t4"); - let tmp1 = z4.add(cs.ns(|| "tmp1"), &z5)?; - let tmp2 = z5 - .mul_by_constant(cs.ns(|| "tmp2.1"), &fp2_nr)? - .add(cs.ns(|| "tmp2.2"), &z4)?; - let tmp4 = tmp - .mul_by_constant(cs.ns(|| "tmp4.1"), &fp2_nr)? - .add(cs.ns(|| "tmp4.2"), &tmp)?; - tmp1.mul(cs.ns(|| "tmp3.1"), &tmp2)? - .sub(cs.ns(|| "tmp3.2"), &tmp4)? - }; - let t5 = tmp.double(cs.ns(|| "t5"))?; - - // for A - - // z0 = 3 * t0 - 2 * z0 - result.c0.c0 = { - let mut cs = cs.ns(|| "result.c0.c0"); - t0.sub(cs.ns(|| "1"), &z0)? - .double(cs.ns(|| "2"))? - .add(cs.ns(|| "3"), &t0)? - }; - - // z1 = 3 * t1 + 2 * z1 - result.c1.c1 = { - let mut cs = cs.ns(|| "result.c1.c1"); - t1.add(cs.ns(|| "1"), &z1)? - .double(cs.ns(|| "2"))? - .add(cs.ns(|| "3"), &t1)? - }; - - // for B - - // z2 = 3 * (xi * t5) + 2 * z2 - result.c1.c0 = { - let mut cs = cs.ns(|| "result.c1.c0"); - let tmp = t5.mul_by_constant(cs.ns(|| "1"), &fp2_nr)?; - z2.add(cs.ns(|| "2"), &tmp)? - .double(cs.ns(|| "3"))? - .add(cs.ns(|| "4"), &tmp)? - }; - - // z3 = 3 * t4 - 2 * z3 - result.c0.c2 = { - let mut cs = cs.ns(|| "result.c0.c2"); - t4.sub(cs.ns(|| "1"), &z3)? - .double(cs.ns(|| "2"))? - .add(cs.ns(|| "3"), &t4)? - }; - - // for C - - // z4 = 3 * t2 - 2 * z4 - result.c0.c1 = { - let mut cs = cs.ns(|| "result.c0.c1"); - t2.sub(cs.ns(|| "1"), &z4)? - .double(cs.ns(|| "2"))? - .add(cs.ns(|| "3"), &t2)? - }; - - // z5 = 3 * t3 + 2 * z5 - result.c1.c2 = { - let mut cs = cs.ns(|| "result.c1.c2"); - t3.add(cs.ns(|| "1"), &z5)? - .double(cs.ns(|| "2"))? - .add(cs.ns(|| "3"), &t3)? - }; - - Ok(result) - } - - #[inline] - pub fn cyclotomic_exp, S: AsRef<[u64]>>( - &self, - mut cs: CS, - exp: S, - ) -> Result { - let mut res = Self::one(cs.ns(|| "one"))?; - let mut found_one = false; - for (j, i) in BitIterator::new(exp).enumerate() { - if found_one { - res = res.cyclotomic_square(cs.ns(|| format!("res_square_{:?}", j)))?; - } else { - found_one = i; - } - if i { - res.mul_in_place(cs.ns(|| format!("res_mul2_{:?}", j)), self)?; - } + let e = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &c1)?; + let new_c1 = e - (&a + &b); + let new_c0 = Self::mul_base_field_by_nonresidue(&b)? + &a; + + Ok(Self::new(new_c0, new_c1)) + } + + pub fn cyclotomic_square(&self) -> Result { + if characteristic_square_mod_6_is_one(Fp12::

::characteristic()) { + let fp2_nr = ::NONRESIDUE; + + let z0 = &self.c0.c0; + let z4 = &self.c0.c1; + let z3 = &self.c0.c2; + let z2 = &self.c1.c0; + let z1 = &self.c1.c1; + let z5 = &self.c1.c2; + + // t0 + t1*y = (z0 + z1*y)^2 = a^2 + let tmp = z0 * z1; + let t0 = { + let tmp1 = z0 + z1; + let tmp2 = z1 * fp2_nr + z0; + let tmp4 = &tmp * fp2_nr + &tmp; + tmp1 * tmp2 - tmp4 + }; + let t1 = tmp.double()?; + + // t2 + t3*y = (z2 + z3*y)^2 = b^2 + let tmp = z2 * z3; + let t2 = { + // (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr); + let tmp1 = z2 + z3; + let tmp2 = z3 * fp2_nr + z2; + let tmp4 = &tmp * fp2_nr + &tmp; + tmp1 * tmp2 - tmp4 + }; + let t3 = tmp.double()?; + + // t4 + t5*y = (z4 + z5*y)^2 = c^2 + let tmp = z4 * z5; + let t4 = { + // (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr); + let tmp1 = z4 + z5; + let tmp2 = (z5 * fp2_nr) + z4; + let tmp4 = (&tmp * fp2_nr) + &tmp; + (tmp1 * tmp2) - tmp4 + }; + let t5 = tmp.double()?; + + // for A + + // z0 = 3 * t0 - 2 * z0 + let c0_c0 = (&t0 - z0).double()? + &t0; + + // z1 = 3 * t1 + 2 * z1 + let c1_c1 = (&t1 + z1).double()? + &t1; + + // for B + + // z2 = 3 * (xi * t5) + 2 * z2 + let c1_c0 = { + let tmp = &t5 * fp2_nr; + (z2 + &tmp).double()? + &tmp + }; + + // z3 = 3 * t4 - 2 * z3 + let c0_c2 = (&t4 - z3).double()? + &t4; + + // for C + + // z4 = 3 * t2 - 2 * z4 + let c0_c1 = (&t2 - z4).double()? + &t2; + + // z5 = 3 * t3 + 2 * z5 + let c1_c2 = (&t3 + z5).double()? + &t3; + let c0 = Fp6Var::new(c0_c0, c0_c1, c0_c2); + let c1 = Fp6Var::new(c1_c0, c1_c1, c1_c2); + + Ok(Self::new(c0, c1)) + } else { + self.square() } - Ok(res) - } -} - -impl FieldGadget, ConstraintF> for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - type Variable = ( - Fp6GadgetVariable, - Fp6GadgetVariable, - ); - - #[inline] - fn get_value(&self) -> Option> { - Some(Fp12::new(self.c0.get_value()?, self.c1.get_value()?)) - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.c0.get_variable(), self.c1.get_variable()) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - let c0 = Fp6Gadget::::zero(cs.ns(|| "c0"))?; - let c1 = Fp6Gadget::::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn one>(mut cs: CS) -> Result { - let c0 = Fp6Gadget::::one(cs.ns(|| "c0"))?; - let c1 = Fp6Gadget::::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn conditionally_add_constant>( - &self, - mut cs: CS, - bit: &Boolean, - coeff: Fp12

, - ) -> Result { - let c0 = self - .c0 - .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; - let c1 = self - .c1 - .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.add(cs.ns(|| "c0"), &other.c0)?; - let c1 = self.c1.add(cs.ns(|| "c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn add_in_place>( - &mut self, - mut cs: CS, - other: &Self, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_in_place(cs.ns(|| "c1"), &other.c1)?; - Ok(self) - } - - #[inline] - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.sub(cs.ns(|| "c0"), &other.c0)?; - let c1 = self.c1.sub(cs.ns(|| "c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn sub_in_place>( - &mut self, - mut cs: CS, - other: &Self, - ) -> Result<&mut Self, SynthesisError> { - self.c0.sub_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.sub_in_place(cs.ns(|| "c1"), &other.c1)?; - Ok(self) - } - - #[inline] - fn negate>( - &self, - mut cs: CS, - ) -> Result { - let c0 = self.c0.negate(cs.ns(|| "c0"))?; - let c1 = self.c1.negate(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn negate_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.negate_in_place(cs.ns(|| "c0"))?; - self.c1.negate_in_place(cs.ns(|| "c1"))?; - Ok(self) - } - - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Karatsuba multiplication: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // - // Enforced with 3 Fp3_mul_gadget's that ensure that: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = v0 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + v0 + v1 - - let v0 = self.c0.mul(cs.ns(|| "v0"), &other.c0)?; - let v1 = self.c1.mul(cs.ns(|| "v1"), &other.c1)?; - let c0 = { - let non_residue_times_v1 = - Self::mul_fp6_by_nonresidue(cs.ns(|| "first mul_by_nr"), &v1)?; - v0.add(cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)? - }; - let c1 = { - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?; - let a0_plus_a1_times_b0_plus_b1 = - a0_plus_a1.mul(&mut cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?; - a0_plus_a1_times_b0_plus_b1 - .sub(cs.ns(|| "res - v0"), &v0)? - .sub(cs.ns(|| "res - v0 - v1"), &v1)? - }; - - Ok(Self::new(c0, c1)) - } - - fn square>( - &self, - mut cs: CS, - ) -> Result { - // From Libsnark/fp2_gadget.tcc - // Complex multiplication for Fp2: - // v0 = A.c0 * A.c1 - // result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 + - // non_residue) * v0 result.c1 = 2 * v0 - // Enforced with 2 constraints: - // (2*A.c0) * A.c1 = result.c1 - // (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1 - // + non_residue)/2 Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - - let non_residue_a1 = Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?; - let a0_plus_non_residue_a1 = self - .c0 - .add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_a1)?; - let one_plus_non_residue_v0 = - Self::mul_fp6_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)? - .add(cs.ns(|| "plus v0"), &v0)?; - - let c0 = a0_plus_a1 - .mul( - cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"), - &a0_plus_non_residue_a1, - )? - .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?; - - v0.double_in_place(cs.ns(|| "2v0"))?; - let c1 = v0; - - Ok(Self { - c0, - c1, - _params: PhantomData, - }) - } - - #[inline] - fn add_constant>( - &self, - mut cs: CS, - other: &Fp12

, - ) -> Result { - let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?; - let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?; - - Ok(Self::new(c0, c1)) - } - - #[inline] - fn add_constant_in_place>( - &mut self, - mut cs: CS, - other: &Fp12

, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; - Ok(self) - } - - fn mul_by_constant>( - &self, - mut cs: CS, - other: &Fp12

, - ) -> Result { - // Karatsuba multiplication (see mul above). - // Doesn't need any constraints; returns linear combinations of - // `self`'s variables. - // - // (The operations below are guaranteed to return linear combinations) - let (a0, a1) = (&self.c0, &self.c1); - let (b0, b1) = (other.c0, other.c1); - let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?; - let mut v1 = Self::mul_fp6_by_nonresidue(&mut cs.ns(|| "v1"), a1)?; - let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?; - - v0.add_in_place(&mut cs.ns(|| "c0"), beta_v1)?; - let c0 = v0; - - let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?; - let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?; - a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?; - let c1 = a0b1; - Ok(Self::new(c0, c1)) - } - - fn frobenius_map>( - &self, - cs: CS, - power: usize, - ) -> Result { - let mut res = self.clone(); - res.frobenius_map_in_place(cs, power)?; - Ok(res) - } - - fn frobenius_map_in_place>( - &mut self, - mut cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - self.c0 - .frobenius_map_in_place(cs.ns(|| "frob_map1"), power)?; - self.c1 - .frobenius_map_in_place(cs.ns(|| "frob_map2"), power)?; - - self.c1 - .c0 - .mul_by_constant_in_place(cs.ns(|| "mul1"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?; - self.c1 - .c1 - .mul_by_constant_in_place(cs.ns(|| "mul2"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?; - self.c1 - .c2 - .mul_by_constant_in_place(cs.ns(|| "mul3"), &P::FROBENIUS_COEFF_FP12_C1[power % 12])?; - Ok(self) - } - - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - // Karatsuba multiplication for Fp2: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - // Compute v1 - let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - - // Perform second check - let non_residue_times_v1 = Self::mul_fp6_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?; - let rhs = result - .c0 - .sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?; - self.c0 - .mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?; - - // Last check - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let one_minus_non_residue_v1 = - v1.sub(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?; - - let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result - .c1 - .add(mul_cs.ns(|| "c1 + c0"), &result.c0)? - .add(mul_cs.ns(|| "rest of stuff"), &one_minus_non_residue_v1)?; - - a0_plus_a1.mul_equals( - mul_cs.ns(|| "third check"), - &b0_plus_b1, - &result_c1_plus_result_c0_plus_one_minus_non_residue_v1, - )?; - - Ok(()) - } - - fn cost_of_mul() -> usize { - 3 * Fp6Gadget::::cost_of_mul() - } - - fn cost_of_mul_equals() -> usize { - Self::cost_of_mul() - } -} - -impl PartialEq for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - fn eq(&self, other: &Self) -> bool { - self.c0 == other.c0 && self.c1 == other.c1 - } -} - -impl Eq for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ -} - -impl EqGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ -} - -impl ConditionalEqGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.c0 - .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; - self.c1 - .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; - Ok(()) - } - - fn cost() -> usize { - 2 * as ConditionalEqGadget>::cost() - } -} - -impl NEqGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - #[inline] - fn enforce_not_equal>( - &self, - mut cs: CS, - other: &Self, - ) -> Result<(), SynthesisError> { - self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; - self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; - Ok(()) - } - - fn cost() -> usize { - 2 * as NEqGadget>::cost() - } -} - -impl ToBitsGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - fn to_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) } -} -impl ToBytesGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - fn to_bytes>( + /// Like `Self::cyclotomic_exp, but additionally uses cyclotomic squaring. + pub fn optimized_cyclotomic_exp( &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl Clone for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - fn clone(&self) -> Self { - Self::new(self.c0.clone(), self.c1.clone()) - } -} - -impl CondSelectGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - true_value: &Self, - false_value: &Self, + exponent: impl AsRef<[u64]>, ) -> Result { - let c0 = Fp6Gadget::::conditionally_select( - &mut cs.ns(|| "c0"), - cond, - &true_value.c0, - &false_value.c0, - )?; - let c1 = Fp6Gadget::::conditionally_select( - &mut cs.ns(|| "c1"), - cond, - &true_value.c1, - &false_value.c1, - )?; + use algebra::biginteger::arithmetic::find_wnaf; + let mut res = Self::one(); + let self_inverse = self.unitary_inverse()?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as CondSelectGadget>::cost() - } -} - -impl TwoBitLookupGadget for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - type TableConstant = Fp12

; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = Fp6Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; - let c1 = Fp6Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; - Ok(Self::new(c0, c1)) - } + let mut found_nonzero = false; + let naf = find_wnaf(exponent.as_ref()); - fn cost() -> usize { - 2 * as TwoBitLookupGadget>::cost() - } -} - -impl ThreeBitCondNegLookupGadget - for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - type TableConstant = Fp12

; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = Fp6Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c0"), - b, - b0b1, - &c0s, - )?; - let c1 = Fp6Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c1"), - b, - b0b1, - &c1s, - )?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as ThreeBitCondNegLookupGadget>::cost() - } -} - -impl AllocGadget, ConstraintF> for Fp12Gadget -where - P: Fp12Parameters, - ::Fp2Params: Fp2Parameters, -{ - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow()) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) + for &value in naf.iter().rev() { + if found_nonzero { + res = res.cyclotomic_square()?; } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - let c0 = Fp6Gadget::::alloc(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp6Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) - } + if value != 0 { + found_nonzero = true; - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) + if value > 0 { + res *= self; + } else { + res *= &self_inverse; + } } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; + } - let c0 = Fp6Gadget::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp6Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) + Ok(res) } } diff --git a/r1cs-std/src/fields/fp2.rs b/r1cs-std/src/fields/fp2.rs index ee3b3ff..3bf8e45 100644 --- a/r1cs-std/src/fields/fp2.rs +++ b/r1cs-std/src/fields/fp2.rs @@ -1,693 +1,10 @@ -use algebra::{ - fields::{Fp2, Fp2Parameters}, - PrimeField, -}; -use core::{borrow::Borrow, marker::PhantomData}; -use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError}; +use crate::fields::{fp::FpVar, quadratic_extension::*}; +use algebra::fields::{Fp2Parameters, Fp2ParamsWrapper, QuadExtParameters}; -use crate::{fields::fp::FpGadget, prelude::*, Vec}; +pub type Fp2Var

= QuadExtVar::Fp>, Fp2ParamsWrapper

>; -#[derive(Derivative)] -#[derivative(Debug(bound = "P: Fp2Parameters, ConstraintF: PrimeField"))] -#[must_use] -pub struct Fp2Gadget, ConstraintF: PrimeField> { - pub c0: FpGadget, - pub c1: FpGadget, - #[derivative(Debug = "ignore")] - _params: PhantomData

, -} - -impl, ConstraintF: PrimeField> - ToConstraintFieldGadget for Fp2Gadget -{ - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?; - let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?; - - res.append(&mut c0_gadget); - res.append(&mut c1_gadget); - - Ok(res) - } -} - -impl, ConstraintF: PrimeField> Fp2Gadget { - pub fn new(c0: FpGadget, c1: FpGadget) -> Self { - Self { - c0, - c1, - _params: PhantomData, - } - } - - /// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE. - #[inline] - pub fn mul_fp_gadget_by_nonresidue>( - cs: CS, - fe: &FpGadget, - ) -> Result, SynthesisError> { - fe.mul_by_constant(cs, &P::NONRESIDUE) - } - - /// Multiply a Fp2Gadget by an element of fp. - #[inline] - pub fn mul_by_fp_constant_in_place>( - &mut self, - mut cs: CS, - fe: &P::Fp, - ) -> Result<&mut Self, SynthesisError> { - self.c0.mul_by_constant_in_place(cs.ns(|| "c0"), fe)?; - self.c1.mul_by_constant_in_place(cs.ns(|| "c1"), fe)?; - Ok(self) - } - - /// Multiply a Fp2Gadget by an element of fp. - #[inline] - pub fn mul_by_fp_constant>( - &self, - cs: CS, - fe: &P::Fp, - ) -> Result { - let mut result = self.clone(); - result.mul_by_fp_constant_in_place(cs, fe)?; - Ok(result) - } -} - -impl, ConstraintF: PrimeField> FieldGadget, ConstraintF> - for Fp2Gadget -{ - type Variable = (ConstraintVar, ConstraintVar); - - #[inline] - fn get_value(&self) -> Option> { - match (self.c0.value, self.c1.value) { - (Some(c0), Some(c1)) => Some(Fp2::new(c0, c1)), - (..) => None, - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.c0.get_variable(), self.c1.get_variable()) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - let c0 = FpGadget::zero(cs.ns(|| "c0"))?; - let c1 = FpGadget::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn one>(mut cs: CS) -> Result { - let c0 = FpGadget::one(cs.ns(|| "c0"))?; - let c1 = FpGadget::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn conditionally_add_constant>( - &self, - mut cs: CS, - bit: &Boolean, - coeff: Fp2

, - ) -> Result { - let c0 = self - .c0 - .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; - let c1 = self - .c1 - .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?; - let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?; - let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn double>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.double_in_place(cs)?; - Ok(result) - } - - #[inline] - fn double_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.double_in_place(&mut cs.ns(|| "double c0"))?; - self.c1.double_in_place(&mut cs.ns(|| "double c1"))?; - Ok(self) - } - - #[inline] - fn negate>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.negate_in_place(cs)?; - Ok(result) - } - - #[inline] - fn negate_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?; - self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?; - Ok(self) - } - - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Karatsuba multiplication for Fp2: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - let v0 = self.c0.mul(mul_cs.ns(|| "v0"), &other.c0)?; - let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - let c0 = { - let non_residue_times_v1 = - v1.mul_by_constant(mul_cs.ns(|| "non_residue * v0"), &P::NONRESIDUE)?; - v0.add(mul_cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)? - }; - let c1 = { - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let a0_plus_a1_times_b0_plus_b1 = - a0_plus_a1.mul(&mut mul_cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?; - a0_plus_a1_times_b0_plus_b1 - .sub(mul_cs.ns(|| "res - v0"), &v0)? - .sub(mul_cs.ns(|| "res - v0 - v1"), &v1)? - }; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn square>( - &self, - mut cs: CS, - ) -> Result { - // From Libsnark/fp2_gadget.tcc - // Complex multiplication for Fp2: - // v0 = A.c0 * A.c1 - // result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 + - // non_residue) * v0 result.c1 = 2 * v0 - // Enforced with 2 constraints: - // (2*A.c0) * A.c1 = result.c1 - // (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1 - // + non_residue)/2 Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - - let non_residue_c1 = self - .c1 - .mul_by_constant(cs.ns(|| "non_residue * a1"), &P::NONRESIDUE)?; - let a0_plus_non_residue_c1 = self - .c0 - .add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_c1)?; - let one_plus_non_residue_v0 = v0.mul_by_constant( - cs.ns(|| "1 + non_residue * v0"), - &(P::Fp::one() + &P::NONRESIDUE), - )?; - - let c0 = a0_plus_a1 - .mul( - cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"), - &a0_plus_non_residue_c1, - )? - .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?; - - v0.double_in_place(cs.ns(|| "2v0"))?; - let c1 = v0; - - Ok(Self::new(c0, c1)) - } - - #[inline] - fn square_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - // From Libsnark/fp2_gadget.tcc - // Complex multiplication for Fp2: - // v0 = A.c0 * A.c1 - // result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 + - // non_residue) * v0 result.c1 = 2 * v0 - // Enforced with 2 constraints: - // (2*A.c0) * A.c1 = result.c1 - // (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1 - // + non_residue)/2 Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - - let _ = self - .c1 - .mul_by_constant_in_place(cs.ns(|| "non_residue * a1"), &P::NONRESIDUE)?; - let a0_plus_non_residue_c1 = self.c0.add(cs.ns(|| "a0 + non_residue * a1"), &self.c1)?; - let one_plus_non_residue_v0 = v0.mul_by_constant( - cs.ns(|| "1 + non_residue * v0"), - &(P::Fp::one() + &P::NONRESIDUE), - )?; - - self.c0 = a0_plus_a1 - .mul( - cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"), - &a0_plus_non_residue_c1, - )? - .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?; - - v0.double_in_place(cs.ns(|| "2v0"))?; - self.c1 = v0; - - Ok(self) - } - - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - // Karatsuba multiplication for Fp2: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - // Compute v1 - let mut v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - - // Perform second check - let non_residue_times_v1 = - v1.mul_by_constant(mul_cs.ns(|| "non_residue * v0"), &P::NONRESIDUE)?; - let rhs = result - .c0 - .sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?; - self.c0 - .mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?; - - // Last check - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let one_minus_non_residue_v1 = - v1.sub_in_place(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?; - - let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result - .c1 - .add(mul_cs.ns(|| "c1 + c0"), &result.c0)? - .add(mul_cs.ns(|| "rest of stuff"), one_minus_non_residue_v1)?; - - a0_plus_a1.mul_equals( - mul_cs.ns(|| "third check"), - &b0_plus_b1, - &result_c1_plus_result_c0_plus_one_minus_non_residue_v1, - )?; - - Ok(()) - } - - fn frobenius_map>( - &self, - cs: CS, - power: usize, - ) -> Result { - let mut result = self.clone(); - let _ = result.frobenius_map_in_place(cs, power)?; - Ok(result) - } - - fn frobenius_map_in_place>( - &mut self, - cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - self.c1 - .mul_by_constant_in_place(cs, &P::FROBENIUS_COEFF_FP2_C1[power % 2])?; - Ok(self) - } - - #[inline] - fn add_constant>( - &self, - cs: CS, - other: &Fp2

, - ) -> Result { - let mut result = self.clone(); - let _ = result.add_constant_in_place(cs, other)?; - Ok(result) - } - - #[inline] - fn add_constant_in_place>( - &mut self, - mut cs: CS, - other: &Fp2

, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; - Ok(self) - } - - fn mul_by_constant>( - &self, - mut cs: CS, - fe: &Fp2

, - ) -> Result { - // Karatsuba multiplication (see mul above). - // Doesn't need any constraints; returns linear combinations of - // `self`'s variables. - // - // (The operations below are guaranteed to return linear combinations) - let (a0, a1) = (&self.c0, &self.c1); - let (b0, b1) = (fe.c0, fe.c1); - let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?; - let beta_v1 = a1.mul_by_constant(&mut cs.ns(|| "v1"), &(b1 * &P::NONRESIDUE))?; - - v0.add_in_place(&mut cs.ns(|| "c0"), &beta_v1)?; - let c0 = v0; - - let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?; - let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?; - a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?; - let c1 = a0b1; - Ok(Self::new(c0, c1)) - } - - fn cost_of_mul() -> usize { - 3 * FpGadget::::cost_of_mul() - } - - fn cost_of_mul_equals() -> usize { - Self::cost_of_mul() - } -} - -impl, ConstraintF: PrimeField> PartialEq - for Fp2Gadget -{ - fn eq(&self, other: &Self) -> bool { - self.c0 == other.c0 && self.c1 == other.c1 - } -} - -impl, ConstraintF: PrimeField> Eq for Fp2Gadget {} - -impl, ConstraintF: PrimeField> EqGadget - for Fp2Gadget -{ -} - -impl, ConstraintF: PrimeField> ConditionalEqGadget - for Fp2Gadget -{ - #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.c0 - .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; - self.c1 - .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; - Ok(()) - } - - fn cost() -> usize { - 2 - } -} - -impl, ConstraintF: PrimeField> NEqGadget - for Fp2Gadget -{ - #[inline] - fn enforce_not_equal>( - &self, - mut cs: CS, - other: &Self, - ) -> Result<(), SynthesisError> { - self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; - self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; - Ok(()) - } - - fn cost() -> usize { - 2 - } -} - -impl, ConstraintF: PrimeField> ToBitsGadget - for Fp2Gadget -{ - fn to_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl, ConstraintF: PrimeField> ToBytesGadget - for Fp2Gadget -{ - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl, ConstraintF: PrimeField> Clone - for Fp2Gadget -{ - fn clone(&self) -> Self { - Self { - c0: self.c0.clone(), - c1: self.c1.clone(), - _params: PhantomData, - } - } -} - -impl, ConstraintF: PrimeField> CondSelectGadget - for Fp2Gadget -{ - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - true_value: &Self, - false_value: &Self, - ) -> Result { - let c0 = FpGadget::::conditionally_select( - &mut cs.ns(|| "c0"), - cond, - &true_value.c0, - &false_value.c0, - )?; - let c1 = FpGadget::::conditionally_select( - &mut cs.ns(|| "c1"), - cond, - &true_value.c1, - &false_value.c1, - )?; - - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 - } -} - -impl, ConstraintF: PrimeField> TwoBitLookupGadget - for Fp2Gadget -{ - type TableConstant = Fp2

; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = FpGadget::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; - let c1 = FpGadget::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as TwoBitLookupGadget>::cost() - } -} - -impl, ConstraintF: PrimeField> - ThreeBitCondNegLookupGadget for Fp2Gadget -{ - type TableConstant = Fp2

; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = FpGadget::three_bit_cond_neg_lookup(cs.ns(|| "Lookup c0"), b, b0b1, &c0s)?; - let c1 = FpGadget::three_bit_cond_neg_lookup(cs.ns(|| "Lookup c1"), b, b0b1, &c1s)?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as ThreeBitCondNegLookupGadget>::cost() - } -} - -impl, ConstraintF: PrimeField> AllocGadget, ConstraintF> - for Fp2Gadget -{ - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow()) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) - } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = FpGadget::alloc(&mut cs.ns(|| "c0"), || c0)?; - let c1 = FpGadget::alloc(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) - } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = FpGadget::alloc_input(&mut cs.ns(|| "c0"), || c0)?; - let c1 = FpGadget::alloc_input(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) +impl QuadExtVarParams> for Fp2ParamsWrapper

{ + fn mul_base_field_var_by_frob_coeff(fe: &mut FpVar, power: usize) { + *fe *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; } } diff --git a/r1cs-std/src/fields/fp3.rs b/r1cs-std/src/fields/fp3.rs index 2c852c7..24f14cc 100644 --- a/r1cs-std/src/fields/fp3.rs +++ b/r1cs-std/src/fields/fp3.rs @@ -1,955 +1,15 @@ -use algebra::{ - fields::fp3::{Fp3, Fp3Parameters}, - PrimeField, SquareRootField, -}; -use core::{borrow::Borrow, marker::PhantomData}; -use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError}; +use crate::fields::{cubic_extension::*, fp::FpVar}; +use algebra::fields::{CubicExtParameters, Fp3Parameters, Fp3ParamsWrapper}; -use crate::{fields::fp::FpGadget, prelude::*, Vec}; +pub type Fp3Var

= CubicExtVar::Fp>, Fp3ParamsWrapper

>; -#[derive(Derivative)] -#[derivative(Debug( - bound = "P: Fp3Parameters, ConstraintF: PrimeField + SquareRootField" -))] -#[must_use] -pub struct Fp3Gadget, ConstraintF: PrimeField + SquareRootField> -{ - pub c0: FpGadget, - pub c1: FpGadget, - pub c2: FpGadget, - #[derivative(Debug = "ignore")] - _params: PhantomData

, -} - -impl, ConstraintF: PrimeField + SquareRootField> - ToConstraintFieldGadget for Fp3Gadget -{ - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?; - let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?; - let mut c2_gadget = self.c2.to_constraint_field(&mut cs.ns(|| "c2"))?; - - res.append(&mut c0_gadget); - res.append(&mut c1_gadget); - res.append(&mut c2_gadget); - - Ok(res) - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - Fp3Gadget -{ - #[inline] - pub fn new( - c0: FpGadget, - c1: FpGadget, - c2: FpGadget, - ) -> Self { - Self { - c0, - c1, - c2, - _params: PhantomData, - } - } - - /// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE. - #[inline] - pub fn mul_fp_gadget_by_nonresidue>( - cs: CS, - fe: &FpGadget, - ) -> Result, SynthesisError> { - fe.mul_by_constant(cs, &P::NONRESIDUE) - } - - /// Multiply a Fp3Gadget by an element of fp. - #[inline] - pub fn mul_by_fp_constant_in_place>( - &mut self, - mut cs: CS, - fe: &P::Fp, - ) -> Result<&mut Self, SynthesisError> { - self.c0.mul_by_constant_in_place(cs.ns(|| "c0"), fe)?; - self.c1.mul_by_constant_in_place(cs.ns(|| "c1"), fe)?; - self.c2.mul_by_constant_in_place(cs.ns(|| "c2"), fe)?; - Ok(self) - } - - /// Multiply a Fp3Gadget by an element of fp. - #[inline] - pub fn mul_by_fp_constant>( - &self, - cs: CS, - fe: &P::Fp, - ) -> Result { - let mut result = self.clone(); - result.mul_by_fp_constant_in_place(cs, fe)?; - Ok(result) - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - FieldGadget, ConstraintF> for Fp3Gadget -{ - type Variable = ( - ConstraintVar, - ConstraintVar, - ConstraintVar, - ); - - #[inline] - fn get_value(&self) -> Option> { - match ( - self.c0.get_value(), - self.c1.get_value(), - self.c2.get_value(), - ) { - (Some(c0), Some(c1), Some(c2)) => Some(Fp3::new(c0, c1, c2)), - (..) => None, - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - ( - self.c0.get_variable(), - self.c1.get_variable(), - self.c2.get_variable(), - ) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - let c0 = FpGadget::::zero(cs.ns(|| "c0"))?; - let c1 = FpGadget::::zero(cs.ns(|| "c1"))?; - let c2 = FpGadget::::zero(cs.ns(|| "c2"))?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn one>(mut cs: CS) -> Result { - let c0 = FpGadget::::one(cs.ns(|| "c0"))?; - let c1 = FpGadget::::zero(cs.ns(|| "c1"))?; - let c2 = FpGadget::::zero(cs.ns(|| "c2"))?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn conditionally_add_constant>( - &self, - mut cs: CS, - bit: &Boolean, - coeff: Fp3

, - ) -> Result { - let c0 = self - .c0 - .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; - let c1 = self - .c1 - .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; - let c2 = self - .c2 - .conditionally_add_constant(cs.ns(|| "c2"), bit, coeff.c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?; - let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?; - let c2 = self.c2.add(&mut cs.ns(|| "add c2"), &other.c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?; - let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?; - let c2 = self.c2.sub(&mut cs.ns(|| "sub c2"), &other.c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn negate>( - &self, - mut cs: CS, - ) -> Result { - let c0 = self.c0.negate(&mut cs.ns(|| "negate c0"))?; - let c1 = self.c1.negate(&mut cs.ns(|| "negate c1"))?; - let c2 = self.c2.negate(&mut cs.ns(|| "negate c2"))?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn negate_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?; - self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?; - self.c2.negate_in_place(&mut cs.ns(|| "negate c2"))?; - Ok(self) - } - - /// Use the Toom-Cook-3x method to compute multiplication. - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Uses Toom-Cook-3x multiplication from - // - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - // v0 = a(0)b(0) = a0 * b0 - let v0 = self.c0.mul(&mut cs.ns(|| "Calc v0"), &other.c0)?; - - // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2) - let v1 = { - let mut v1_cs = cs.ns(|| "compute v1"); - let a0_plus_a1_plus_a2 = self - .c0 - .add(v1_cs.ns(|| "a0 + a1"), &self.c1)? - .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?; - let b0_plus_b1_plus_b2 = other - .c0 - .add(v1_cs.ns(|| "b0 + b1"), &other.c1)? - .add(v1_cs.ns(|| "b0 + b1 + b2"), &other.c2)?; - - a0_plus_a1_plus_a2.mul( - v1_cs.ns(|| "(a0 + a1 + a2)(b0 + b1 + b2)"), - &b0_plus_b1_plus_b2, - )? - }; - - // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2) - let v2 = { - let mut v2_cs = cs.ns(|| "compute v2"); - - let a0_minus_a1_plus_a2 = self - .c0 - .sub(v2_cs.ns(|| "a0 - a1"), &self.c1)? - .add(v2_cs.ns(|| "a0 - a1 + a2"), &self.c2)?; - - let b0_minus_b1_plus_b2 = other - .c0 - .sub(v2_cs.ns(|| "b0 - b1"), &other.c1)? - .add(v2_cs.ns(|| "b0 - b1 + b2"), &other.c2)?; - - a0_minus_a1_plus_a2.mul( - v2_cs.ns(|| "(a0 - a1 + a2)(b0 - b1 + b2)"), - &b0_minus_b1_plus_b2, - )? - }; - - // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2) - let v3 = { - let v3_cs = &mut cs.ns(|| "compute v3"); - - let a1_double = self.c1.double(v3_cs.ns(|| "2 * a1"))?; - let a2_quad = self - .c2 - .double(v3_cs.ns(|| "2 * a2"))? - .double(v3_cs.ns(|| "4 * a2"))?; - - let a0_plus_2_a1_plus_4_a2 = self - .c0 - .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)? - .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; - - let b1_double = other.c1.double(v3_cs.ns(|| "2 * b1"))?; - let b2_quad = other - .c2 - .double(v3_cs.ns(|| "2 * b2"))? - .double(v3_cs.ns(|| "4 * b2"))?; - let b0_plus_2_b1_plus_4_b2 = other - .c0 - .add(v3_cs.ns(|| "b0 + 2b1"), &b1_double)? - .add(v3_cs.ns(|| "b0 + 2b1 + 4b2"), &b2_quad)?; - - a0_plus_2_a1_plus_4_a2.mul( - v3_cs.ns(|| "(a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)"), - &b0_plus_2_b1_plus_4_b2, - )? - }; - - // v4 = a(∞)b(∞) = a2 * b2 - let v4 = self.c2.mul(cs.ns(|| "v2: a2 * b2"), &other.c2)?; - - let two = P::Fp::one().double(); - let six = two.double() + &two; - let mut two_and_six = [two, six]; - algebra::fields::batch_inversion(&mut two_and_six); - let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); - - let half_v0 = v0.mul_by_constant(cs.ns(|| "half_v0"), &two_inverse)?; - let half_v1 = v1.mul_by_constant(cs.ns(|| "half_v1"), &two_inverse)?; - let one_sixth_v2 = v2.mul_by_constant(cs.ns(|| "v2_by_six"), &six_inverse)?; - let one_sixth_v3 = v3.mul_by_constant(cs.ns(|| "v3_by_six"), &six_inverse)?; - let two_v4 = v4.double(cs.ns(|| "2 * v4"))?; - - // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) - let c0 = { - let c0_cs = &mut cs.ns(|| "c0"); - - // No constraints, only get a linear combination back. - let temp = half_v0 - .sub(c0_cs.ns(|| "sub1"), &half_v1)? - .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? - .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? - .sub(c0_cs.ns(|| "sub4"), &two_v4)?; - let non_residue_times_inner = - temp.mul_by_constant(&mut c0_cs.ns(|| "mul5"), &P::NONRESIDUE)?; - v0.add(c0_cs.ns(|| "add6"), &non_residue_times_inner)? - }; - - // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 - let c1 = { - let c1_cs = &mut cs.ns(|| "c1"); - let one_third_v2 = one_sixth_v2.double(&mut c1_cs.ns(|| "v2_by_3"))?; - let non_residue_v4 = - v4.mul_by_constant(&mut c1_cs.ns(|| "mul_by_beta"), &P::NONRESIDUE)?; - - let result = half_v0 - .negate(c1_cs.ns(|| "neg1"))? - .add(c1_cs.ns(|| "add2"), &v1)? - .sub(c1_cs.ns(|| "sub3"), &one_third_v2)? - .sub(c1_cs.ns(|| "sub4"), &one_sixth_v3)? - .add(c1_cs.ns(|| "sub5"), &two_v4)? - .add(c1_cs.ns(|| "sub6"), &non_residue_v4)?; - result - }; - - // -v0 + (1/2)v1 + (1/2)v2 −v4 - let c2 = { - let c2_cs = &mut cs.ns(|| "c2"); - let half_v2 = v2.mul_by_constant(&mut c2_cs.ns(|| "mul1"), &two_inverse)?; - let result = half_v1 - .add(c2_cs.ns(|| "add1"), &half_v2)? - .sub(c2_cs.ns(|| "sub1"), &v4)? - .sub(c2_cs.ns(|| "sub2"), &v0)?; - result - }; - - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - // Karatsuba multiplication for Fp3: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // v2 = A.c2 * B.c2 - // result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2) - // result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2 - // result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2, - // We enforce this with six constraints: - // - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // v2 = A.c2 * B.c2 - // - // result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2)) - // result.c1 + v0 + v1 - βv2 = (a0 + a1)(b0 + b1) - // result.c2 + v0 - v1 + v2 = (a0 + a2)(b0 + b2) - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - // - // This implementation adapted from - // https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs - let v0 = self.c0.mul(cs.ns(|| "v0 = a0 * b0"), &other.c0)?; - let v1 = self.c1.mul(cs.ns(|| "v1 = a1 * b1"), &other.c1)?; - let v2 = self.c2.mul(cs.ns(|| "v2 = a2 * b2"), &other.c2)?; - - // Check c0 - let nr_a1_plus_a2 = self - .c1 - .add(cs.ns(|| "a1 + a2"), &self.c2)? - .mul_by_constant(cs.ns(|| "nr*(a1 + a2)"), &P::NONRESIDUE)?; - let b1_plus_b2 = other.c1.add(cs.ns(|| "b1 + b2"), &other.c2)?; - let nr_v1 = v1.mul_by_constant(cs.ns(|| "nr * v1"), &P::NONRESIDUE)?; - let nr_v2 = v2.mul_by_constant(cs.ns(|| "nr * v2"), &P::NONRESIDUE)?; - let to_check = result - .c0 - .sub(cs.ns(|| "c0 - v0"), &v0)? - .add(cs.ns(|| "c0 - v0 + nr * v1"), &nr_v1)? - .add(cs.ns(|| "c0 - v0 + nr * v1 + nr * v2"), &nr_v2)?; - nr_a1_plus_a2.mul_equals(cs.ns(|| "check c0"), &b1_plus_b2, &to_check)?; - - // Check c1 - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?; - let to_check = result - .c1 - .sub(cs.ns(|| "c1 - nr * v2"), &nr_v2)? - .add(cs.ns(|| "c1 - nr * v2 + v0"), &v0)? - .add(cs.ns(|| "c1 - nr * v2 + v0 + v1"), &v1)?; - a0_plus_a1.mul_equals(cs.ns(|| "check c1"), &b0_plus_b1, &to_check)?; - - // Check c2 - let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?; - let b0_plus_b2 = other.c0.add(cs.ns(|| "b0 + b2"), &other.c2)?; - let to_check = result - .c2 - .add(cs.ns(|| "c2 + v0"), &v0)? - .sub(cs.ns(|| "c2 + v0 - v1"), &v1)? - .add(cs.ns(|| "c2 + v0 - v1 + v2"), &v2)?; - a0_plus_a2.mul_equals(cs.ns(|| "check c2"), &b0_plus_b2, &to_check)?; - Ok(()) - } - - /// Use the Chung-Hasan asymmetric squaring formula. - /// - /// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on - /// Abstract Pairing-Friendly - /// Fields.pdf; Section 4 (CH-SQR2)) - #[inline] - fn square>( - &self, - mut cs: CS, - ) -> Result { - let a = self.c0.clone(); - let b = self.c1.clone(); - let c = self.c2.clone(); - - let s0 = a.square(cs.ns(|| "s0"))?; - let ab = a.mul(cs.ns(|| "ab"), &b)?; - let s1 = ab.double(cs.ns(|| "s1"))?; - let s2 = a - .sub(cs.ns(|| "a-b"), &b)? - .add(cs.ns(|| "plus c"), &c)? - .square(cs.ns(|| "s2"))?; - let s3 = b.mul(cs.ns(|| "bc"), &c)?.double(cs.ns(|| "s3"))?; - let s4 = c.square(cs.ns(|| "s4"))?; - - let c0 = Self::mul_fp_gadget_by_nonresidue(cs.ns(|| "c0 part 1"), &s3)? - .add(cs.ns(|| "c0"), &s0)?; - - let c1 = Self::mul_fp_gadget_by_nonresidue(cs.ns(|| "c1 part 1"), &s4)? - .add(cs.ns(|| "c1"), &s1)?; - - let c2 = s1 - .add(cs.ns(|| "c2 part1"), &s2)? - .add(cs.ns(|| "c2 part2"), &s3)? - .sub(cs.ns(|| "c2 part3"), &s0)? - .sub(cs.ns(|| "c2 part4"), &s4)?; - - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn add_constant>( - &self, - mut cs: CS, - other: &Fp3

, - ) -> Result { - let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?; - let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?; - let c2 = self.c2.add_constant(cs.ns(|| "c2"), &other.c2)?; - - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn add_constant_in_place>( - &mut self, - mut cs: CS, - other: &Fp3

, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; - self.c2.add_constant_in_place(cs.ns(|| "c2"), &other.c2)?; - Ok(self) - } - - /// Use the Toom-Cook-3x method to compute multiplication. - #[inline] - fn mul_by_constant>( - &self, - mut cs: CS, - other: &Fp3

, - ) -> Result { - // Uses Toom-Cook-3x multiplication from - // - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - // v0 = a(0)b(0) = a0 * b0 - let v0 = self.c0.mul_by_constant(cs.ns(|| "v0"), &other.c0)?; - - // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2) - let v1 = { - let mut v1_cs = cs.ns(|| "v1"); - let mut a0_plus_a1_plus_a2 = self - .c0 - .add(v1_cs.ns(|| "a0 + a1"), &self.c1)? - .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?; - let b0_plus_b1_plus_b2 = other.c0 + &other.c1 + &other.c2; - - a0_plus_a1_plus_a2.mul_by_constant_in_place( - v1_cs.ns(|| "(a0 + a1 + a2)*(b0 + b1 + b2)"), - &b0_plus_b1_plus_b2, - )?; - a0_plus_a1_plus_a2 - }; - - // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2) - let mut v2 = { - let mut v2_cs = cs.ns(|| "v2"); - let mut a0_minus_a1_plus_a2 = self - .c0 - .sub(v2_cs.ns(|| "sub1"), &self.c1)? - .add(v2_cs.ns(|| "add2"), &self.c2)?; - let b0_minus_b1_plus_b2 = other.c0 - &other.c1 + &other.c2; - a0_minus_a1_plus_a2.mul_by_constant_in_place( - v2_cs.ns(|| "(a0 - a1 + a2)*(b0 - b1 + b2)"), - &b0_minus_b1_plus_b2, - )?; - a0_minus_a1_plus_a2 - }; - - // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2) - let mut v3 = { - let mut v3_cs = cs.ns(|| "v3"); - let a1_double = self.c1.double(v3_cs.ns(|| "2a1"))?; - let a2_quad = self - .c2 - .double(v3_cs.ns(|| "2a2"))? - .double(v3_cs.ns(|| "4a2"))?; - let mut a0_plus_2_a1_plus_4_a2 = self - .c0 - .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)? - .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; - - let b1_double = other.c1.double(); - let b2_quad = other.c2.double().double(); - let b0_plus_2_b1_plus_4_b2 = other.c0 + &b1_double + &b2_quad; - - a0_plus_2_a1_plus_4_a2.mul_by_constant_in_place( - v3_cs.ns(|| "(a0 + 2a1 + 4a2)*(b0 + 2b1 + 4b2)"), - &b0_plus_2_b1_plus_4_b2, - )?; - a0_plus_2_a1_plus_4_a2 - }; - - // v4 = a(∞)b(∞) = a2 * b2 - let v4 = self.c2.mul_by_constant(cs.ns(|| "v4"), &other.c2)?; - - let two = P::Fp::one().double(); - let six = two.double() + &two; - let mut two_and_six = [two, six]; - algebra::fields::batch_inversion(&mut two_and_six); - let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); - - let mut half_v0 = v0.mul_by_constant(cs.ns(|| "half_v0"), &two_inverse)?; - let half_v1 = v1.mul_by_constant(cs.ns(|| "half_v1"), &two_inverse)?; - let mut one_sixth_v2 = v2.mul_by_constant(cs.ns(|| "v2_by_6"), &six_inverse)?; - let one_sixth_v3 = v3.mul_by_constant_in_place(cs.ns(|| "v3_by_6"), &six_inverse)?; - let two_v4 = v4.double(cs.ns(|| "2v4"))?; - - // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) - let c0 = { - let mut c0_cs = cs.ns(|| "c0"); - - // No constraints, only get a linear combination back. - let mut inner = half_v0 - .sub(c0_cs.ns(|| "sub1"), &half_v1)? - .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? - .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? - .sub(c0_cs.ns(|| "sub4"), &two_v4)?; - let non_residue_times_inner = - inner.mul_by_constant_in_place(&mut c0_cs, &P::NONRESIDUE)?; - v0.add(c0_cs.ns(|| "add5"), non_residue_times_inner)? - }; - - // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 - let c1 = { - let mut c1_cs = cs.ns(|| "c1"); - let one_third_v2 = one_sixth_v2.double_in_place(c1_cs.ns(|| "double1"))?; - let non_residue_v4 = - v4.mul_by_constant(c1_cs.ns(|| "mul_by_const1"), &P::NONRESIDUE)?; - - half_v0 - .negate_in_place(c1_cs.ns(|| "neg1"))? - .add(c1_cs.ns(|| "add1"), &v1)? - .sub(c1_cs.ns(|| "sub2"), one_third_v2)? - .sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)? - .add(c1_cs.ns(|| "add4"), &two_v4)? - .add(c1_cs.ns(|| "add5"), &non_residue_v4)? - }; - - // -v0 + (1/2)v1 + (1/2)v2 −v4 - let c2 = { - let mut c2_cs = cs.ns(|| "c2"); - let half_v2 = v2.mul_by_constant_in_place(c2_cs.ns(|| "half_v2"), &two_inverse)?; - half_v1 - .add(c2_cs.ns(|| "add1"), half_v2)? - .sub(c2_cs.ns(|| "sub2"), &v4)? - .sub(c2_cs.ns(|| "sub3"), &v0)? - }; - - Ok(Self::new(c0, c1, c2)) - } - - fn frobenius_map>( - &self, - cs: CS, +impl CubicExtVarParams> for Fp3ParamsWrapper

{ + fn mul_base_field_vars_by_frob_coeff( + c1: &mut FpVar, + c2: &mut FpVar, power: usize, - ) -> Result { - let mut result = self.clone(); - result.frobenius_map_in_place(cs, power)?; - Ok(result) - } - - fn frobenius_map_in_place>( - &mut self, - mut cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - self.c1.mul_by_constant_in_place( - cs.ns(|| "c1_power"), - &P::FROBENIUS_COEFF_FP3_C1[power % 3], - )?; - self.c2.mul_by_constant_in_place( - cs.ns(|| "c2_power"), - &P::FROBENIUS_COEFF_FP3_C2[power % 3], - )?; - - Ok(self) - } - - fn cost_of_mul() -> usize { - 5 * FpGadget::::cost_of_mul() - } - - fn cost_of_mul_equals() -> usize { - 6 * FpGadget::::cost_of_mul() - } -} - -impl, ConstraintF: PrimeField + SquareRootField> PartialEq - for Fp3Gadget -{ - fn eq(&self, other: &Self) -> bool { - self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2 - } -} - -impl, ConstraintF: PrimeField + SquareRootField> Eq - for Fp3Gadget -{ -} - -impl, ConstraintF: PrimeField + SquareRootField> - EqGadget for Fp3Gadget -{ -} - -impl, ConstraintF: PrimeField + SquareRootField> - ConditionalEqGadget for Fp3Gadget -{ - #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.c0 - .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; - self.c1 - .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; - self.c2 - .conditional_enforce_equal(&mut cs.ns(|| "c2"), &other.c2, condition)?; - Ok(()) - } - - fn cost() -> usize { - 3 * as ConditionalEqGadget>::cost() - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - NEqGadget for Fp3Gadget -{ - #[inline] - fn enforce_not_equal>( - &self, - mut cs: CS, - other: &Self, - ) -> Result<(), SynthesisError> { - self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; - self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; - self.c2.enforce_not_equal(&mut cs.ns(|| "c2"), &other.c2)?; - Ok(()) - } - - fn cost() -> usize { - 3 * as NEqGadget>::cost() - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - ToBitsGadget for Fp3Gadget -{ - fn to_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_bits(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } - - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_non_unique_bits(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - ToBytesGadget for Fp3Gadget -{ - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_bytes(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } - - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_non_unique_bytes(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } -} - -impl, ConstraintF: PrimeField + SquareRootField> Clone - for Fp3Gadget -{ - fn clone(&self) -> Self { - Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone()) - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - CondSelectGadget for Fp3Gadget -{ - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - first: &Self, - second: &Self, - ) -> Result { - let c0 = FpGadget::::conditionally_select( - &mut cs.ns(|| "c0"), - cond, - &first.c0, - &second.c0, - )?; - let c1 = FpGadget::::conditionally_select( - &mut cs.ns(|| "c1"), - cond, - &first.c1, - &second.c1, - )?; - let c2 = FpGadget::::conditionally_select( - &mut cs.ns(|| "c2"), - cond, - &first.c2, - &second.c2, - )?; - - Ok(Self::new(c0, c1, c2)) - } - - fn cost() -> usize { - 3 * as CondSelectGadget>::cost() - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - TwoBitLookupGadget for Fp3Gadget -{ - type TableConstant = Fp3

; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c2s = c.iter().map(|f| f.c2).collect::>(); - let c0 = FpGadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; - let c1 = FpGadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; - let c2 = FpGadget::::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?; - Ok(Self::new(c0, c1, c2)) - } - - fn cost() -> usize { - 3 * as TwoBitLookupGadget>::cost() - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - ThreeBitCondNegLookupGadget for Fp3Gadget -{ - type TableConstant = Fp3

; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c2s = c.iter().map(|f| f.c2).collect::>(); - let c0 = FpGadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c0"), - b, - b0b1, - &c0s, - )?; - let c1 = FpGadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c1"), - b, - b0b1, - &c1s, - )?; - let c2 = FpGadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c2"), - b, - b0b1, - &c2s, - )?; - Ok(Self::new(c0, c1, c2)) - } - - fn cost() -> usize { - 3 * as ThreeBitCondNegLookupGadget>::cost() - } -} - -impl, ConstraintF: PrimeField + SquareRootField> - AllocGadget, ConstraintF> for Fp3Gadget -{ - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow()) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1, c2) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = FpGadget::::alloc(&mut cs.ns(|| "c0"), || c0)?; - let c1 = FpGadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; - let c2 = FpGadget::::alloc(&mut cs.ns(|| "c2"), || c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1, c2) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = FpGadget::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; - let c1 = FpGadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; - let c2 = FpGadget::::alloc_input(&mut cs.ns(|| "c2"), || c2)?; - Ok(Self::new(c0, c1, c2)) + ) { + *c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + *c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; } } diff --git a/r1cs-std/src/fields/fp4.rs b/r1cs-std/src/fields/fp4.rs index d04264e..2f9da2b 100644 --- a/r1cs-std/src/fields/fp4.rs +++ b/r1cs-std/src/fields/fp4.rs @@ -1,757 +1,11 @@ -use algebra::{ - fields::{Fp2, Fp2Parameters, Fp4, Fp4Parameters}, - BigInteger, PrimeField, -}; -use core::{borrow::Borrow, marker::PhantomData}; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use crate::fields::{fp2::Fp2Var, quadratic_extension::*}; +use algebra::fields::{Fp4Parameters, Fp4ParamsWrapper, QuadExtParameters}; -use crate::{prelude::*, Vec}; +pub type Fp4Var

= QuadExtVar::Fp2Params>, Fp4ParamsWrapper

>; -type Fp2Gadget = - super::fp2::Fp2Gadget<

::Fp2Params, ConstraintF>; -type Fp2GadgetVariable = as FieldGadget< - Fp2<

::Fp2Params>, - ConstraintF, ->>::Variable; - -#[derive(Derivative)] -#[derivative(Debug(bound = "ConstraintF: PrimeField"))] -#[must_use] -pub struct Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - pub c0: Fp2Gadget, - pub c1: Fp2Gadget, - #[derivative(Debug = "ignore")] - _params: PhantomData

, -} - -impl ToConstraintFieldGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?; - let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?; - - res.append(&mut c0_gadget); - res.append(&mut c1_gadget); - - Ok(res) - } -} - -impl Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - pub fn new(c0: Fp2Gadget, c1: Fp2Gadget) -> Self { - Self { - c0, - c1, - _params: PhantomData, - } - } - - /// Multiply a Fp2Gadget by quadratic nonresidue P::NONRESIDUE. - #[inline] - pub fn mul_fp2_gadget_by_nonresidue>( - cs: CS, - fe: &Fp2Gadget, - ) -> Result, SynthesisError> { - let new_c0 = Fp2Gadget::::mul_fp_gadget_by_nonresidue(cs, &fe.c1)?; - let new_c1 = fe.c0.clone(); - Ok(Fp2Gadget::::new(new_c0, new_c1)) - } - - /// Multiply a Fp4Gadget by an element of fp. - #[inline] - pub fn mul_by_fp_constant_in_place>( - &mut self, - mut cs: CS, - fe: &<

::Fp2Params as Fp2Parameters>::Fp, - ) -> Result<&mut Self, SynthesisError> { - self.c0.mul_by_fp_constant_in_place(cs.ns(|| "c0"), fe)?; - self.c1.mul_by_fp_constant_in_place(cs.ns(|| "c1"), fe)?; - Ok(self) - } - - /// Multiply a Fp4Gadget by an element of fp. - #[inline] - pub fn mul_by_fp_constant>( - &self, - cs: CS, - fe: &<

::Fp2Params as Fp2Parameters>::Fp, - ) -> Result { - let mut result = self.clone(); - result.mul_by_fp_constant_in_place(cs, fe)?; - Ok(result) - } - - pub fn unitary_inverse>( - &self, - cs: CS, - ) -> Result { - Ok(Self::new(self.c0.clone(), self.c1.negate(cs)?)) - } - - #[inline] - pub fn cyclotomic_exp, B: BigInteger>( - &self, - mut cs: CS, - exponent: &B, - ) -> Result { - let mut res = Self::one(cs.ns(|| "one"))?; - let self_inverse = self.unitary_inverse(cs.ns(|| "unitary inverse"))?; - - let mut found_nonzero = false; - let naf = exponent.find_wnaf(); - - for (i, &value) in naf.iter().rev().enumerate() { - if found_nonzero { - res.square_in_place(cs.ns(|| format!("square {}", i)))?; - } - - if value != 0 { - found_nonzero = true; - - if value > 0 { - res.mul_in_place(cs.ns(|| format!("res *= self {}", i)), &self)?; - } else { - res.mul_in_place( - cs.ns(|| format!("res *= self_inverse {}", i)), - &self_inverse, - )?; - } - } - } - - Ok(res) - } -} - -impl FieldGadget, ConstraintF> for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - type Variable = ( - Fp2GadgetVariable, - Fp2GadgetVariable, - ); - - #[inline] - fn get_value(&self) -> Option> { - match (self.c0.get_value(), self.c1.get_value()) { - (Some(c0), Some(c1)) => Some(Fp4::new(c0, c1)), - (..) => None, - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.c0.get_variable(), self.c1.get_variable()) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - let c0 = Fp2Gadget::::zero(cs.ns(|| "c0"))?; - let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn one>(mut cs: CS) -> Result { - let c0 = Fp2Gadget::::one(cs.ns(|| "c0"))?; - let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn conditionally_add_constant>( - &self, - mut cs: CS, - bit: &Boolean, - coeff: Fp4

, - ) -> Result { - let c0 = self - .c0 - .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; - let c1 = self - .c1 - .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?; - let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?; - let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn double>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.double_in_place(cs)?; - Ok(result) - } - - #[inline] - fn double_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.double_in_place(&mut cs.ns(|| "double c0"))?; - self.c1.double_in_place(&mut cs.ns(|| "double c1"))?; - Ok(self) - } - - #[inline] - fn negate>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.negate_in_place(cs)?; - Ok(result) - } - - #[inline] - fn negate_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?; - self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?; - Ok(self) - } - - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Karatsuba multiplication for Fp4: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - let v0 = self.c0.mul(mul_cs.ns(|| "v0"), &other.c0)?; - let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - let c0 = { - let non_residue_times_v1 = - Self::mul_fp2_gadget_by_nonresidue(mul_cs.ns(|| "first mul_by_nr"), &v1)?; - v0.add(mul_cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)? - }; - let c1 = { - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let a0_plus_a1_times_b0_plus_b1 = - a0_plus_a1.mul(&mut mul_cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?; - a0_plus_a1_times_b0_plus_b1 - .sub(mul_cs.ns(|| "res - v0"), &v0)? - .sub(mul_cs.ns(|| "res - v0 - v1"), &v1)? - }; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn square>( - &self, - mut cs: CS, - ) -> Result { - // From Libsnark/fp4_gadget.tcc - // Complex multiplication for Fp4: - // v0 = A.c0 * A.c1 - // result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 + - // non_residue) * v0 result.c1 = 2 * v0 - // Enforced with 2 constraints: - // (2*A.c0) * A.c1 = result.c1 - // (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1 - // + non_residue)/2 Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - - let non_residue_c1 = - Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?; - let a0_plus_non_residue_c1 = self - .c0 - .add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_c1)?; - let one_plus_non_residue_v0 = - Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)? - .add(cs.ns(|| "plus v0"), &v0)?; - - let c0 = a0_plus_a1 - .mul( - cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"), - &a0_plus_non_residue_c1, - )? - .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?; - - v0.double_in_place(cs.ns(|| "2v0"))?; - let c1 = v0; - - Ok(Self::new(c0, c1)) - } - - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - // Karatsuba multiplication for Fp4: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - // Compute v1 - let mut v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - - // Perform second check - let non_residue_times_v1 = - Self::mul_fp2_gadget_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?; - let rhs = result - .c0 - .sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?; - self.c0 - .mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?; - - // Last check - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let one_minus_non_residue_v1 = - v1.sub_in_place(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?; - - let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result - .c1 - .add(mul_cs.ns(|| "c1 + c0"), &result.c0)? - .add(mul_cs.ns(|| "rest of stuff"), one_minus_non_residue_v1)?; - - a0_plus_a1.mul_equals( - mul_cs.ns(|| "third check"), - &b0_plus_b1, - &result_c1_plus_result_c0_plus_one_minus_non_residue_v1, - )?; - - Ok(()) - } - - fn frobenius_map>( - &self, - cs: CS, - power: usize, - ) -> Result { - let mut result = self.clone(); - let _ = result.frobenius_map_in_place(cs, power)?; - Ok(result) - } - - fn frobenius_map_in_place>( - &mut self, - mut cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - self.c0 - .frobenius_map_in_place(cs.ns(|| "frob_map1"), power)?; - self.c1 - .frobenius_map_in_place(cs.ns(|| "frob_map2"), power)?; - self.c1 - .mul_by_fp_constant_in_place(cs.ns(|| "mul"), &P::FROBENIUS_COEFF_FP4_C1[power % 4])?; - Ok(self) - } - - #[inline] - fn add_constant>( - &self, - cs: CS, - other: &Fp4

, - ) -> Result { - let mut result = self.clone(); - let _ = result.add_constant_in_place(cs, other)?; - Ok(result) - } - - #[inline] - fn add_constant_in_place>( - &mut self, - mut cs: CS, - other: &Fp4

, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; - Ok(self) - } - - fn mul_by_constant>( - &self, - mut cs: CS, - fe: &Fp4

, - ) -> Result { - // Karatsuba multiplication (see mul above). - // Doesn't need any constraints; returns linear combinations of - // `self`'s variables. - // - // (The operations below are guaranteed to return linear combinations) - let (a0, a1) = (&self.c0, &self.c1); - let (b0, b1) = (fe.c0, fe.c1); - let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?; - let mut v1 = Self::mul_fp2_gadget_by_nonresidue(&mut cs.ns(|| "v1"), a1)?; - let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?; - - v0.add_in_place(&mut cs.ns(|| "c0"), &beta_v1)?; - let c0 = v0; - - let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?; - let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?; - a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?; - let c1 = a0b1; - Ok(Self::new(c0, c1)) - } - - fn cost_of_mul() -> usize { - 3 * Fp2Gadget::::cost_of_mul() - } - - fn cost_of_mul_equals() -> usize { - Self::cost_of_mul() - } -} - -impl PartialEq for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn eq(&self, other: &Self) -> bool { - self.c0 == other.c0 && self.c1 == other.c1 - } -} - -impl Eq for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ -} - -impl EqGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ -} - -impl ConditionalEqGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.c0 - .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; - self.c1 - .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; - Ok(()) - } - - fn cost() -> usize { - 2 * as ConditionalEqGadget>::cost() - } -} - -impl NEqGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn enforce_not_equal>( - &self, - mut cs: CS, - other: &Self, - ) -> Result<(), SynthesisError> { - self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; - self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; - Ok(()) - } - - fn cost() -> usize { - 2 * as NEqGadget>::cost() - } -} - -impl ToBitsGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn to_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl ToBytesGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl Clone for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn clone(&self) -> Self { - Self { - c0: self.c0.clone(), - c1: self.c1.clone(), - _params: PhantomData, - } - } -} - -impl CondSelectGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - first: &Self, - second: &Self, - ) -> Result { - let c0 = Fp2Gadget::::conditionally_select( - &mut cs.ns(|| "c0"), - cond, - &first.c0, - &second.c0, - )?; - let c1 = Fp2Gadget::::conditionally_select( - &mut cs.ns(|| "c1"), - cond, - &first.c1, - &second.c1, - )?; - - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as CondSelectGadget>::cost() - } -} - -impl TwoBitLookupGadget for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - type TableConstant = Fp4

; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; - let c1 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as TwoBitLookupGadget>::cost() - } -} - -impl ThreeBitCondNegLookupGadget - for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - type TableConstant = Fp4

; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = Fp2Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c0"), - b, - b0b1, - &c0s, - )?; - let c1 = Fp2Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c1"), - b, - b0b1, - &c1s, - )?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as ThreeBitCondNegLookupGadget>::cost() - } -} - -impl AllocGadget, ConstraintF> for Fp4Gadget -where - P: Fp4Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow()) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) - } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = Fp2Gadget::::alloc(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp2Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) - } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) +impl QuadExtVarParams> for Fp4ParamsWrapper

{ + fn mul_base_field_var_by_frob_coeff(fe: &mut Fp2Var, power: usize) { + fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; } } diff --git a/r1cs-std/src/fields/fp6_2over3.rs b/r1cs-std/src/fields/fp6_2over3.rs index f889627..147e239 100644 --- a/r1cs-std/src/fields/fp6_2over3.rs +++ b/r1cs-std/src/fields/fp6_2over3.rs @@ -1,749 +1,12 @@ -use algebra::{ - fields::{ - fp6_2over3::{Fp6, Fp6Parameters}, - Fp3, Fp3Parameters, - }, - BigInteger, PrimeField, SquareRootField, -}; -use core::{borrow::Borrow, marker::PhantomData}; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use crate::fields::{fp3::Fp3Var, quadratic_extension::*}; +use algebra::fields::{fp6_2over3::*, QuadExtParameters}; -use crate::{prelude::*, Vec}; +pub type Fp6Var

= QuadExtVar::Fp3Params>, Fp6ParamsWrapper

>; -type Fp3Gadget = - super::fp3::Fp3Gadget<

::Fp3Params, ConstraintF>; -type Fp3GadgetVariable = as FieldGadget< - Fp3<

::Fp3Params>, - ConstraintF, ->>::Variable; - -#[derive(Derivative)] -#[derivative(Debug(bound = "ConstraintF: PrimeField + SquareRootField"))] -#[must_use] -pub struct Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - pub c0: Fp3Gadget, - pub c1: Fp3Gadget, - #[derivative(Debug = "ignore")] - _params: PhantomData

, -} - -impl ToConstraintFieldGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?; - let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?; - - res.append(&mut c0_gadget); - res.append(&mut c1_gadget); - - Ok(res) - } -} - -impl Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - pub fn new(c0: Fp3Gadget, c1: Fp3Gadget) -> Self { - Self { - c0, - c1, - _params: PhantomData, - } - } - - /// Multiply a Fp3Gadget by quadratic nonresidue P::NONRESIDUE. - #[inline] - pub fn mul_fp3_gadget_by_nonresidue>( - mut cs: CS, - fe: &Fp3Gadget, - ) -> Result, SynthesisError> { - let mut res = Fp3Gadget::::new(fe.c2.clone(), fe.c0.clone(), fe.c1.clone()); - res.c0.mul_by_constant_in_place( - cs.ns(|| "res * non_residue"), - &::NONRESIDUE, - )?; - Ok(res) - } - - pub fn unitary_inverse>( - &self, - cs: CS, - ) -> Result { - Ok(Self::new(self.c0.clone(), self.c1.negate(cs)?)) - } - - #[inline] - pub fn cyclotomic_exp, B: BigInteger>( - &self, - mut cs: CS, - exponent: &B, - ) -> Result { - let mut res = Self::one(cs.ns(|| "one"))?; - let self_inverse = self.unitary_inverse(cs.ns(|| "unitary inverse"))?; - - let mut found_nonzero = false; - let naf = exponent.find_wnaf(); - - for (i, &value) in naf.iter().rev().enumerate() { - if found_nonzero { - res.square_in_place(cs.ns(|| format!("square {}", i)))?; - } - - if value != 0 { - found_nonzero = true; - - if value > 0 { - res.mul_in_place(cs.ns(|| format!("res *= self {}", i)), &self)?; - } else { - res.mul_in_place( - cs.ns(|| format!("res *= self_inverse {}", i)), - &self_inverse, - )?; - } - } - } - - Ok(res) - } -} - -impl FieldGadget, ConstraintF> - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - type Variable = ( - Fp3GadgetVariable, - Fp3GadgetVariable, - ); - - #[inline] - fn get_value(&self) -> Option> { - match (self.c0.get_value(), self.c1.get_value()) { - (Some(c0), Some(c1)) => Some(Fp6::new(c0, c1)), - (..) => None, - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - (self.c0.get_variable(), self.c1.get_variable()) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - let c0 = Fp3Gadget::::zero(cs.ns(|| "c0"))?; - let c1 = Fp3Gadget::::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn one>(mut cs: CS) -> Result { - let c0 = Fp3Gadget::::one(cs.ns(|| "c0"))?; - let c1 = Fp3Gadget::::zero(cs.ns(|| "c1"))?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn conditionally_add_constant>( - &self, - mut cs: CS, - bit: &Boolean, - coeff: Fp6

, - ) -> Result { - let c0 = self - .c0 - .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; - let c1 = self - .c1 - .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?; - let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?; - let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn double>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.double_in_place(cs)?; - Ok(result) - } - - #[inline] - fn double_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.double_in_place(&mut cs.ns(|| "double c0"))?; - self.c1.double_in_place(&mut cs.ns(|| "double c1"))?; - Ok(self) - } - - #[inline] - fn negate>(&self, cs: CS) -> Result { - let mut result = self.clone(); - result.negate_in_place(cs)?; - Ok(result) - } - - #[inline] - fn negate_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?; - self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?; - Ok(self) - } - - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Karatsuba multiplication for Fp6: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - let v0 = self.c0.mul(mul_cs.ns(|| "v0"), &other.c0)?; - let v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - let c0 = { - let non_residue_times_v1 = - Self::mul_fp3_gadget_by_nonresidue(mul_cs.ns(|| "first mul_by_nr"), &v1)?; - v0.add(mul_cs.ns(|| "v0 + beta * v1"), &non_residue_times_v1)? - }; - let c1 = { - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let a0_plus_a1_times_b0_plus_b1 = - a0_plus_a1.mul(&mut mul_cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)?; - a0_plus_a1_times_b0_plus_b1 - .sub(mul_cs.ns(|| "res - v0"), &v0)? - .sub(mul_cs.ns(|| "res - v0 - v1"), &v1)? - }; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn square>( - &self, - mut cs: CS, - ) -> Result { - // From Libsnark/fp4_gadget.tcc - // Complex multiplication for Fp6: - // v0 = A.c0 * A.c1 - // result.c0 = (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) - (1 + - // non_residue) * v0 result.c1 = 2 * v0 - // Enforced with 2 constraints: - // (2*A.c0) * A.c1 = result.c1 - // (A.c0 + A.c1) * (A.c0 + non_residue * A.c1) = result.c0 + result.c1 * (1 - // + non_residue)/2 Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - let mut v0 = self.c0.mul(cs.ns(|| "v0"), &self.c1)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - - let non_residue_c1 = - Self::mul_fp3_gadget_by_nonresidue(cs.ns(|| "non_residue * a1"), &self.c1)?; - let a0_plus_non_residue_c1 = self - .c0 - .add(cs.ns(|| "a0 + non_residue * a1"), &non_residue_c1)?; - let one_plus_non_residue_v0 = - Self::mul_fp3_gadget_by_nonresidue(cs.ns(|| "non_residue * v0"), &v0)? - .add(cs.ns(|| "plus v0"), &v0)?; - - let c0 = a0_plus_a1 - .mul( - cs.ns(|| "(a0 + a1) * (a0 + non_residue * a1)"), - &a0_plus_non_residue_c1, - )? - .sub(cs.ns(|| "- (1 + non_residue) v0"), &one_plus_non_residue_v0)?; - - v0.double_in_place(cs.ns(|| "2v0"))?; - let c1 = v0; - - Ok(Self::new(c0, c1)) - } - - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - // Karatsuba multiplication for Fp6: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // result.c0 = v0 + non_residue * v1 - // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 - // Enforced with 3 constraints: - // A.c1 * B.c1 = v1 - // A.c0 * B.c0 = result.c0 - non_residue * v1 - // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - let mul_cs = &mut cs.ns(|| "mul"); - - // Compute v1 - let mut v1 = self.c1.mul(mul_cs.ns(|| "v1"), &other.c1)?; - - // Perform second check - let non_residue_times_v1 = - Self::mul_fp3_gadget_by_nonresidue(mul_cs.ns(|| "nr * v1"), &v1)?; - let rhs = result - .c0 - .sub(mul_cs.ns(|| "sub from result.c0"), &non_residue_times_v1)?; - self.c0 - .mul_equals(mul_cs.ns(|| "second check"), &other.c0, &rhs)?; - - // Last check - let a0_plus_a1 = self.c0.add(mul_cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(mul_cs.ns(|| "b0 + b1"), &other.c1)?; - let one_minus_non_residue_v1 = - v1.sub_in_place(mul_cs.ns(|| "sub from v1"), &non_residue_times_v1)?; - - let result_c1_plus_result_c0_plus_one_minus_non_residue_v1 = result - .c1 - .add(mul_cs.ns(|| "c1 + c0"), &result.c0)? - .add(mul_cs.ns(|| "rest of stuff"), one_minus_non_residue_v1)?; - - a0_plus_a1.mul_equals( - mul_cs.ns(|| "third check"), - &b0_plus_b1, - &result_c1_plus_result_c0_plus_one_minus_non_residue_v1, - )?; - - Ok(()) - } - - fn frobenius_map>( - &self, - cs: CS, - power: usize, - ) -> Result { - let mut result = self.clone(); - let _ = result.frobenius_map_in_place(cs, power)?; - Ok(result) - } - - fn frobenius_map_in_place>( - &mut self, - mut cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - self.c0 - .frobenius_map_in_place(cs.ns(|| "frob_map1"), power)?; - self.c1 - .frobenius_map_in_place(cs.ns(|| "frob_map2"), power)?; - self.c1 - .mul_by_fp_constant_in_place(cs.ns(|| "mul"), &P::FROBENIUS_COEFF_FP6_C1[power % 6])?; - Ok(self) - } - - #[inline] - fn add_constant>( - &self, - cs: CS, - other: &Fp6

, - ) -> Result { - let mut result = self.clone(); - let _ = result.add_constant_in_place(cs, other)?; - Ok(result) - } - - #[inline] - fn add_constant_in_place>( - &mut self, - mut cs: CS, - other: &Fp6

, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; - Ok(self) - } - - fn mul_by_constant>( - &self, - mut cs: CS, - fe: &Fp6

, - ) -> Result { - // Karatsuba multiplication (see mul above). - // Doesn't need any constraints; returns linear combinations of - // `self`'s variables. - // - // (The operations below are guaranteed to return linear combinations) - let (a0, a1) = (&self.c0, &self.c1); - let (b0, b1) = (fe.c0, fe.c1); - let mut v0 = a0.mul_by_constant(&mut cs.ns(|| "v0"), &b0)?; - let mut v1 = Self::mul_fp3_gadget_by_nonresidue(&mut cs.ns(|| "v1"), a1)?; - let beta_v1 = v1.mul_by_constant_in_place(&mut cs.ns(|| "beta * v1"), &b1)?; - - v0.add_in_place(&mut cs.ns(|| "c0"), &beta_v1)?; - let c0 = v0; - - let mut a0b1 = a0.mul_by_constant(&mut cs.ns(|| "a0b1"), &b1)?; - let a1b0 = a1.mul_by_constant(&mut cs.ns(|| "a1b0"), &b0)?; - a0b1.add_in_place(&mut cs.ns(|| "c1"), &a1b0)?; - let c1 = a0b1; - Ok(Self::new(c0, c1)) - } - - fn cost_of_mul() -> usize { - 2 * Fp3Gadget::::cost_of_mul() - } - - fn cost_of_mul_equals() -> usize { - Self::cost_of_mul() - } -} - -impl PartialEq for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - fn eq(&self, other: &Self) -> bool { - self.c0 == other.c0 && self.c1 == other.c1 - } -} - -impl Eq for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ -} - -impl EqGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ -} - -impl ConditionalEqGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.c0 - .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; - self.c1 - .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; - Ok(()) - } - - fn cost() -> usize { - 2 * as ConditionalEqGadget>::cost() - } -} - -impl NEqGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - #[inline] - fn enforce_not_equal>( - &self, - mut cs: CS, - other: &Self, - ) -> Result<(), SynthesisError> { - self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; - self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; - Ok(()) - } - - fn cost() -> usize { - 2 * as NEqGadget>::cost() - } -} - -impl ToBitsGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - fn to_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl ToBytesGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } - - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; - c0.append(&mut c1); - Ok(c0) - } -} - -impl Clone for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - fn clone(&self) -> Self { - Self { - c0: self.c0.clone(), - c1: self.c1.clone(), - _params: PhantomData, - } - } -} - -impl CondSelectGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - first: &Self, - second: &Self, - ) -> Result { - let c0 = Fp3Gadget::::conditionally_select( - &mut cs.ns(|| "c0"), - cond, - &first.c0, - &second.c0, - )?; - let c1 = Fp3Gadget::::conditionally_select( - &mut cs.ns(|| "c1"), - cond, - &first.c1, - &second.c1, - )?; - - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as CondSelectGadget>::cost() - } -} - -impl TwoBitLookupGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - type TableConstant = Fp6

; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = Fp3Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; - let c1 = Fp3Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as TwoBitLookupGadget>::cost() - } -} - -impl ThreeBitCondNegLookupGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - type TableConstant = Fp6

; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c0 = Fp3Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c0"), - b, - b0b1, - &c0s, - )?; - let c1 = Fp3Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c1"), - b, - b0b1, - &c1s, - )?; - Ok(Self::new(c0, c1)) - } - - fn cost() -> usize { - 2 * as ThreeBitCondNegLookupGadget>::cost() - } -} - -impl AllocGadget, ConstraintF> - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp3Params: Fp3Parameters, -{ - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow()) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) - } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = Fp3Gadget::::alloc(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp3Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) - } - - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1)) - } - Err(_) => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = Fp3Gadget::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp3Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; - Ok(Self::new(c0, c1)) +impl QuadExtVarParams> for Fp6ParamsWrapper

{ + fn mul_base_field_var_by_frob_coeff(fe: &mut Fp3Var, power: usize) { + fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; } } diff --git a/r1cs-std/src/fields/fp6_3over2.rs b/r1cs-std/src/fields/fp6_3over2.rs index 22e3a48..34da0bc 100644 --- a/r1cs-std/src/fields/fp6_3over2.rs +++ b/r1cs-std/src/fields/fp6_3over2.rs @@ -1,114 +1,43 @@ -use algebra::{ - fields::{ - fp6_3over2::{Fp6, Fp6Parameters}, - Field, Fp2Parameters, - }, - PrimeField, -}; -use core::{borrow::Borrow, marker::PhantomData}; -use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError}; +use crate::fields::{cubic_extension::*, fp2::*}; +use algebra::fields::{fp6_3over2::*, CubicExtParameters, Fp2}; +use core::ops::MulAssign; +use r1cs_core::SynthesisError; -use crate::{prelude::*, Vec}; +pub type Fp6Var

= CubicExtVar::Fp2Params>, Fp6ParamsWrapper

>; -type Fp2Gadget = - super::fp2::Fp2Gadget<

::Fp2Params, ConstraintF>; - -#[derive(Derivative)] -#[derivative(Debug(bound = "ConstraintF: PrimeField"))] -#[must_use] -pub struct Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - pub c0: Fp2Gadget, - pub c1: Fp2Gadget, - pub c2: Fp2Gadget, - #[derivative(Debug = "ignore")] - _params: PhantomData

, -} - -impl ToConstraintFieldGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn to_constraint_field>( - &self, - mut cs: CS, - ) -> Result>, SynthesisError> { - let mut res = Vec::new(); - - let mut c0_gadget = self.c0.to_constraint_field(&mut cs.ns(|| "c0"))?; - let mut c1_gadget = self.c1.to_constraint_field(&mut cs.ns(|| "c1"))?; - let mut c2_gadget = self.c2.to_constraint_field(&mut cs.ns(|| "c2"))?; - - res.append(&mut c0_gadget); - res.append(&mut c1_gadget); - res.append(&mut c2_gadget); - - Ok(res) +impl CubicExtVarParams> for Fp6ParamsWrapper

{ + fn mul_base_field_vars_by_frob_coeff( + c1: &mut Fp2Var, + c2: &mut Fp2Var, + power: usize, + ) { + *c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; + *c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; } } -impl Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - pub fn new( - c0: Fp2Gadget, - c1: Fp2Gadget, - c2: Fp2Gadget, - ) -> Self { - Self { - c0, - c1, - c2, - _params: PhantomData, - } - } - /// Multiply a Fp2Gadget by cubic nonresidue P::NONRESIDUE. - #[inline] - pub fn mul_fp2_gadget_by_nonresidue>( - cs: CS, - fe: &Fp2Gadget, - ) -> Result, SynthesisError> { - fe.mul_by_constant(cs, &P::NONRESIDUE) - } - - #[inline] - pub fn mul_by_0_c1_0>( - &self, - mut cs: CS, - c1: &Fp2Gadget, - ) -> Result { +impl Fp6Var

{ + pub fn mul_by_0_c1_0(&self, c1: &Fp2Var) -> Result { // Karatsuba multiplication // v0 = a0 * b0 = 0 // v1 = a1 * b1 - let v1 = self.c1.mul(cs.ns(|| "first mul"), c1)?; + let v1 = &self.c1 * c1; // v2 = a2 * b2 = 0 - let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?; + let a1_plus_a2 = &self.c1 + &self.c2; let b1_plus_b2 = c1.clone(); - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; + let a0_plus_a1 = &self.c0 + &self.c1; // c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0 // = NONRESIDUE * ((a1 + a2) * b1 - v1) - let c0 = a1_plus_a2 - .mul(cs.ns(|| "second mul"), &b1_plus_b2)? - .sub(cs.ns(|| "first sub"), &v1)? - .mul_by_constant(cs.ns(|| "mul_by_nonresidue"), &P::NONRESIDUE)?; + let c0 = &(a1_plus_a2 * &b1_plus_b2 - &v1) * P::NONRESIDUE; // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2 // = (a0 + a1) * b1 - v1 - let c1 = a0_plus_a1 - .mul(cs.ns(|| "third mul"), &c1)? - .sub(cs.ns(|| "second sub"), &v1)?; + let c1 = a0_plus_a1 * c1 - &v1; // c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1 // = v1 let c2 = v1; @@ -116,939 +45,37 @@ where } // #[inline] - pub fn mul_by_c0_c1_0>( + pub fn mul_by_c0_c1_0( &self, - mut cs: CS, - c0: &Fp2Gadget, - c1: &Fp2Gadget, + c0: &Fp2Var, + c1: &Fp2Var, ) -> Result { - let v0 = self.c0.mul(cs.ns(|| "v0"), c0)?; - let v1 = self.c1.mul(cs.ns(|| "v1"), c1)?; + let v0 = &self.c0 * c0; + let v1 = &self.c1 * c1; // v2 = 0. - let a1_plus_a2 = self.c1.add(cs.ns(|| "a1 + a2"), &self.c2)?; - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?; + let a1_plus_a2 = &self.c1 + &self.c2; + let a0_plus_a1 = &self.c0 + &self.c1; + let a0_plus_a2 = &self.c0 + &self.c2; let b1_plus_b2 = c1.clone(); - let b0_plus_b1 = c0.add(cs.ns(|| "b0 + b1"), &c1)?; + let b0_plus_b1 = c0 + c1; let b0_plus_b2 = c0.clone(); - let c0 = { - let cs = &mut cs.ns(|| "c0"); - a1_plus_a2 - .mul(cs.ns(|| "(a1 + a2) * (b1 + b2)"), &b1_plus_b2)? - .sub(cs.ns(|| "sub v1"), &v1)? - .mul_by_constant(cs.ns(|| "First mul_by_nonresidue"), &P::NONRESIDUE)? - .add(cs.ns(|| "add v0"), &v0)? - }; + let c0 = (&a1_plus_a2 * &b1_plus_b2 - &v1) * P::NONRESIDUE + &v0; - let c1 = { - let cs = &mut cs.ns(|| "c1"); - a0_plus_a1 - .mul(cs.ns(|| "(a0 + a1) * (b0 + b1)"), &b0_plus_b1)? - .sub(cs.ns(|| "sub v0"), &v0)? - .sub(cs.ns(|| "sub v1"), &v1)? - }; + let c1 = a0_plus_a1 * &b0_plus_b1 - &v0 - &v1; - let c2 = { - a0_plus_a2 - .mul(cs.ns(|| "(a0 + a2) * (b0 + b2)"), &b0_plus_b2)? - .sub(cs.ns(|| "sub v0"), &v0)? - .add(cs.ns(|| "add v1"), &v1)? - }; + let c2 = a0_plus_a2 * &b0_plus_b2 - &v0 + &v1; Ok(Self::new(c0, c1, c2)) } } -impl FieldGadget, ConstraintF> for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - type Variable = ( - (ConstraintVar, ConstraintVar), - (ConstraintVar, ConstraintVar), - (ConstraintVar, ConstraintVar), - ); - - #[inline] - fn get_value(&self) -> Option> { - match ( - self.c0.get_value(), - self.c1.get_value(), - self.c2.get_value(), - ) { - (Some(c0), Some(c1), Some(c2)) => Some(Fp6::new(c0, c1, c2)), - (..) => None, - } - } - - #[inline] - fn get_variable(&self) -> Self::Variable { - ( - self.c0.get_variable(), - self.c1.get_variable(), - self.c2.get_variable(), - ) - } - - #[inline] - fn zero>(mut cs: CS) -> Result { - let c0 = Fp2Gadget::::zero(cs.ns(|| "c0"))?; - let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; - let c2 = Fp2Gadget::::zero(cs.ns(|| "c2"))?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn one>(mut cs: CS) -> Result { - let c0 = Fp2Gadget::::one(cs.ns(|| "c0"))?; - let c1 = Fp2Gadget::::zero(cs.ns(|| "c1"))?; - let c2 = Fp2Gadget::::zero(cs.ns(|| "c2"))?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn conditionally_add_constant>( - &self, - mut cs: CS, - bit: &Boolean, - coeff: Fp6

, - ) -> Result { - let c0 = self - .c0 - .conditionally_add_constant(cs.ns(|| "c0"), bit, coeff.c0)?; - let c1 = self - .c1 - .conditionally_add_constant(cs.ns(|| "c1"), bit, coeff.c1)?; - let c2 = self - .c2 - .conditionally_add_constant(cs.ns(|| "c2"), bit, coeff.c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn add>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.add(&mut cs.ns(|| "add c0"), &other.c0)?; - let c1 = self.c1.add(&mut cs.ns(|| "add c1"), &other.c1)?; - let c2 = self.c2.add(&mut cs.ns(|| "add c2"), &other.c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn sub>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - let c0 = self.c0.sub(&mut cs.ns(|| "sub c0"), &other.c0)?; - let c1 = self.c1.sub(&mut cs.ns(|| "sub c1"), &other.c1)?; - let c2 = self.c2.sub(&mut cs.ns(|| "sub c2"), &other.c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn negate>( - &self, - mut cs: CS, - ) -> Result { - let c0 = self.c0.negate(&mut cs.ns(|| "negate c0"))?; - let c1 = self.c1.negate(&mut cs.ns(|| "negate c1"))?; - let c2 = self.c2.negate(&mut cs.ns(|| "negate c2"))?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn negate_in_place>( - &mut self, - mut cs: CS, - ) -> Result<&mut Self, SynthesisError> { - self.c0.negate_in_place(&mut cs.ns(|| "negate c0"))?; - self.c1.negate_in_place(&mut cs.ns(|| "negate c1"))?; - self.c2.negate_in_place(&mut cs.ns(|| "negate c2"))?; - Ok(self) - } - - /// Use the Toom-Cook-3x method to compute multiplication. - #[inline] - fn mul>( - &self, - mut cs: CS, - other: &Self, - ) -> Result { - // Uses Toom-Cook-3x multiplication from - // - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - // v0 = a(0)b(0) = a0 * b0 - let v0 = self.c0.mul(&mut cs.ns(|| "Calc v0"), &other.c0)?; - - // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2) - let v1 = { - let mut v1_cs = cs.ns(|| "compute v1"); - let a0_plus_a1_plus_a2 = self - .c0 - .add(v1_cs.ns(|| "a0 + a1"), &self.c1)? - .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?; - let b0_plus_b1_plus_b2 = other - .c0 - .add(v1_cs.ns(|| "b0 + b1"), &other.c1)? - .add(v1_cs.ns(|| "b0 + b1 + b2"), &other.c2)?; - - a0_plus_a1_plus_a2.mul( - v1_cs.ns(|| "(a0 + a1 + a2)(b0 + b1 + b2)"), - &b0_plus_b1_plus_b2, - )? - }; - - // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2) - let v2 = { - let mut v2_cs = cs.ns(|| "compute v2"); - - let a0_minus_a1_plus_a2 = self - .c0 - .sub(v2_cs.ns(|| "a0 - a1"), &self.c1)? - .add(v2_cs.ns(|| "a0 - a1 + a2"), &self.c2)?; - - let b0_minus_b1_plus_b2 = other - .c0 - .sub(v2_cs.ns(|| "b0 - b1"), &other.c1)? - .add(v2_cs.ns(|| "b0 - b1 + b2"), &other.c2)?; - - a0_minus_a1_plus_a2.mul( - v2_cs.ns(|| "(a0 - a1 + a2)(b0 - b1 + b2)"), - &b0_minus_b1_plus_b2, - )? - }; - - // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2) - let v3 = { - let v3_cs = &mut cs.ns(|| "compute v3"); - - let a1_double = self.c1.double(v3_cs.ns(|| "2 * a1"))?; - let a2_quad = self - .c2 - .double(v3_cs.ns(|| "2 * a2"))? - .double(v3_cs.ns(|| "4 * a2"))?; - - let a0_plus_2_a1_plus_4_a2 = self - .c0 - .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)? - .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; - - let b1_double = other.c1.double(v3_cs.ns(|| "2 * b1"))?; - let b2_quad = other - .c2 - .double(v3_cs.ns(|| "2 * b2"))? - .double(v3_cs.ns(|| "4 * b2"))?; - let b0_plus_2_b1_plus_4_b2 = other - .c0 - .add(v3_cs.ns(|| "b0 + 2b1"), &b1_double)? - .add(v3_cs.ns(|| "b0 + 2b1 + 4b2"), &b2_quad)?; - - a0_plus_2_a1_plus_4_a2.mul( - v3_cs.ns(|| "(a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2)"), - &b0_plus_2_b1_plus_4_b2, - )? - }; - - // v4 = a(∞)b(∞) = a2 * b2 - let v4 = self.c2.mul(cs.ns(|| "v2: a2 * b2"), &other.c2)?; - - let two = ::Fp::one().double(); - let six = two.double() + &two; - let mut two_and_six = [two, six]; - algebra::fields::batch_inversion(&mut two_and_six); - let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); - - let half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?; - let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?; - let one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_six"), &six_inverse)?; - let one_sixth_v3 = v3.mul_by_fp_constant(cs.ns(|| "v3_by_six"), &six_inverse)?; - let two_v4 = v4.double(cs.ns(|| "2 * v4"))?; - - // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) - let c0 = { - let c0_cs = &mut cs.ns(|| "c0"); - - // No constraints, only get a linear combination back. - let temp = half_v0 - .sub(c0_cs.ns(|| "sub1"), &half_v1)? - .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? - .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? - .sub(c0_cs.ns(|| "sub4"), &two_v4)?; - let non_residue_times_inner = - temp.mul_by_constant(&mut c0_cs.ns(|| "mul5"), &P::NONRESIDUE)?; - v0.add(c0_cs.ns(|| "add6"), &non_residue_times_inner)? - }; - - // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 - let c1 = { - let c1_cs = &mut cs.ns(|| "c1"); - let one_third_v2 = one_sixth_v2.double(&mut c1_cs.ns(|| "v2_by_3"))?; - let non_residue_v4 = - v4.mul_by_constant(&mut c1_cs.ns(|| "mul_by_beta"), &P::NONRESIDUE)?; - - let result = half_v0 - .negate(c1_cs.ns(|| "neg1"))? - .add(c1_cs.ns(|| "add2"), &v1)? - .sub(c1_cs.ns(|| "sub3"), &one_third_v2)? - .sub(c1_cs.ns(|| "sub4"), &one_sixth_v3)? - .add(c1_cs.ns(|| "sub5"), &two_v4)? - .add(c1_cs.ns(|| "sub6"), &non_residue_v4)?; - result - }; - - // -v0 + (1/2)v1 + (1/2)v2 −v4 - let c2 = { - let c2_cs = &mut cs.ns(|| "c2"); - let half_v2 = v2.mul_by_fp_constant(&mut c2_cs.ns(|| "mul1"), &two_inverse)?; - let result = half_v1 - .add(c2_cs.ns(|| "add1"), &half_v2)? - .sub(c2_cs.ns(|| "sub1"), &v4)? - .sub(c2_cs.ns(|| "sub2"), &v0)?; - result - }; - - Ok(Self::new(c0, c1, c2)) - } - - /// Use the Chung-Hasan asymmetric squaring formula. - /// - /// (Devegili OhEig Scott Dahab --- Multiplication and Squaring on - /// Abstract Pairing-Friendly - /// Fields.pdf; Section 4 (CH-SQR2)) - #[inline] - fn square>( - &self, - mut cs: CS, - ) -> Result { - let a = self.c0.clone(); - let b = self.c1.clone(); - let c = self.c2.clone(); - - let s0 = a.square(cs.ns(|| "s0"))?; - let ab = a.mul(cs.ns(|| "ab"), &b)?; - let s1 = ab.double(cs.ns(|| "s1"))?; - let s2 = a - .sub(cs.ns(|| "a-b"), &b)? - .add(cs.ns(|| "plus c"), &c)? - .square(cs.ns(|| "s2"))?; - let s3 = b.mul(cs.ns(|| "bc"), &c)?.double(cs.ns(|| "s3"))?; - let s4 = c.square(cs.ns(|| "s4"))?; - - let c0 = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "c0 part 1"), &s3)? - .add(cs.ns(|| "c0"), &s0)?; - - let c1 = Self::mul_fp2_gadget_by_nonresidue(cs.ns(|| "c1 part 1"), &s4)? - .add(cs.ns(|| "c1"), &s1)?; - - let c2 = s1 - .add(cs.ns(|| "c2 part1"), &s2)? - .add(cs.ns(|| "c2 part2"), &s3)? - .sub(cs.ns(|| "c2 part3"), &s0)? - .sub(cs.ns(|| "c2 part4"), &s4)?; - - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - // Karatsuba multiplication for Fp3: - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // v2 = A.c2 * B.c2 - // result.c0 = v0 + β((a1 + a2)(b1 + b2) − v1 − v2) - // result.c1 = (a0 + a1)(b0 + b1) − v0 − v1 + βv2 - // result.c2 = (a0 + a2)(b0 + b2) − v0 + v1 − v2, - // We enforce this with six constraints: - // - // v0 = A.c0 * B.c0 - // v1 = A.c1 * B.c1 - // v2 = A.c2 * B.c2 - // - // result.c0 - v0 + \beta*(v1 + v2) = β(a1 + a2)(b1 + b2)) - // result.c1 + v0 + v1 - βv2 = (a0 + a1)(b0 + b1) - // result.c2 + v0 - v1 + v2 = (a0 + a2)(b0 + b2) - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - // - // This implementation adapted from - // https://github.com/ZencashOfficial/ginger-lib/blob/development/r1cs/gadgets/std/src/fields/fp3.rs - let v0 = self.c0.mul(cs.ns(|| "v0 = a0 * b0"), &other.c0)?; - let v1 = self.c1.mul(cs.ns(|| "v1 = a1 * b1"), &other.c1)?; - let v2 = self.c2.mul(cs.ns(|| "v2 = a2 * b2"), &other.c2)?; - - // Check c0 - let nr_a1_plus_a2 = self - .c1 - .add(cs.ns(|| "a1 + a2"), &self.c2)? - .mul_by_constant(cs.ns(|| "nr*(a1 + a2)"), &P::NONRESIDUE)?; - let b1_plus_b2 = other.c1.add(cs.ns(|| "b1 + b2"), &other.c2)?; - let nr_v1 = v1.mul_by_constant(cs.ns(|| "nr * v1"), &P::NONRESIDUE)?; - let nr_v2 = v2.mul_by_constant(cs.ns(|| "nr * v2"), &P::NONRESIDUE)?; - let to_check = result - .c0 - .sub(cs.ns(|| "c0 - v0"), &v0)? - .add(cs.ns(|| "c0 - v0 + nr * v1"), &nr_v1)? - .add(cs.ns(|| "c0 - v0 + nr * v1 + nr * v2"), &nr_v2)?; - nr_a1_plus_a2.mul_equals(cs.ns(|| "check c0"), &b1_plus_b2, &to_check)?; - - // Check c1 - let a0_plus_a1 = self.c0.add(cs.ns(|| "a0 + a1"), &self.c1)?; - let b0_plus_b1 = other.c0.add(cs.ns(|| "b0 + b1"), &other.c1)?; - let to_check = result - .c1 - .sub(cs.ns(|| "c1 - nr * v2"), &nr_v2)? - .add(cs.ns(|| "c1 - nr * v2 + v0"), &v0)? - .add(cs.ns(|| "c1 - nr * v2 + v0 + v1"), &v1)?; - a0_plus_a1.mul_equals(cs.ns(|| "check c1"), &b0_plus_b1, &to_check)?; - - // Check c2 - let a0_plus_a2 = self.c0.add(cs.ns(|| "a0 + a2"), &self.c2)?; - let b0_plus_b2 = other.c0.add(cs.ns(|| "b0 + b2"), &other.c2)?; - let to_check = result - .c2 - .add(cs.ns(|| "c2 + v0"), &v0)? - .sub(cs.ns(|| "c2 + v0 - v1"), &v1)? - .add(cs.ns(|| "c2 + v0 - v1 + v2"), &v2)?; - a0_plus_a2.mul_equals(cs.ns(|| "check c2"), &b0_plus_b2, &to_check)?; - Ok(()) - } - - #[inline] - fn add_constant>( - &self, - mut cs: CS, - other: &Fp6

, - ) -> Result { - let c0 = self.c0.add_constant(cs.ns(|| "c0"), &other.c0)?; - let c1 = self.c1.add_constant(cs.ns(|| "c1"), &other.c1)?; - let c2 = self.c2.add_constant(cs.ns(|| "c2"), &other.c2)?; - - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn add_constant_in_place>( - &mut self, - mut cs: CS, - other: &Fp6

, - ) -> Result<&mut Self, SynthesisError> { - self.c0.add_constant_in_place(cs.ns(|| "c0"), &other.c0)?; - self.c1.add_constant_in_place(cs.ns(|| "c1"), &other.c1)?; - self.c2.add_constant_in_place(cs.ns(|| "c2"), &other.c2)?; - Ok(self) - } - - /// Use the Toom-Cook-3x method to compute multiplication. - #[inline] - fn mul_by_constant>( - &self, - mut cs: CS, - other: &Fp6

, - ) -> Result { - // Uses Toom-Cook-3x multiplication from - // - // Reference: - // "Multiplication and Squaring on Pairing-Friendly Fields" - // Devegili, OhEigeartaigh, Scott, Dahab - - // v0 = a(0)b(0) = a0 * b0 - let v0 = self.c0.mul_by_constant(cs.ns(|| "v0"), &other.c0)?; - - // v1 = a(1)b(1) = (a0 + a1 + a2)(b0 + b1 + b2) - let v1 = { - let mut v1_cs = cs.ns(|| "v1"); - let mut a0_plus_a1_plus_a2 = self - .c0 - .add(v1_cs.ns(|| "a0 + a1"), &self.c1)? - .add(v1_cs.ns(|| "a0 + a1 + a2"), &self.c2)?; - let b0_plus_b1_plus_b2 = other.c0 + &other.c1 + &other.c2; - - a0_plus_a1_plus_a2.mul_by_constant_in_place( - v1_cs.ns(|| "(a0 + a1 + a2)*(b0 + b1 + b2)"), - &b0_plus_b1_plus_b2, - )?; - a0_plus_a1_plus_a2 - }; - - // v2 = a(−1)b(−1) = (a0 − a1 + a2)(b0 − b1 + b2) - let mut v2 = { - let mut v2_cs = cs.ns(|| "v2"); - let mut a0_minus_a1_plus_a2 = self - .c0 - .sub(v2_cs.ns(|| "sub1"), &self.c1)? - .add(v2_cs.ns(|| "add2"), &self.c2)?; - let b0_minus_b1_plus_b2 = other.c0 - &other.c1 + &other.c2; - a0_minus_a1_plus_a2.mul_by_constant_in_place( - v2_cs.ns(|| "(a0 - a1 + a2)*(b0 - b1 + b2)"), - &b0_minus_b1_plus_b2, - )?; - a0_minus_a1_plus_a2 - }; - - // v3 = a(2)b(2) = (a0 + 2a1 + 4a2)(b0 + 2b1 + 4b2) - let mut v3 = { - let mut v3_cs = cs.ns(|| "v3"); - let a1_double = self.c1.double(v3_cs.ns(|| "2a1"))?; - let a2_quad = self - .c2 - .double(v3_cs.ns(|| "2a2"))? - .double(v3_cs.ns(|| "4a2"))?; - let mut a0_plus_2_a1_plus_4_a2 = self - .c0 - .add(v3_cs.ns(|| "a0 + 2a1"), &a1_double)? - .add(v3_cs.ns(|| "a0 + 2a1 + 4a2"), &a2_quad)?; - - let b1_double = other.c1.double(); - let b2_quad = other.c2.double().double(); - let b0_plus_2_b1_plus_4_b2 = other.c0 + &b1_double + &b2_quad; - - a0_plus_2_a1_plus_4_a2.mul_by_constant_in_place( - v3_cs.ns(|| "(a0 + 2a1 + 4a2)*(b0 + 2b1 + 4b2)"), - &b0_plus_2_b1_plus_4_b2, - )?; - a0_plus_2_a1_plus_4_a2 - }; - - // v4 = a(∞)b(∞) = a2 * b2 - let v4 = self.c2.mul_by_constant(cs.ns(|| "v4"), &other.c2)?; - - let two = ::Fp::one().double(); - let six = two.double() + &two; - let mut two_and_six = [two, six]; - algebra::fields::batch_inversion(&mut two_and_six); - let (two_inverse, six_inverse) = (two_and_six[0], two_and_six[1]); - - let mut half_v0 = v0.mul_by_fp_constant(cs.ns(|| "half_v0"), &two_inverse)?; - let half_v1 = v1.mul_by_fp_constant(cs.ns(|| "half_v1"), &two_inverse)?; - let mut one_sixth_v2 = v2.mul_by_fp_constant(cs.ns(|| "v2_by_6"), &six_inverse)?; - let one_sixth_v3 = v3.mul_by_fp_constant_in_place(cs.ns(|| "v3_by_6"), &six_inverse)?; - let two_v4 = v4.double(cs.ns(|| "2v4"))?; - - // c0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) - let c0 = { - let mut c0_cs = cs.ns(|| "c0"); - - // No constraints, only get a linear combination back. - let mut inner = half_v0 - .sub(c0_cs.ns(|| "sub1"), &half_v1)? - .sub(c0_cs.ns(|| "sub2"), &one_sixth_v2)? - .add(c0_cs.ns(|| "add3"), &one_sixth_v3)? - .sub(c0_cs.ns(|| "sub4"), &two_v4)?; - let non_residue_times_inner = - inner.mul_by_constant_in_place(&mut c0_cs, &P::NONRESIDUE)?; - v0.add(c0_cs.ns(|| "add5"), non_residue_times_inner)? - }; - - // −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 - let c1 = { - let mut c1_cs = cs.ns(|| "c1"); - let one_third_v2 = one_sixth_v2.double_in_place(c1_cs.ns(|| "double1"))?; - let non_residue_v4 = - v4.mul_by_constant(c1_cs.ns(|| "mul_by_const1"), &P::NONRESIDUE)?; - - half_v0 - .negate_in_place(c1_cs.ns(|| "neg1"))? - .add(c1_cs.ns(|| "add1"), &v1)? - .sub(c1_cs.ns(|| "sub2"), one_third_v2)? - .sub(c1_cs.ns(|| "sub3"), &one_sixth_v3)? - .add(c1_cs.ns(|| "add4"), &two_v4)? - .add(c1_cs.ns(|| "add5"), &non_residue_v4)? - }; - - // -v0 + (1/2)v1 + (1/2)v2 −v4 - let c2 = { - let mut c2_cs = cs.ns(|| "c2"); - let half_v2 = v2.mul_by_fp_constant_in_place(c2_cs.ns(|| "half_v2"), &two_inverse)?; - half_v1 - .add(c2_cs.ns(|| "add1"), half_v2)? - .sub(c2_cs.ns(|| "sub2"), &v4)? - .sub(c2_cs.ns(|| "sub3"), &v0)? - }; - - Ok(Self::new(c0, c1, c2)) - } - - fn frobenius_map>( - &self, - cs: CS, - power: usize, - ) -> Result { - let mut result = self.clone(); - result.frobenius_map_in_place(cs, power)?; - Ok(result) - } - - fn frobenius_map_in_place>( - &mut self, - mut cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - self.c0.frobenius_map_in_place(&mut cs.ns(|| "c0"), power)?; - self.c1.frobenius_map_in_place(&mut cs.ns(|| "c1"), power)?; - self.c2.frobenius_map_in_place(&mut cs.ns(|| "c2"), power)?; - - self.c1.mul_by_constant_in_place( - cs.ns(|| "c1_power"), - &P::FROBENIUS_COEFF_FP6_C1[power % 6], - )?; - self.c2.mul_by_constant_in_place( - cs.ns(|| "c2_power"), - &P::FROBENIUS_COEFF_FP6_C2[power % 6], - )?; - - Ok(self) - } - - fn cost_of_mul() -> usize { - 5 * Fp2Gadget::::cost_of_mul() - } - - fn cost_of_mul_equals() -> usize { - 6 * Fp2Gadget::::cost_of_mul() - } -} - -impl PartialEq for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn eq(&self, other: &Self) -> bool { - self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2 - } -} - -impl Eq for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ -} - -impl EqGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ -} - -impl ConditionalEqGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn conditional_enforce_equal>( - &self, - mut cs: CS, - other: &Self, - condition: &Boolean, - ) -> Result<(), SynthesisError> { - self.c0 - .conditional_enforce_equal(&mut cs.ns(|| "c0"), &other.c0, condition)?; - self.c1 - .conditional_enforce_equal(&mut cs.ns(|| "c1"), &other.c1, condition)?; - self.c2 - .conditional_enforce_equal(&mut cs.ns(|| "c2"), &other.c2, condition)?; - Ok(()) - } - - fn cost() -> usize { - 3 * as ConditionalEqGadget>::cost() - } -} - -impl NEqGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn enforce_not_equal>( - &self, - mut cs: CS, - other: &Self, - ) -> Result<(), SynthesisError> { - self.c0.enforce_not_equal(&mut cs.ns(|| "c0"), &other.c0)?; - self.c1.enforce_not_equal(&mut cs.ns(|| "c1"), &other.c1)?; - self.c2.enforce_not_equal(&mut cs.ns(|| "c2"), &other.c2)?; - Ok(()) - } - - fn cost() -> usize { - 3 * as NEqGadget>::cost() - } -} - -impl ToBitsGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn to_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bits(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_bits(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } - - fn to_non_unique_bits>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bits(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bits(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_non_unique_bits(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } -} - -impl ToBytesGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn to_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_bytes(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_bytes(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } - - fn to_non_unique_bytes>( - &self, - mut cs: CS, - ) -> Result, SynthesisError> { - let mut c0 = self.c0.to_non_unique_bytes(cs.ns(|| "c0"))?; - let mut c1 = self.c1.to_non_unique_bytes(cs.ns(|| "c1"))?; - let mut c2 = self.c2.to_non_unique_bytes(cs.ns(|| "c2"))?; - - c0.append(&mut c1); - c0.append(&mut c2); - - Ok(c0) - } -} - -impl Clone for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - fn clone(&self) -> Self { - Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone()) - } -} - -impl CondSelectGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn conditionally_select>( - mut cs: CS, - cond: &Boolean, - true_value: &Self, - false_value: &Self, - ) -> Result { - let c0 = Fp2Gadget::::conditionally_select( - &mut cs.ns(|| "c0"), - cond, - &true_value.c0, - &false_value.c0, - )?; - let c1 = Fp2Gadget::::conditionally_select( - &mut cs.ns(|| "c1"), - cond, - &true_value.c1, - &false_value.c1, - )?; - let c2 = Fp2Gadget::::conditionally_select( - &mut cs.ns(|| "c2"), - cond, - &true_value.c2, - &false_value.c2, - )?; - - Ok(Self::new(c0, c1, c2)) - } - - fn cost() -> usize { - 3 * as CondSelectGadget>::cost() - } -} - -impl TwoBitLookupGadget for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - type TableConstant = Fp6

; - fn two_bit_lookup>( - mut cs: CS, - b: &[Boolean], - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c2s = c.iter().map(|f| f.c2).collect::>(); - let c0 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?; - let c1 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?; - let c2 = Fp2Gadget::::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?; - Ok(Self::new(c0, c1, c2)) - } - - fn cost() -> usize { - 3 * as TwoBitLookupGadget>::cost() - } -} - -impl ThreeBitCondNegLookupGadget - for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - type TableConstant = Fp6

; - - fn three_bit_cond_neg_lookup>( - mut cs: CS, - b: &[Boolean], - b0b1: &Boolean, - c: &[Self::TableConstant], - ) -> Result { - let c0s = c.iter().map(|f| f.c0).collect::>(); - let c1s = c.iter().map(|f| f.c1).collect::>(); - let c2s = c.iter().map(|f| f.c2).collect::>(); - let c0 = Fp2Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c0"), - b, - b0b1, - &c0s, - )?; - let c1 = Fp2Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c1"), - b, - b0b1, - &c1s, - )?; - let c2 = Fp2Gadget::::three_bit_cond_neg_lookup( - cs.ns(|| "Lookup c2"), - b, - b0b1, - &c2s, - )?; - Ok(Self::new(c0, c1, c2)) - } - - fn cost() -> usize { - 3 * as ThreeBitCondNegLookupGadget>::cost() - } -} - -impl AllocGadget, ConstraintF> for Fp6Gadget -where - P: Fp6Parameters, - P::Fp2Params: Fp2Parameters, -{ - #[inline] - fn alloc_constant>( - mut cs: CS, - t: T, - ) -> Result - where - T: Borrow>, - { - Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow()) - } - - #[inline] - fn alloc>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1, c2) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = Fp2Gadget::::alloc(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp2Gadget::::alloc(&mut cs.ns(|| "c1"), || c1)?; - let c2 = Fp2Gadget::::alloc(&mut cs.ns(|| "c2"), || c2)?; - Ok(Self::new(c0, c1, c2)) - } - - #[inline] - fn alloc_input>( - mut cs: CS, - value_gen: F, - ) -> Result - where - F: FnOnce() -> Result, - T: Borrow>, - { - let (c0, c1, c2) = match value_gen() { - Ok(fe) => { - let fe = *fe.borrow(); - (Ok(fe.c0), Ok(fe.c1), Ok(fe.c2)) - } - _ => ( - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - Err(SynthesisError::AssignmentMissing), - ), - }; - - let c0 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c0"), || c0)?; - let c1 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c1"), || c1)?; - let c2 = Fp2Gadget::::alloc_input(&mut cs.ns(|| "c2"), || c2)?; - Ok(Self::new(c0, c1, c2)) +impl MulAssign> for Fp6Var

{ + fn mul_assign(&mut self, other: Fp2) { + self.c0 *= other; + self.c1 *= other; + self.c2 *= other; } } diff --git a/r1cs-std/src/fields/mod.rs b/r1cs-std/src/fields/mod.rs index a4ea67e..a733f13 100644 --- a/r1cs-std/src/fields/mod.rs +++ b/r1cs-std/src/fields/mod.rs @@ -1,9 +1,15 @@ -use algebra::{fields::BitIterator, Field, PrimeField, Vec}; -use core::fmt::Debug; -use r1cs_core::{ConstraintSystem, SynthesisError}; +use algebra::{prelude::*, BitIterator}; +use core::{ + fmt::Debug, + ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}, +}; +use r1cs_core::SynthesisError; use crate::{prelude::*, Assignment}; +pub mod cubic_extension; +pub mod quadratic_extension; + pub mod fp; pub mod fp12; pub mod fp2; @@ -12,278 +18,146 @@ pub mod fp4; pub mod fp6_2over3; pub mod fp6_3over2; -use crate::fields::fp::FpGadget; -pub trait ToConstraintFieldGadget { - fn to_constraint_field>( - &self, - cs: CS, - ) -> Result>, SynthesisError>; +/// A hack used to work around the lack of implied bounds. +pub trait FieldOpsBounds<'a, F, T: 'a>: + Sized + + Add<&'a T, Output = T> + + Sub<&'a T, Output = T> + + Mul<&'a T, Output = T> + + Add + + Sub + + Mul + + Add + + Sub + + Mul +{ } -pub trait FieldGadget: - Sized +/// A variable representing a field. Corresponds to the native type `F`. +pub trait FieldVar: + 'static + Clone + + From> + + R1CSVar + EqGadget - + NEqGadget - + ConditionalEqGadget + ToBitsGadget - + AllocGadget + + AllocVar + ToBytesGadget + CondSelectGadget - + TwoBitLookupGadget - + ThreeBitCondNegLookupGadget + + for<'a> FieldOpsBounds<'a, F, Self> + + for<'a> AddAssign<&'a Self> + + for<'a> SubAssign<&'a Self> + + for<'a> MulAssign<&'a Self> + + AddAssign + + SubAssign + + MulAssign + + AddAssign + + SubAssign + + MulAssign + Debug { - type Variable: Clone + Debug; - - fn get_value(&self) -> Option; - - fn get_variable(&self) -> Self::Variable; - - fn zero>(_: CS) -> Result; - - fn one>(_: CS) -> Result; + fn zero() -> Self; - fn conditionally_add_constant>( - &self, - _: CS, - _: &Boolean, - _: F, - ) -> Result; - - fn add>( - &self, - _: CS, - _: &Self, - ) -> Result; - - fn add_in_place>( - &mut self, - cs: CS, - other: &Self, - ) -> Result<&mut Self, SynthesisError> { - *self = self.add(cs, other)?; - Ok(self) + fn is_zero(&self) -> Result, SynthesisError> { + self.is_eq(&Self::zero()) } - fn double>(&self, cs: CS) -> Result { - self.add(cs, &self) - } - - fn double_in_place>( - &mut self, - cs: CS, - ) -> Result<&mut Self, SynthesisError> { - *self = self.double(cs)?; - Ok(self) - } + fn one() -> Self; - fn sub>( - &self, - _: CS, - _: &Self, - ) -> Result; - - fn sub_in_place>( - &mut self, - cs: CS, - other: &Self, - ) -> Result<&mut Self, SynthesisError> { - *self = self.sub(cs, other)?; - Ok(self) + fn is_one(&self) -> Result, SynthesisError> { + self.is_eq(&Self::one()) } - fn negate>(&self, _: CS) -> Result; + fn constant(v: F) -> Self; - #[inline] - fn negate_in_place>( - &mut self, - cs: CS, - ) -> Result<&mut Self, SynthesisError> { - *self = self.negate(cs)?; - Ok(self) + fn double(&self) -> Result { + Ok(self.clone() + self) } - fn mul>( - &self, - _: CS, - _: &Self, - ) -> Result; - - fn mul_in_place>( - &mut self, - cs: CS, - other: &Self, - ) -> Result<&mut Self, SynthesisError> { - *self = self.mul(cs, other)?; + fn double_in_place(&mut self) -> Result<&mut Self, SynthesisError> { + *self += self.double()?; Ok(self) } - fn square>(&self, cs: CS) -> Result { - self.mul(cs, &self) - } + fn negate(&self) -> Result; - fn square_in_place>( - &mut self, - cs: CS, - ) -> Result<&mut Self, SynthesisError> { - *self = self.square(cs)?; + #[inline] + fn negate_in_place(&mut self) -> Result<&mut Self, SynthesisError> { + *self = self.negate()?; Ok(self) } - fn mul_equals>( - &self, - mut cs: CS, - other: &Self, - result: &Self, - ) -> Result<(), SynthesisError> { - let actual_result = self.mul(cs.ns(|| "calc_actual_result"), other)?; - result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result) - } - - fn square_equals>( - &self, - mut cs: CS, - result: &Self, - ) -> Result<(), SynthesisError> { - let actual_result = self.square(cs.ns(|| "calc_actual_result"))?; - result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result) + fn square(&self) -> Result { + Ok(self.clone() * self) } - fn add_constant>( - &self, - _: CS, - _: &F, - ) -> Result; - - fn add_constant_in_place>( - &mut self, - cs: CS, - other: &F, - ) -> Result<&mut Self, SynthesisError> { - *self = self.add_constant(cs, other)?; + fn square_in_place(&mut self) -> Result<&mut Self, SynthesisError> { + *self = self.square()?; Ok(self) } - fn sub_constant>( - &self, - cs: CS, - fe: &F, - ) -> Result { - self.add_constant(cs, &(-(*fe))) + /// Enforce that `self * other == result`. + fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { + let actual_result = self.clone() * other; + result.enforce_equal(&actual_result) } - fn sub_constant_in_place>( - &mut self, - cs: CS, - other: &F, - ) -> Result<&mut Self, SynthesisError> { - self.add_constant_in_place(cs, &(-(*other))) + /// Enforce that `self * self == result`. + fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> { + let actual_result = self.square()?; + result.enforce_equal(&actual_result) } - fn mul_by_constant>( - &self, - _: CS, - _: &F, - ) -> Result; - - fn mul_by_constant_in_place>( - &mut self, - cs: CS, - other: &F, - ) -> Result<&mut Self, SynthesisError> { - *self = self.mul_by_constant(cs, other)?; - Ok(self) - } - - fn inverse>( - &self, - mut cs: CS, - ) -> Result { - let one = Self::one(&mut cs.ns(|| "one"))?; - let inverse = Self::alloc(&mut cs.ns(|| "alloc inverse"), || { - self.get_value().and_then(|val| val.inverse()).get() + fn inverse(&self) -> Result; + + /// Returns (self / denominator), but requires fewer constraints than + /// self * denominator.inverse() + /// It is up to the caller to ensure that denominator is non-zero, + /// since in that case the result is unconstrained. + fn mul_by_inverse(&self, denominator: &Self) -> Result { + let result = Self::new_witness(self.cs().unwrap(), || { + let denominator_inv_native = denominator.value()?.inverse().get()?; + let result = self.value()? * &denominator_inv_native; + Ok(result) })?; - self.mul_equals(cs.ns(|| "check inv"), &inverse, &one)?; - Ok(inverse) - } - - // Returns (self / denominator), but requires fewer constraints than - // self * denominator.inverse() - // It is up to the caller to ensure that denominator is non-zero, - // since in that case the result is unconstrained. - fn mul_by_inverse>( - &self, - mut cs: CS, - denominator: &Self, - ) -> Result { - let denominator_inv_native = denominator - .get_value() - .and_then(|val| val.inverse()) - .get()?; - let result_native = self.get_value().get()? * &denominator_inv_native; - - let result = Self::alloc(&mut cs.ns(|| "alloc mul_by_inverse result"), || { - Ok(result_native) - })?; - result.mul_equals(cs.ns(|| "check mul_by_inverse"), &denominator, &self)?; + result.mul_equals(&denominator, &self)?; Ok(result) } - fn frobenius_map>( - &self, - _: CS, - power: usize, - ) -> Result; - - fn frobenius_map_in_place>( - &mut self, - cs: CS, - power: usize, - ) -> Result<&mut Self, SynthesisError> { - *self = self.frobenius_map(cs, power)?; + fn frobenius_map(&self, power: usize) -> Result; + + fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> { + *self = self.frobenius_map(power)?; Ok(self) } - /// Accepts as input a list of bits which, when interpreted in big-endian + /// Accepts as input a list of bits which, when interpreted in little-endian /// form, are a scalar. - #[inline] - fn pow>( - &self, - mut cs: CS, - bits: &[Boolean], - ) -> Result { - let mut res = Self::one(cs.ns(|| "Alloc result"))?; - for (i, bit) in bits.iter().enumerate() { - res = res.square(cs.ns(|| format!("Double {}", i)))?; - let tmp = res.mul(cs.ns(|| format!("Add {}-th base power", i)), self)?; - res = Self::conditionally_select( - cs.ns(|| format!("Conditional Select {}", i)), - bit, - &tmp, - &res, - )?; + // + // TODO: check that the input really should be in little-endian or not... + fn pow(&self, bits: &[Boolean]) -> Result { + let mut res = Self::one(); + for bit in bits.iter() { + res.square_in_place()?; + let tmp = res.clone() * self; + res = bit.select(&tmp, &res)?; } Ok(res) } - fn pow_by_constant, CS: ConstraintSystem>( - &self, - mut cs: CS, - exp: S, - ) -> Result { + fn pow_by_constant>(&self, exp: S) -> Result { let mut res = self.clone(); let mut found_one = false; - for (i, bit) in BitIterator::new(exp).enumerate() { + for bit in BitIterator::new(exp) { if found_one { - res = res.square(cs.ns(|| format!("square for bit {:?}", i)))?; + res = res.square()?; } if bit { if found_one { - res = res.mul(cs.ns(|| format!("mul for bit {:?}", i)), self)?; + res *= self; } found_one = true; } @@ -291,16 +165,6 @@ pub trait FieldGadget: Ok(res) } - - fn cost_of_mul() -> usize; - - fn cost_of_mul_equals() -> usize { - Self::cost_of_mul() + >::cost() - } - - fn cost_of_inv() -> usize { - Self::cost_of_mul_equals() - } } #[cfg(test)] @@ -308,267 +172,197 @@ pub(crate) mod tests { use rand::{self, SeedableRng}; use rand_xorshift::XorShiftRng; - use crate::{prelude::*, test_constraint_system::TestConstraintSystem, Vec}; + use crate::{fields::*, Vec}; use algebra::{test_rng, BitIterator, Field, UniformRand}; - use r1cs_core::ConstraintSystem; + use r1cs_core::{ConstraintSystem, SynthesisError}; #[allow(dead_code)] - pub(crate) fn field_test>() { - let mut cs = TestConstraintSystem::::new(); + pub(crate) fn field_test() -> Result<(), SynthesisError> + where + F: Field, + ConstraintF: Field, + AF: FieldVar, + AF: TwoBitLookupGadget, + for<'a> &'a AF: FieldOpsBounds<'a, F, AF>, + { + let cs = ConstraintSystem::::new_ref(); let mut rng = test_rng(); - let a_native = FE::rand(&mut rng); - let b_native = FE::rand(&mut rng); - let a = F::alloc(&mut cs.ns(|| "generate_a"), || Ok(a_native)).unwrap(); - let b = F::alloc(&mut cs.ns(|| "generate_b"), || Ok(b_native)).unwrap(); - let b_const = F::alloc_constant(&mut cs.ns(|| "generate_b_as_constant"), b_native).unwrap(); - - let zero = F::zero(cs.ns(|| "zero")).unwrap(); - let zero_native = zero.get_value().unwrap(); - zero.enforce_equal(&mut cs.ns(|| "zero_equals?"), &zero) - .unwrap(); - assert_eq!(zero, zero); - - let one = F::one(cs.ns(|| "one")).unwrap(); - let one_native = one.get_value().unwrap(); - assert_eq!(one, one); - one.enforce_equal(&mut cs.ns(|| "one_equals?"), &one) - .unwrap(); - assert_ne!(one, zero); - - let one_dup = zero.add(cs.ns(|| "zero_plus_one"), &one).unwrap(); - one_dup - .enforce_equal(&mut cs.ns(|| "one_plus_zero_equals"), &one) - .unwrap(); - assert_eq!(one_dup, one); - - let two = one.add(cs.ns(|| "one_plus_one"), &one).unwrap(); - two.enforce_equal(&mut cs.ns(|| "two_equals?"), &two) - .unwrap(); - assert_eq!(two, two); - assert_ne!(zero, two); - assert_ne!(one, two); - - // a == a - assert_eq!(a, a); + let a_native = F::rand(&mut rng); + let b_native = F::rand(&mut rng); + let a = AF::new_witness(cs.ns("generate_a"), || Ok(a_native))?; + let b = AF::new_witness(cs.ns("generate_b"), || Ok(b_native))?; + let b_const = AF::new_constant(cs.ns("b_as_constant"), b_native)?; + + let zero = AF::zero(); + let zero_native = zero.value()?; + zero.enforce_equal(&zero)?; + + let one = AF::one(); + let one_native = one.value()?; + one.enforce_equal(&one)?; + + one.enforce_not_equal(&zero)?; + + let one_dup = &zero + &one; + one_dup.enforce_equal(&one)?; + + let two = &one + &one; + two.enforce_equal(&two)?; + two.enforce_equal(&one.double()?)?; + two.enforce_not_equal(&one)?; + two.enforce_not_equal(&zero)?; // a + 0 = a - let a_plus_zero = a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap(); - assert_eq!(a_plus_zero, a); - assert_eq!(a_plus_zero.get_value().unwrap(), a_native); - a_plus_zero - .enforce_equal(&mut cs.ns(|| "a_plus_zero_equals?"), &a) - .unwrap(); + let a_plus_zero = &a + &zero; + assert_eq!(a_plus_zero.value()?, a_native); + a_plus_zero.enforce_equal(&a)?; + a_plus_zero.enforce_not_equal(&a.double()?)?; // a - 0 = a - let a_minus_zero = a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap(); - assert_eq!(a_minus_zero, a); - assert_eq!(a_minus_zero.get_value().unwrap(), a_native); - a_minus_zero - .enforce_equal(&mut cs.ns(|| "a_minus_zero_equals?"), &a) - .unwrap(); + let a_minus_zero = &a - &zero; + assert_eq!(a_minus_zero.value()?, a_native); + a_minus_zero.enforce_equal(&a)?; // a - a = 0 - let a_minus_a = a.sub(cs.ns(|| "a_minus_a"), &a).unwrap(); - assert_eq!(a_minus_a, zero); - assert_eq!(a_minus_a.get_value().unwrap(), zero_native); - a_minus_a - .enforce_equal(&mut cs.ns(|| "a_minus_a_equals?"), &zero) - .unwrap(); + let a_minus_a = &a - &a; + assert_eq!(a_minus_a.value()?, zero_native); + a_minus_a.enforce_equal(&zero)?; // a + b = b + a - let a_b = a.add(cs.ns(|| "a_plus_b"), &b).unwrap(); - let b_a = b.add(cs.ns(|| "b_plus_a"), &a).unwrap(); - assert_eq!(a_b, b_a); - assert_eq!(a_b.get_value().unwrap(), a_native + &b_native); - a_b.enforce_equal(&mut cs.ns(|| "a+b == b+a"), &b_a) - .unwrap(); + let a_b = &a + &b; + let b_a = &b + &a; + assert_eq!(a_b.value()?, a_native + &b_native); + a_b.enforce_equal(&b_a)?; // (a + b) + a = a + (b + a) - let ab_a = a_b.add(cs.ns(|| "a_b_plus_a"), &a).unwrap(); - let a_ba = a.add(cs.ns(|| "a_plus_b_a"), &b_a).unwrap(); - assert_eq!(ab_a, a_ba); - assert_eq!(ab_a.get_value().unwrap(), a_native + &b_native + &a_native); - ab_a.enforce_equal(&mut cs.ns(|| "a+b + a == a+ b+a"), &a_ba) - .unwrap(); - - let b_times_a_plus_b = a_b.mul(cs.ns(|| "b * (a + b)"), &b).unwrap(); - let b_times_b_plus_a = b_a.mul(cs.ns(|| "b * (b + a)"), &b).unwrap(); - assert_eq!(b_times_b_plus_a, b_times_a_plus_b); + let ab_a = &a_b + &a; + let a_ba = &a + &b_a; + assert_eq!(ab_a.value()?, a_native + &b_native + &a_native); + ab_a.enforce_equal(&a_ba)?; + + let b_times_a_plus_b = &a_b * &b; + let b_times_b_plus_a = &b_a * &b; assert_eq!( - b_times_a_plus_b.get_value().unwrap(), + b_times_a_plus_b.value()?, b_native * &(b_native + &a_native) ); assert_eq!( - b_times_a_plus_b.get_value().unwrap(), + b_times_a_plus_b.value()?, (b_native + &a_native) * &b_native ); assert_eq!( - b_times_a_plus_b.get_value().unwrap(), + b_times_a_plus_b.value()?, (a_native + &b_native) * &b_native ); - b_times_b_plus_a - .enforce_equal(&mut cs.ns(|| "b*(a+b) == b * (b+a)"), &b_times_a_plus_b) - .unwrap(); - - // a * 0 = 0 - assert_eq!(a.mul(cs.ns(|| "a_times_zero"), &zero).unwrap(), zero); + b_times_b_plus_a.enforce_equal(&b_times_a_plus_b)?; // a * 1 = a - assert_eq!(a.mul(cs.ns(|| "a_times_one"), &one).unwrap(), a); - assert_eq!( - a.mul(cs.ns(|| "a_times_one2"), &one) - .unwrap() - .get_value() - .unwrap(), - a_native * &one_native - ); + assert_eq!((&a * &one).value()?, a_native * &one_native); // a * b = b * a - let ab = a.mul(cs.ns(|| "a_times_b"), &b).unwrap(); - let ba = b.mul(cs.ns(|| "b_times_a"), &a).unwrap(); - assert_eq!(ab, ba); - assert_eq!(ab.get_value().unwrap(), a_native * &b_native); + let ab = &a * &b; + let ba = &b * &a; + assert_eq!(ab.value()?, ba.value()?); + assert_eq!(ab.value()?, a_native * &b_native); - let ab_const = a.mul(cs.ns(|| "a_times_b_const"), &b_const).unwrap(); - let b_const_a = b_const.mul(cs.ns(|| "b_const_times_a"), &a).unwrap(); - assert_eq!(ab_const, b_const_a); - assert_eq!(ab_const, ab); - assert_eq!(ab_const.get_value().unwrap(), a_native * &b_native); + let ab_const = &a * &b_const; + let b_const_a = &b_const * &a; + assert_eq!(ab_const.value()?, b_const_a.value()?); + assert_eq!(ab_const.value()?, ab.value()?); + assert_eq!(ab_const.value()?, a_native * &b_native); // (a * b) * a = a * (b * a) - let ab_a = ab.mul(cs.ns(|| "ab_times_a"), &a).unwrap(); - let a_ba = a.mul(cs.ns(|| "a_times_ba"), &ba).unwrap(); - assert_eq!(ab_a, a_ba); - assert_eq!(ab_a.get_value().unwrap(), a_native * &b_native * &a_native); - - let aa = a.mul(cs.ns(|| "a * a"), &a).unwrap(); - let a_squared = a.square(cs.ns(|| "a^2")).unwrap(); - a_squared - .enforce_equal(&mut cs.ns(|| "a^2 == a*a"), &aa) - .unwrap(); - assert_eq!(aa, a_squared); - assert_eq!(aa.get_value().unwrap(), a_native.square()); - - let aa = a - .mul_by_constant(cs.ns(|| "a * a via mul_by_const"), &a.get_value().unwrap()) - .unwrap(); - a_squared - .enforce_equal(&mut cs.ns(|| "a^2 == a*a via mul_by_const"), &aa) - .unwrap(); - assert_eq!(aa, a_squared); - assert_eq!(aa.get_value().unwrap(), a_native.square()); - - let a_b2 = a - .add_constant(cs.ns(|| "a + b via add_const"), &b.get_value().unwrap()) - .unwrap(); - a_b.enforce_equal(&mut cs.ns(|| "a + b == a + b via add_const"), &a_b2) - .unwrap(); - assert_eq!(a_b, a_b2); - - let a_inv = a.inverse(cs.ns(|| "a_inv")).unwrap(); - a_inv - .mul_equals(cs.ns(|| "check a_inv * a = 1"), &a, &one) - .unwrap(); - assert_eq!( - a_inv.get_value().unwrap(), - a.get_value().unwrap().inverse().unwrap() - ); - assert_eq!(a_inv.get_value().unwrap(), a_native.inverse().unwrap()); - - let a_b_inv = a.mul_by_inverse(cs.ns(|| "a_b_inv"), &b).unwrap(); - a_b_inv - .mul_equals(cs.ns(|| "check a_b_inv * b = a"), &b, &a) - .unwrap(); - assert_eq!( - a_b_inv.get_value().unwrap(), - a_native * b_native.inverse().unwrap() - ); + let ab_a = &ab * &a; + let a_ba = &a * &ba; + assert_eq!(ab_a.value()?, a_ba.value()?); + assert_eq!(ab_a.value()?, a_native * &b_native * &a_native); + + let aa = &a * &a; + let a_squared = a.square()?; + a_squared.enforce_equal(&aa)?; + assert_eq!(aa.value()?, a_squared.value()?); + assert_eq!(aa.value()?, a_native.square()); + + let aa = &a * a.value()?; + a_squared.enforce_equal(&aa)?; + assert_eq!(aa.value()?, a_squared.value()?); + assert_eq!(aa.value()?, a_native.square()); + + let a_b2 = &a + b_native; + a_b.enforce_equal(&a_b2)?; + assert_eq!(a_b.value()?, a_b2.value()?); + + let a_inv = a.inverse()?; + a_inv.mul_equals(&a, &one)?; + assert_eq!(a_inv.value()?, a.value()?.inverse().unwrap()); + assert_eq!(a_inv.value()?, a_native.inverse().unwrap()); + + let a_b_inv = a.mul_by_inverse(&b)?; + a_b_inv.mul_equals(&b, &a)?; + assert_eq!(a_b_inv.value()?, a_native * b_native.inverse().unwrap()); // a * a * a = a^3 let bits = BitIterator::new([0x3]) .map(Boolean::constant) .collect::>(); - assert_eq!( - a_native * &(a_native * &a_native), - a.pow(cs.ns(|| "test_pow"), &bits) - .unwrap() - .get_value() - .unwrap() - ); + assert_eq!(a_native.pow([0x3]), a.pow(&bits)?.value()?); // a * a * a = a^3 - assert_eq!( - a_native * &(a_native * &a_native), - a.pow_by_constant(cs.ns(|| "test_constant_pow"), &[3]) - .unwrap() - .get_value() - .unwrap() - ); + assert_eq!(a_native.pow([0x3]), a.pow_by_constant(&[3])?.value()?); // a * a * a = a^3 - let mut constants = [FE::zero(); 4]; + let mut constants = [F::zero(); 4]; for c in &mut constants { *c = UniformRand::rand(&mut test_rng()); - println!("Current c[i]: {:?}", c); } - let bits = [Boolean::constant(false), Boolean::constant(true)]; - let lookup_result = - F::two_bit_lookup(cs.ns(|| "Lookup"), &bits, constants.as_ref()).unwrap(); - assert_eq!(lookup_result.get_value().unwrap(), constants[2]); - - let negone: FE = UniformRand::rand(&mut test_rng()); - - let n = F::alloc(&mut cs.ns(|| "alloc new var"), || Ok(negone)).unwrap(); - let _ = n.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap(); - let _ = n - .to_non_unique_bytes(&mut cs.ns(|| "ToBytes Strict")) - .unwrap(); - - let ab_false = a - .conditionally_add_constant( - cs.ns(|| "Add bool with coeff false"), - &Boolean::constant(false), - b_native, - ) - .unwrap(); - assert_eq!(ab_false.get_value().unwrap(), a_native); - let ab_true = a - .conditionally_add_constant( - cs.ns(|| "Add bool with coeff true"), - &Boolean::constant(true), - b_native, - ) - .unwrap(); - assert_eq!(ab_true.get_value().unwrap(), a_native + &b_native); - - if !cs.is_satisfied() { + let bits = [ + Boolean::::constant(false), + Boolean::constant(true), + ]; + let lookup_result = AF::two_bit_lookup(&bits, constants.as_ref())?; + assert_eq!(lookup_result.value()?, constants[2]); + + let negone: F = UniformRand::rand(&mut test_rng()); + + let n = AF::new_witness(cs.ns("alloc new var"), || Ok(negone)).unwrap(); + let _ = n.to_bytes()?; + let _ = n.to_non_unique_bytes()?; + + let ab_false = &a + (AF::from(Boolean::Constant(false)) * b_native); + assert_eq!(ab_false.value()?, a_native); + let ab_true = &a + (AF::from(Boolean::Constant(true)) * b_native); + assert_eq!(ab_true.value()?, a_native + &b_native); + + if !cs.is_satisfied().unwrap() { println!("{:?}", cs.which_is_unsatisfied().unwrap()); } - assert!(cs.is_satisfied()); + assert!(cs.is_satisfied().unwrap()); + Ok(()) } #[allow(dead_code)] - pub(crate) fn frobenius_tests< - FE: Field, - ConstraintF: Field, - F: FieldGadget, - >( + pub(crate) fn frobenius_tests( maxpower: usize, - ) { - let mut cs = TestConstraintSystem::::new(); + ) -> Result<(), SynthesisError> + where + F: Field, + ConstraintF: Field, + AF: FieldVar, + for<'a> &'a AF: FieldOpsBounds<'a, F, AF>, + { + let cs = ConstraintSystem::::new_ref(); let mut rng = XorShiftRng::seed_from_u64(1231275789u64); for i in 0..=maxpower { - let mut a = FE::rand(&mut rng); - let mut a_gadget = F::alloc(cs.ns(|| format!("a_gadget_{:?}", i)), || Ok(a)).unwrap(); - a_gadget = a_gadget - .frobenius_map(cs.ns(|| format!("frob_map_{}", i)), i) - .unwrap(); + let mut a = F::rand(&mut rng); + let mut a_gadget = AF::new_witness(cs.ns(format!("a_gadget_{:?}", i)), || Ok(a))?; + a_gadget.frobenius_map_in_place(i)?; a.frobenius_map(i); - assert_eq!(a_gadget.get_value().unwrap(), a); + assert_eq!(a_gadget.value()?, a); } - assert!(cs.is_satisfied()); + assert!(cs.is_satisfied().unwrap()); + Ok(()) } } diff --git a/r1cs-std/src/fields/quadratic_extension.rs b/r1cs-std/src/fields/quadratic_extension.rs new file mode 100644 index 0000000..7b3db2d --- /dev/null +++ b/r1cs-std/src/fields/quadratic_extension.rs @@ -0,0 +1,504 @@ +use algebra::{ + fields::{Field, QuadExtField, QuadExtParameters}, + One, +}; +use core::{borrow::Borrow, marker::PhantomData}; +use r1cs_core::{ConstraintSystemRef, Namespace, SynthesisError}; + +use crate::{ + fields::{FieldOpsBounds, FieldVar}, + prelude::*, + Assignment, Vec, +}; + +#[derive(Derivative)] +#[derivative(Debug(bound = "BF: core::fmt::Debug"), Clone(bound = "BF: Clone"))] +#[must_use] +pub struct QuadExtVar, P: QuadExtVarParams> +where + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, +{ + pub c0: BF, + pub c1: BF, + #[derivative(Debug = "ignore")] + _params: PhantomData

, +} + +pub trait QuadExtVarParams>: + QuadExtParameters +where + for<'a> &'a BF: FieldOpsBounds<'a, Self::BaseField, BF>, +{ + fn mul_base_field_var_by_frob_coeff(fe: &mut BF, power: usize); +} + +impl, P: QuadExtVarParams> QuadExtVar +where + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, +{ + pub fn new(c0: BF, c1: BF) -> Self { + Self { + c0, + c1, + _params: PhantomData, + } + } + + /// Multiply a BF by quadratic nonresidue P::NONRESIDUE. + #[inline] + pub fn mul_base_field_by_nonresidue(fe: &BF) -> Result { + Ok(fe * P::NONRESIDUE) + } + + #[inline] + pub fn mul_by_base_field_constant(&self, fe: P::BaseField) -> Self { + let c0 = self.c0.clone() * fe; + let c1 = self.c1.clone() * fe; + QuadExtVar::new(c0, c1) + } + + #[inline] + pub fn mul_assign_by_base_field_constant(&mut self, fe: P::BaseField) { + *self = (&*self).mul_by_base_field_constant(fe); + } + + /// This is only to be used when the element is *known* to be in the cyclotomic subgroup. + #[inline] + pub fn unitary_inverse(&self) -> Result { + Ok(Self::new(self.c0.clone(), self.c1.negate()?)) + } + + /// This is only to be used when the element is *known* to be in the cyclotomic subgroup. + #[inline] + pub fn cyclotomic_exp(&self, exponent: impl AsRef<[u64]>) -> Result + where + Self: FieldVar, P::BasePrimeField>, + { + use algebra::biginteger::arithmetic::find_wnaf; + let mut res = Self::one(); + let self_inverse = self.unitary_inverse()?; + + let mut found_nonzero = false; + let naf = find_wnaf(exponent.as_ref()); + + for &value in naf.iter().rev() { + if found_nonzero { + res.square_in_place()?; + } + + if value != 0 { + found_nonzero = true; + + if value > 0 { + res *= self; + } else { + res *= &self_inverse; + } + } + } + + Ok(res) + } +} + +impl R1CSVar for QuadExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: QuadExtVarParams, +{ + type Value = QuadExtField

; + + fn cs(&self) -> Option> { + [&self.c0, &self.c1].cs() + } + + #[inline] + fn value(&self) -> Result { + match (self.c0.value(), self.c1.value()) { + (Ok(c0), Ok(c1)) => Ok(QuadExtField::new(c0, c1)), + (..) => Err(SynthesisError::AssignmentMissing), + } + } +} + +impl From> for QuadExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: QuadExtVarParams, +{ + fn from(other: Boolean) -> Self { + let c0 = BF::from(other); + let c1 = BF::zero(); + Self::new(c0, c1) + } +} + +impl<'a, BF, P> FieldOpsBounds<'a, QuadExtField

, QuadExtVar> for QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ +} +impl<'a, BF, P> FieldOpsBounds<'a, QuadExtField

, QuadExtVar> for &'a QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ +} + +impl FieldVar, P::BasePrimeField> for QuadExtVar +where + BF: FieldVar, + for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>, + P: QuadExtVarParams, +{ + fn constant(other: QuadExtField

) -> Self { + let c0 = BF::constant(other.c0); + let c1 = BF::constant(other.c1); + Self::new(c0, c1) + } + + fn zero() -> Self { + let c0 = BF::zero(); + let c1 = BF::zero(); + Self::new(c0, c1) + } + + fn one() -> Self { + let c0 = BF::one(); + let c1 = BF::zero(); + Self::new(c0, c1) + } + + #[inline] + fn double(&self) -> Result { + let c0 = self.c0.double()?; + let c1 = self.c1.double()?; + Ok(Self::new(c0, c1)) + } + + #[inline] + fn negate(&self) -> Result { + let mut result = self.clone(); + result.c0.negate_in_place()?; + result.c1.negate_in_place()?; + Ok(result) + } + + #[inline] + fn square(&self) -> Result { + // From Libsnark/fp2_gadget.tcc + // Complex multiplication for Fp2: + // "Multiplication and Squaring on Pairing-Friendly Fields" + // Devegili, OhEigeartaigh, Scott, Dahab + + // v0 = c0 - c1 + let mut v0 = &self.c0 - &self.c1; + // v3 = c0 - beta * c1 + let v3 = &self.c0 - &Self::mul_base_field_by_nonresidue(&self.c1)?; + // v2 = c0 * c1 + let v2 = &self.c0 * &self.c1; + + // v0 = (v0 * v3) + v2 + v0 *= &v3; + v0 += &v2; + + let c0 = &v0 + &Self::mul_base_field_by_nonresidue(&v2)?; + let c1 = v2.double()?; + + Ok(Self::new(c0, c1)) + } + + fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> { + // Karatsuba multiplication for Fp2: + // v0 = A.c0 * B.c0 + // v1 = A.c1 * B.c1 + // result.c0 = v0 + non_residue * v1 + // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 + // Enforced with 3 constraints: + // A.c1 * B.c1 = v1 + // A.c0 * B.c0 = result.c0 - non_residue * v1 + // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 + // Reference: + // "Multiplication and Squaring on Pairing-Friendly Fields" + // Devegili, OhEigeartaigh, Scott, Dahab + // Compute v1 + let v1 = &self.c1 * &other.c1; + + // Perform second check + let non_residue_times_v1 = Self::mul_base_field_by_nonresidue(&v1)?; + let rhs = &result.c0 - &non_residue_times_v1; + self.c0.mul_equals(&other.c0, &rhs)?; + + // Last check + let a0_plus_a1 = &self.c0 + &self.c1; + let b0_plus_b1 = &other.c0 + &other.c1; + let one_minus_non_residue_v1 = &v1 - &non_residue_times_v1; + + let tmp = &(&result.c1 + &result.c0) + &one_minus_non_residue_v1; + a0_plus_a1.mul_equals(&b0_plus_b1, &tmp)?; + + Ok(()) + } + + fn frobenius_map(&self, power: usize) -> Result { + let mut result = self.clone(); + result.c0.frobenius_map_in_place(power)?; + result.c1.frobenius_map_in_place(power)?; + P::mul_base_field_var_by_frob_coeff(&mut result.c1, power); + Ok(result) + } + + fn inverse(&self) -> Result { + let one = Self::new_constant(self.cs().get()?.clone(), QuadExtField::one())?; + let inverse = Self::new_witness(self.cs().get()?.clone(), || { + self.value().and_then(|val| val.inverse().get()) + })?; + self.mul_equals(&inverse, &one)?; + Ok(inverse) + } +} + +impl_bounded_ops!( + QuadExtVar, + QuadExtField

, + Add, + add, + AddAssign, + add_assign, + |this: &'a QuadExtVar, other: &'a QuadExtVar| { + let c0 = &this.c0 + &other.c0; + let c1 = &this.c1 + &other.c1; + QuadExtVar::new(c0, c1) + }, + |this: &'a QuadExtVar, other: QuadExtField

| { + this + QuadExtVar::constant(other) + }, + (BF: FieldVar, P: QuadExtVarParams), + for <'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF> +); +impl_bounded_ops!( + QuadExtVar, + QuadExtField

, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a QuadExtVar, other: &'a QuadExtVar| { + let c0 = &this.c0 - &other.c0; + let c1 = &this.c1 - &other.c1; + QuadExtVar::new(c0, c1) + }, + |this: &'a QuadExtVar, other: QuadExtField

| { + this - QuadExtVar::constant(other) + }, + (BF: FieldVar, P: QuadExtVarParams), + for <'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF> +); +impl_bounded_ops!( + QuadExtVar, + QuadExtField

, + Mul, + mul, + MulAssign, + mul_assign, + |this: &'a QuadExtVar, other: &'a QuadExtVar| { + // Karatsuba multiplication for Fp2: + // v0 = A.c0 * B.c0 + // v1 = A.c1 * B.c1 + // result.c0 = v0 + non_residue * v1 + // result.c1 = (A.c0 + A.c1) * (B.c0 + B.c1) - v0 - v1 + // Enforced with 3 constraints: + // A.c1 * B.c1 = v1 + // A.c0 * B.c0 = result.c0 - non_residue * v1 + // (A.c0+A.c1)*(B.c0+B.c1) = result.c1 + result.c0 + (1 - non_residue) * v1 + // Reference: + // "Multiplication and Squaring on Pairing-Friendly Fields" + // Devegili, OhEigeartaigh, Scott, Dahab + let mut result = this.clone(); + let v0 = &this.c0 * &other.c0; + let v1 = &this.c1 * &other.c1; + + result.c1 += &this.c0; + result.c1 *= &other.c0 + &other.c1; + result.c1 -= &v0; + result.c1 -= &v1; + result.c0 = v0 + &QuadExtVar::::mul_base_field_by_nonresidue(&v1).unwrap(); + result + }, + |this: &'a QuadExtVar, other: QuadExtField

| { + this * QuadExtVar::constant(other) + }, + (BF: FieldVar, P: QuadExtVarParams), + for <'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF> +); + +impl EqGadget for QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + fn is_eq(&self, other: &Self) -> Result, SynthesisError> { + let b0 = self.c0.is_eq(&other.c0)?; + let b1 = self.c1.is_eq(&other.c1)?; + b0.and(&b1) + } + + #[inline] + fn conditional_enforce_equal( + &self, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + self.c0.conditional_enforce_equal(&other.c0, condition)?; + self.c1.conditional_enforce_equal(&other.c1, condition)?; + Ok(()) + } + + #[inline] + fn conditional_enforce_not_equal( + &self, + other: &Self, + condition: &Boolean, + ) -> Result<(), SynthesisError> { + let is_equal = self.is_eq(other)?; + is_equal + .and(condition)? + .enforce_equal(&Boolean::Constant(false)) + } +} + +impl ToBitsGadget for QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + fn to_bits(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_bits()?; + let mut c1 = self.c1.to_bits()?; + c0.append(&mut c1); + Ok(c0) + } + + fn to_non_unique_bits(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_non_unique_bits()?; + let mut c1 = self.c1.to_non_unique_bits()?; + c0.append(&mut c1); + Ok(c0) + } +} + +impl ToBytesGadget for QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + fn to_bytes(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_bytes()?; + let mut c1 = self.c1.to_bytes()?; + c0.append(&mut c1); + Ok(c0) + } + + fn to_non_unique_bytes(&self) -> Result>, SynthesisError> { + let mut c0 = self.c0.to_non_unique_bytes()?; + let mut c1 = self.c1.to_non_unique_bytes()?; + c0.append(&mut c1); + Ok(c0) + } +} + +impl CondSelectGadget for QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + #[inline] + fn conditionally_select( + cond: &Boolean, + true_value: &Self, + false_value: &Self, + ) -> Result { + let c0 = BF::conditionally_select(cond, &true_value.c0, &false_value.c0)?; + let c1 = BF::conditionally_select(cond, &true_value.c1, &false_value.c1)?; + Ok(Self::new(c0, c1)) + } +} + +impl TwoBitLookupGadget for QuadExtVar +where + BF: FieldVar + + TwoBitLookupGadget, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + type TableConstant = QuadExtField

; + + fn two_bit_lookup( + b: &[Boolean], + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c0 = BF::two_bit_lookup(b, &c0s)?; + let c1 = BF::two_bit_lookup(b, &c1s)?; + Ok(Self::new(c0, c1)) + } +} + +impl ThreeBitCondNegLookupGadget for QuadExtVar +where + BF: FieldVar + + ThreeBitCondNegLookupGadget, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + type TableConstant = QuadExtField

; + + fn three_bit_cond_neg_lookup( + b: &[Boolean], + b0b1: &Boolean, + c: &[Self::TableConstant], + ) -> Result { + let c0s = c.iter().map(|f| f.c0).collect::>(); + let c1s = c.iter().map(|f| f.c1).collect::>(); + let c0 = BF::three_bit_cond_neg_lookup(b, b0b1, &c0s)?; + let c1 = BF::three_bit_cond_neg_lookup(b, b0b1, &c1s)?; + Ok(Self::new(c0, c1)) + } +} + +impl AllocVar, P::BasePrimeField> for QuadExtVar +where + BF: FieldVar, + for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>, + P: QuadExtVarParams, +{ + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + let ns = cs.into(); + let cs = ns.cs(); + let (c0, c1) = match f() { + Ok(fe) => (Ok(fe.borrow().c0), Ok(fe.borrow().c1)), + Err(_) => ( + Err(SynthesisError::AssignmentMissing), + Err(SynthesisError::AssignmentMissing), + ), + }; + + let c0 = BF::new_variable(cs.ns("c0"), || c0, mode)?; + let c1 = BF::new_variable(cs.ns("c1"), || c1, mode)?; + Ok(Self::new(c0, c1)) + } +}