mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-11 00:11:29 +01:00
Update field variables in r1cs-std
This commit is contained in:
521
r1cs-std/src/fields/cubic_extension.rs
Normal file
521
r1cs-std/src/fields/cubic_extension.rs
Normal file
@@ -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<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>>
|
||||||
|
where
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
{
|
||||||
|
pub c0: BF,
|
||||||
|
pub c1: BF,
|
||||||
|
pub c2: BF,
|
||||||
|
#[derivative(Debug = "ignore")]
|
||||||
|
_params: PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CubicExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
|
||||||
|
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<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>> CubicExtVar<BF, P>
|
||||||
|
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<BF, SynthesisError> {
|
||||||
|
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<BF, P> R1CSVar<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
type Value = CubicExtField<P>;
|
||||||
|
|
||||||
|
fn cs(&self) -> Option<ConstraintSystemRef<P::BasePrimeField>> {
|
||||||
|
[&self.c0, &self.c1, &self.c2].cs()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value(&self) -> Result<Self::Value, SynthesisError> {
|
||||||
|
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<BF, P> From<Boolean<P::BasePrimeField>> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn from(other: Boolean<P::BasePrimeField>) -> 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<P>, CubicExtVar<BF, P>> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
impl<'a, BF, P> FieldOpsBounds<'a, CubicExtField<P>, CubicExtVar<BF, P>> for &'a CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BF, P> FieldVar<CubicExtField<P>, P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn constant(other: CubicExtField<P>) -> 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<Self, SynthesisError> {
|
||||||
|
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<Self, SynthesisError> {
|
||||||
|
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<Self, SynthesisError> {
|
||||||
|
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<Self, SynthesisError> {
|
||||||
|
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<Self, SynthesisError> {
|
||||||
|
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<BF, P>,
|
||||||
|
CubicExtField<P>,
|
||||||
|
Add,
|
||||||
|
add,
|
||||||
|
AddAssign,
|
||||||
|
add_assign,
|
||||||
|
|this: &'a CubicExtVar<BF, P>, other: &'a CubicExtVar<BF, P>| {
|
||||||
|
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<BF, P>, other: CubicExtField<P>| {
|
||||||
|
this + CubicExtVar::constant(other)
|
||||||
|
},
|
||||||
|
(BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>),
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
);
|
||||||
|
impl_bounded_ops!(
|
||||||
|
CubicExtVar<BF, P>,
|
||||||
|
CubicExtField<P>,
|
||||||
|
Sub,
|
||||||
|
sub,
|
||||||
|
SubAssign,
|
||||||
|
sub_assign,
|
||||||
|
|this: &'a CubicExtVar<BF, P>, other: &'a CubicExtVar<BF, P>| {
|
||||||
|
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<BF, P>, other: CubicExtField<P>| {
|
||||||
|
this - CubicExtVar::constant(other)
|
||||||
|
},
|
||||||
|
(BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>),
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
);
|
||||||
|
impl_bounded_ops!(
|
||||||
|
CubicExtVar<BF, P>,
|
||||||
|
CubicExtField<P>,
|
||||||
|
Mul,
|
||||||
|
mul,
|
||||||
|
MulAssign,
|
||||||
|
mul_assign,
|
||||||
|
|this: &'a CubicExtVar<BF, P>, other: &'a CubicExtVar<BF, P>| {
|
||||||
|
// 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<BF, P>, other: CubicExtField<P>| {
|
||||||
|
this * CubicExtVar::constant(other)
|
||||||
|
},
|
||||||
|
(BF: FieldVar<P::BaseField, P::BasePrimeField>, P: CubicExtVarParams<BF>),
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<BF, P> EqGadget<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn is_eq(&self, other: &Self) -> Result<Boolean<P::BasePrimeField>, 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<P::BasePrimeField>,
|
||||||
|
) -> 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<P::BasePrimeField>,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
let is_equal = self.is_eq(other)?;
|
||||||
|
is_equal
|
||||||
|
.and(condition)?
|
||||||
|
.enforce_equal(&Boolean::Constant(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BF, P> ToBitsGadget<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn to_bits(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, 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<Vec<Boolean<P::BasePrimeField>>, 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<BF, P> ToBytesGadget<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn to_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, 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<Vec<UInt8<P::BasePrimeField>>, 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<BF, P> CondSelectGadget<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn conditionally_select(
|
||||||
|
cond: &Boolean<P::BasePrimeField>,
|
||||||
|
true_value: &Self,
|
||||||
|
false_value: &Self,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
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<BF, P> TwoBitLookupGadget<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>
|
||||||
|
+ TwoBitLookupGadget<P::BasePrimeField, TableConstant = P::BaseField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
type TableConstant = CubicExtField<P>;
|
||||||
|
|
||||||
|
fn two_bit_lookup(
|
||||||
|
b: &[Boolean<P::BasePrimeField>],
|
||||||
|
c: &[Self::TableConstant],
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||||
|
let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
|
||||||
|
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<BF, P> ThreeBitCondNegLookupGadget<P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>
|
||||||
|
+ ThreeBitCondNegLookupGadget<P::BasePrimeField, TableConstant = P::BaseField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
type TableConstant = CubicExtField<P>;
|
||||||
|
|
||||||
|
fn three_bit_cond_neg_lookup(
|
||||||
|
b: &[Boolean<P::BasePrimeField>],
|
||||||
|
b0b1: &Boolean<P::BasePrimeField>,
|
||||||
|
c: &[Self::TableConstant],
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||||
|
let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
|
||||||
|
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<BF, P> AllocVar<CubicExtField<P>, P::BasePrimeField> for CubicExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: CubicExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn new_variable<T: Borrow<CubicExtField<P>>>(
|
||||||
|
cs: impl Into<Namespace<P::BasePrimeField>>,
|
||||||
|
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||||
|
mode: AllocationMode,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,127 +1,89 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
boolean::Boolean,
|
boolean::Boolean,
|
||||||
fields::{fp::FpGadget, FieldGadget},
|
fields::{fp::FpVar, FieldVar},
|
||||||
|
prelude::*,
|
||||||
ToBitsGadget,
|
ToBitsGadget,
|
||||||
};
|
};
|
||||||
use algebra::PrimeField;
|
use algebra::PrimeField;
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
use r1cs_core::{lc, SynthesisError, Variable};
|
||||||
|
|
||||||
impl<F: PrimeField> FpGadget<F> {
|
impl<F: PrimeField> FpVar<F> {
|
||||||
/// 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
|
/// constraint system will not be satisfied otherwise. If `self` should
|
||||||
/// also be checked for equality, e.g. `a <= b` instead of `a < b`, set
|
/// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
|
||||||
/// `should_also_check_quality` to `true`. This variant verifies `a` and `b`
|
/// `should_also_check_quality` to `true`. This variant verifies `self` and `other`
|
||||||
/// are `<= (p-1)/2`.
|
/// are `<= (p-1)/2`.
|
||||||
pub fn enforce_cmp<CS: ConstraintSystem<F>>(
|
pub fn enforce_cmp(
|
||||||
&self,
|
&self,
|
||||||
mut cs: CS,
|
other: &FpVar<F>,
|
||||||
b: &FpGadget<F>,
|
|
||||||
ordering: Ordering,
|
ordering: Ordering,
|
||||||
should_also_check_equality: bool,
|
should_also_check_equality: bool,
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
let (left, right) = Self::process_cmp_inputs(
|
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||||
cs.ns(|| "process cmp inputs"),
|
left.enforce_smaller_than(&right)
|
||||||
&self,
|
|
||||||
b,
|
|
||||||
ordering,
|
|
||||||
should_also_check_equality,
|
|
||||||
)?;
|
|
||||||
Self::enforce_smaller_than(cs.ns(|| "enforce smaller than"), &left, &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
|
/// constraint system will not be satisfied otherwise. If `self` should
|
||||||
/// also be checked for equality, e.g. `a <= b` instead of `a < b`, set
|
/// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
|
||||||
/// `should_also_check_quality` to `true`. This variant assumes `a` and `b`
|
/// `should_also_check_quality` to `true`. This variant assumes `self` and `other`
|
||||||
/// are `<= (p-1)/2` and does not generate constraints to verify that.
|
/// are `<= (p-1)/2` and does not generate constraints to verify that.
|
||||||
pub fn enforce_cmp_unchecked<CS: ConstraintSystem<F>>(
|
pub fn enforce_cmp_unchecked(
|
||||||
&self,
|
&self,
|
||||||
mut cs: CS,
|
other: &FpVar<F>,
|
||||||
b: &FpGadget<F>,
|
|
||||||
ordering: Ordering,
|
ordering: Ordering,
|
||||||
should_also_check_equality: bool,
|
should_also_check_equality: bool,
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
let (left, right) = Self::process_cmp_inputs(
|
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||||
cs.ns(|| "process cmp inputs"),
|
left.enforce_smaller_than_unchecked(&right)
|
||||||
&self,
|
|
||||||
b,
|
|
||||||
ordering,
|
|
||||||
should_also_check_equality,
|
|
||||||
)?;
|
|
||||||
Self::enforce_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), &left, &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
|
/// `Boolean` that contains the result - `1` if true, `0` otherwise. The
|
||||||
/// constraint system will be satisfied in any case. If `self` should
|
/// 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
|
/// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
|
||||||
/// `should_also_check_quality` to `true`. This variant verifies `a` and `b`
|
/// `should_also_check_quality` to `true`. This variant verifies `self` and `other`
|
||||||
/// are `<= (p-1)/2`.
|
/// are `<= (p-1)/2`.
|
||||||
pub fn is_cmp<CS: ConstraintSystem<F>>(
|
pub fn is_cmp(
|
||||||
&self,
|
&self,
|
||||||
mut cs: CS,
|
other: &FpVar<F>,
|
||||||
b: &FpGadget<F>,
|
|
||||||
ordering: Ordering,
|
ordering: Ordering,
|
||||||
should_also_check_equality: bool,
|
should_also_check_equality: bool,
|
||||||
) -> Result<Boolean, SynthesisError> {
|
) -> Result<Boolean<F>, SynthesisError> {
|
||||||
let (left, right) = Self::process_cmp_inputs(
|
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||||
cs.ns(|| "process cmp inputs"),
|
left.is_smaller_than(&right)
|
||||||
&self,
|
|
||||||
b,
|
|
||||||
ordering,
|
|
||||||
should_also_check_equality,
|
|
||||||
)?;
|
|
||||||
Self::is_smaller_than(cs.ns(|| "enforce smaller than"), &left, &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
|
/// `Boolean` that contains the result - `1` if true, `0` otherwise. The
|
||||||
/// constraint system will be satisfied in any case. If `self` should
|
/// 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
|
/// also be checked for equality, e.g. `self <= other` instead of `self < other`, set
|
||||||
/// `should_also_check_quality` to `true`. This variant assumes `a` and `b`
|
/// `should_also_check_quality` to `true`. This variant assumes `self` and `other`
|
||||||
/// are `<= (p-1)/2` and does not generate constraints to verify that.
|
/// are `<= (p-1)/2` and does not generate constraints to verify that.
|
||||||
pub fn is_cmp_unchecked<CS: ConstraintSystem<F>>(
|
pub fn is_cmp_unchecked(
|
||||||
&self,
|
&self,
|
||||||
mut cs: CS,
|
other: &FpVar<F>,
|
||||||
b: &FpGadget<F>,
|
|
||||||
ordering: Ordering,
|
ordering: Ordering,
|
||||||
should_also_check_equality: bool,
|
should_also_check_equality: bool,
|
||||||
) -> Result<Boolean, SynthesisError> {
|
) -> Result<Boolean<F>, SynthesisError> {
|
||||||
let (left, right) = Self::process_cmp_inputs(
|
let (left, right) = self.process_cmp_inputs(other, ordering, should_also_check_equality)?;
|
||||||
cs.ns(|| "process cmp inputs"),
|
left.is_smaller_than_unchecked(&right)
|
||||||
&self,
|
|
||||||
b,
|
|
||||||
ordering,
|
|
||||||
should_also_check_equality,
|
|
||||||
)?;
|
|
||||||
Self::is_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), &left, &right)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_cmp_inputs<CS: ConstraintSystem<F>>(
|
fn process_cmp_inputs(
|
||||||
mut cs: CS,
|
&self,
|
||||||
a: &FpGadget<F>,
|
other: &Self,
|
||||||
b: &FpGadget<F>,
|
|
||||||
ordering: Ordering,
|
ordering: Ordering,
|
||||||
should_also_check_equality: bool,
|
should_also_check_equality: bool,
|
||||||
) -> Result<(FpGadget<F>, FpGadget<F>), SynthesisError> {
|
) -> Result<(Self, Self), SynthesisError> {
|
||||||
let left;
|
let (left, right) = match ordering {
|
||||||
let right;
|
Ordering::Less => (self, other),
|
||||||
match ordering {
|
Ordering::Greater => (other, self),
|
||||||
Ordering::Less => {
|
Ordering::Equal => Err(SynthesisError::Unsatisfiable)?,
|
||||||
left = a;
|
|
||||||
right = b;
|
|
||||||
}
|
|
||||||
Ordering::Greater => {
|
|
||||||
left = b;
|
|
||||||
right = a;
|
|
||||||
}
|
|
||||||
Ordering::Equal => {
|
|
||||||
return Err(SynthesisError::Unsatisfiable);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let right_for_check = if should_also_check_equality {
|
let right_for_check = if should_also_check_equality {
|
||||||
right.add_constant(cs.ns(|| "plus one"), &F::one())?
|
right + F::one()
|
||||||
} else {
|
} else {
|
||||||
right.clone()
|
right.clone()
|
||||||
};
|
};
|
||||||
@@ -129,77 +91,47 @@ impl<F: PrimeField> FpGadget<F> {
|
|||||||
Ok((left.clone(), right_for_check))
|
Ok((left.clone(), right_for_check))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to enforce `a <= (p-1)/2`.
|
// Helper function to enforce `self <= (p-1)/2`.
|
||||||
pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two<CS: ConstraintSystem<F>>(
|
pub fn enforce_smaller_or_equal_than_mod_minus_one_div_two(
|
||||||
mut cs: CS,
|
&self,
|
||||||
a: &FpGadget<F>,
|
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
let a_bits = a.to_bits(cs.ns(|| "a to bits"))?;
|
let _ = Boolean::enforce_smaller_or_equal_than(
|
||||||
Boolean::enforce_smaller_or_equal_than::<_, _, F, _>(
|
&self.to_bits()?,
|
||||||
cs.ns(|| "enforce smaller than modulus minus one div two"),
|
|
||||||
&a_bits,
|
|
||||||
F::modulus_minus_one_div_two(),
|
F::modulus_minus_one_div_two(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to check `a < b` and output a result bit. This function
|
/// Helper function to check `self < other` and output a result bit. This function
|
||||||
/// verifies `a` and `b` are `<= (p-1)/2`.
|
/// verifies `self` and `other` are `<= (p-1)/2`.
|
||||||
fn is_smaller_than<CS: ConstraintSystem<F>>(
|
fn is_smaller_than(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
|
||||||
mut cs: CS,
|
self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||||
a: &FpGadget<F>,
|
other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||||
b: &FpGadget<F>,
|
self.is_smaller_than_unchecked(other)
|
||||||
) -> Result<Boolean, 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::is_smaller_than_unchecked(cs.ns(|| "enforce smaller than"), a, b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to check `a < b` and output a result bit. This function
|
/// Helper function to check `self < other` and output a result bit. This function
|
||||||
/// assumes `a` and `b` are `<= (p-1)/2` and does not generate constraints
|
/// assumes `self` and `other` are `<= (p-1)/2` and does not generate constraints
|
||||||
/// to verify that.
|
/// to verify that.
|
||||||
fn is_smaller_than_unchecked<CS: ConstraintSystem<F>>(
|
fn is_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<Boolean<F>, SynthesisError> {
|
||||||
mut cs: CS,
|
Ok((self - other).double()?.to_bits()?.last().unwrap().clone())
|
||||||
a: &FpGadget<F>,
|
|
||||||
b: &FpGadget<F>,
|
|
||||||
) -> Result<Boolean, SynthesisError> {
|
|
||||||
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])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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`.
|
/// are `<= (p-1)/2`.
|
||||||
fn enforce_smaller_than<CS: ConstraintSystem<F>>(
|
fn enforce_smaller_than(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
|
||||||
mut cs: CS,
|
self.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||||
a: &FpGadget<F>,
|
other.enforce_smaller_or_equal_than_mod_minus_one_div_two()?;
|
||||||
b: &FpGadget<F>,
|
self.enforce_smaller_than_unchecked(other)
|
||||||
) -> 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
/// are `<= (p-1)/2` and does not generate constraints to verify that.
|
||||||
fn enforce_smaller_than_unchecked<CS: ConstraintSystem<F>>(
|
fn enforce_smaller_than_unchecked(&self, other: &FpVar<F>) -> Result<(), SynthesisError> {
|
||||||
mut cs: CS,
|
let cs = [self, other].cs().unwrap();
|
||||||
a: &FpGadget<F>,
|
let is_smaller_than = self.is_smaller_than_unchecked(other)?;
|
||||||
b: &FpGadget<F>,
|
let lc_one = lc!() + Variable::One;
|
||||||
) -> Result<(), SynthesisError> {
|
cs.enforce_constraint(is_smaller_than.lc(), lc_one.clone(), lc_one)
|
||||||
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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,9 +141,7 @@ mod test {
|
|||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::{
|
use crate::{alloc::AllocVar, fields::fp::FpVar};
|
||||||
alloc::AllocGadget, fields::fp::FpGadget, test_constraint_system::TestConstraintSystem,
|
|
||||||
};
|
|
||||||
use algebra::{bls12_381::Fr, PrimeField, UniformRand};
|
use algebra::{bls12_381::Fr, PrimeField, UniformRand};
|
||||||
use r1cs_core::ConstraintSystem;
|
use r1cs_core::ConstraintSystem;
|
||||||
|
|
||||||
@@ -233,43 +163,20 @@ mod test {
|
|||||||
r
|
r
|
||||||
}
|
}
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
let a = rand_in_range(&mut rng);
|
let a = rand_in_range(&mut rng);
|
||||||
let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap();
|
let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
|
||||||
let b = rand_in_range(&mut rng);
|
let b = rand_in_range(&mut rng);
|
||||||
let b_var = FpGadget::<Fr>::alloc(cs.ns(|| "b"), || Ok(b)).unwrap();
|
let b_var = FpVar::<Fr>::new_witness(cs.ns("b"), || Ok(b)).unwrap();
|
||||||
|
|
||||||
match a.cmp(&b) {
|
match a.cmp(&b) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
a_var
|
a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
|
||||||
.enforce_cmp(cs.ns(|| "smaller than test"), &b_var, Ordering::Less, false)
|
a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
|
||||||
.unwrap();
|
|
||||||
a_var
|
|
||||||
.enforce_cmp(
|
|
||||||
cs.ns(|| "smaller than test 2"),
|
|
||||||
&b_var,
|
|
||||||
Ordering::Less,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
a_var
|
a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
|
||||||
.enforce_cmp(
|
a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -277,79 +184,46 @@ mod test {
|
|||||||
if i == 0 {
|
if i == 0 {
|
||||||
println!("number of constraints: {}", cs.num_constraints());
|
println!("number of constraints: {}", cs.num_constraints());
|
||||||
}
|
}
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
for _i in 0..10 {
|
for _i in 0..10 {
|
||||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
let a = rand_in_range(&mut rng);
|
let a = rand_in_range(&mut rng);
|
||||||
let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap();
|
let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
|
||||||
let b = rand_in_range(&mut rng);
|
let b = rand_in_range(&mut rng);
|
||||||
let b_var = FpGadget::<Fr>::alloc(cs.ns(|| "b"), || Ok(b)).unwrap();
|
let b_var = FpVar::<Fr>::new_witness(cs.ns("b"), || Ok(b)).unwrap();
|
||||||
|
|
||||||
match b.cmp(&a) {
|
match b.cmp(&a) {
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
a_var
|
a_var.enforce_cmp(&b_var, Ordering::Less, false).unwrap();
|
||||||
.enforce_cmp(cs.ns(|| "smaller than test"), &b_var, Ordering::Less, false)
|
a_var.enforce_cmp(&b_var, Ordering::Less, true).unwrap();
|
||||||
.unwrap();
|
|
||||||
a_var
|
|
||||||
.enforce_cmp(
|
|
||||||
cs.ns(|| "smaller than test 2"),
|
|
||||||
&b_var,
|
|
||||||
Ordering::Less,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
a_var
|
a_var.enforce_cmp(&b_var, Ordering::Greater, false).unwrap();
|
||||||
.enforce_cmp(
|
a_var.enforce_cmp(&b_var, Ordering::Greater, true).unwrap();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(!cs.is_satisfied());
|
assert!(!cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
for _i in 0..10 {
|
for _i in 0..10 {
|
||||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
let a = rand_in_range(&mut rng);
|
let a = rand_in_range(&mut rng);
|
||||||
let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap();
|
let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
|
||||||
a_var
|
a_var.enforce_cmp(&a_var, Ordering::Less, false).unwrap();
|
||||||
.enforce_cmp(cs.ns(|| "smaller than test"), &a_var, Ordering::Less, false)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(!cs.is_satisfied());
|
assert!(!cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
for _i in 0..10 {
|
for _i in 0..10 {
|
||||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
let a = rand_in_range(&mut rng);
|
let a = rand_in_range(&mut rng);
|
||||||
let a_var = FpGadget::<Fr>::alloc(cs.ns(|| "a"), || Ok(a)).unwrap();
|
let a_var = FpVar::<Fr>::new_witness(cs.ns("a"), || Ok(a)).unwrap();
|
||||||
a_var
|
a_var.enforce_cmp(&a_var, Ordering::Less, true).unwrap();
|
||||||
.enforce_cmp(
|
assert!(cs.is_satisfied().unwrap());
|
||||||
cs.ns(|| "smaller than or equal to test"),
|
|
||||||
&a_var,
|
|
||||||
Ordering::Less,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(cs.is_satisfied());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,693 +1,10 @@
|
|||||||
use algebra::{
|
use crate::fields::{fp::FpVar, quadratic_extension::*};
|
||||||
fields::{Fp2, Fp2Parameters},
|
use algebra::fields::{Fp2Parameters, Fp2ParamsWrapper, QuadExtParameters};
|
||||||
PrimeField,
|
|
||||||
};
|
|
||||||
use core::{borrow::Borrow, marker::PhantomData};
|
|
||||||
use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
|
|
||||||
|
|
||||||
use crate::{fields::fp::FpGadget, prelude::*, Vec};
|
pub type Fp2Var<P> = QuadExtVar<FpVar<<P as Fp2Parameters>::Fp>, Fp2ParamsWrapper<P>>;
|
||||||
|
|
||||||
#[derive(Derivative)]
|
impl<P: Fp2Parameters> QuadExtVarParams<FpVar<P::Fp>> for Fp2ParamsWrapper<P> {
|
||||||
#[derivative(Debug(bound = "P: Fp2Parameters, ConstraintF: PrimeField"))]
|
fn mul_base_field_var_by_frob_coeff(fe: &mut FpVar<P::Fp>, power: usize) {
|
||||||
#[must_use]
|
*fe *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
pub struct Fp2Gadget<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> {
|
|
||||||
pub c0: FpGadget<ConstraintF>,
|
|
||||||
pub c1: FpGadget<ConstraintF>,
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
_params: PhantomData<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField>
|
|
||||||
ToConstraintFieldGadget<ConstraintF> for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn to_constraint_field<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<FpGadget<ConstraintF>>, 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<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Fp2Gadget<P, ConstraintF> {
|
|
||||||
pub fn new(c0: FpGadget<ConstraintF>, c1: FpGadget<ConstraintF>) -> Self {
|
|
||||||
Self {
|
|
||||||
c0,
|
|
||||||
c1,
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_fp_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
cs: CS,
|
|
||||||
fe: &FpGadget<ConstraintF>,
|
|
||||||
) -> Result<FpGadget<ConstraintF>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
fe: &P::Fp,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.mul_by_fp_constant_in_place(cs, fe)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> FieldGadget<Fp2<P>, ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
type Variable = (ConstraintVar<ConstraintF>, ConstraintVar<ConstraintF>);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_value(&self) -> Option<Fp2<P>> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = FpGadget::zero(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = FpGadget::zero(cs.ns(|| "c1"))?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = FpGadget::one(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = FpGadget::zero(cs.ns(|| "c1"))?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
bit: &Boolean,
|
|
||||||
coeff: Fp2<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.double_in_place(cs)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.negate_in_place(cs)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, 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");
|
|
||||||
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<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 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
power: usize,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
let _ = result.frobenius_map_in_place(cs, power)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
other: &Fp2<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
let _ = result.add_constant_in_place(cs, other)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Fp2<P>,
|
|
||||||
) -> 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
fe: &Fp2<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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::<ConstraintF>::cost_of_mul()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost_of_mul_equals() -> usize {
|
|
||||||
Self::cost_of_mul()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> PartialEq
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.c0 == other.c0 && self.c1 == other.c1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Eq for Fp2Gadget<P, ConstraintF> {}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> EqGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> NEqGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ToBitsGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn to_bits<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> ToBytesGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> Clone
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
c0: self.c0.clone(),
|
|
||||||
c1: self.c1.clone(),
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> CondSelectGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
cond: &Boolean,
|
|
||||||
true_value: &Self,
|
|
||||||
false_value: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = FpGadget::<ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c0"),
|
|
||||||
cond,
|
|
||||||
&true_value.c0,
|
|
||||||
&false_value.c0,
|
|
||||||
)?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c1"),
|
|
||||||
cond,
|
|
||||||
&true_value.c1,
|
|
||||||
&false_value.c1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
type TableConstant = Fp2<P>;
|
|
||||||
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
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 * <FpGadget<ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField>
|
|
||||||
ThreeBitCondNegLookupGadget<ConstraintF> for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
type TableConstant = Fp2<P>;
|
|
||||||
|
|
||||||
fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
b0b1: &Boolean,
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
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 * <FpGadget<ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp2Parameters<Fp = ConstraintF>, ConstraintF: PrimeField> AllocGadget<Fp2<P>, ConstraintF>
|
|
||||||
for Fp2Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
t: T,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
T: Borrow<Fp2<P>>,
|
|
||||||
{
|
|
||||||
Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp2<P>>,
|
|
||||||
{
|
|
||||||
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<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp2<P>>,
|
|
||||||
{
|
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,955 +1,15 @@
|
|||||||
use algebra::{
|
use crate::fields::{cubic_extension::*, fp::FpVar};
|
||||||
fields::fp3::{Fp3, Fp3Parameters},
|
use algebra::fields::{CubicExtParameters, Fp3Parameters, Fp3ParamsWrapper};
|
||||||
PrimeField, SquareRootField,
|
|
||||||
};
|
|
||||||
use core::{borrow::Borrow, marker::PhantomData};
|
|
||||||
use r1cs_core::{ConstraintSystem, ConstraintVar, SynthesisError};
|
|
||||||
|
|
||||||
use crate::{fields::fp::FpGadget, prelude::*, Vec};
|
pub type Fp3Var<P> = CubicExtVar<FpVar<<P as Fp3Parameters>::Fp>, Fp3ParamsWrapper<P>>;
|
||||||
|
|
||||||
#[derive(Derivative)]
|
impl<P: Fp3Parameters> CubicExtVarParams<FpVar<P::Fp>> for Fp3ParamsWrapper<P> {
|
||||||
#[derivative(Debug(
|
fn mul_base_field_vars_by_frob_coeff(
|
||||||
bound = "P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField"
|
c1: &mut FpVar<P::Fp>,
|
||||||
))]
|
c2: &mut FpVar<P::Fp>,
|
||||||
#[must_use]
|
|
||||||
pub struct Fp3Gadget<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
{
|
|
||||||
pub c0: FpGadget<ConstraintF>,
|
|
||||||
pub c1: FpGadget<ConstraintF>,
|
|
||||||
pub c2: FpGadget<ConstraintF>,
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
_params: PhantomData<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
ToConstraintFieldGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn to_constraint_field<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<FpGadget<ConstraintF>>, 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<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn new(
|
|
||||||
c0: FpGadget<ConstraintF>,
|
|
||||||
c1: FpGadget<ConstraintF>,
|
|
||||||
c2: FpGadget<ConstraintF>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
c0,
|
|
||||||
c1,
|
|
||||||
c2,
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply a FpGadget by quadratic nonresidue P::NONRESIDUE.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_fp_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
cs: CS,
|
|
||||||
fe: &FpGadget<ConstraintF>,
|
|
||||||
) -> Result<FpGadget<ConstraintF>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
fe: &P::Fp,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.mul_by_fp_constant_in_place(cs, fe)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
FieldGadget<Fp3<P>, ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
type Variable = (
|
|
||||||
ConstraintVar<ConstraintF>,
|
|
||||||
ConstraintVar<ConstraintF>,
|
|
||||||
ConstraintVar<ConstraintF>,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_value(&self) -> Option<Fp3<P>> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = FpGadget::<ConstraintF>::zero(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::zero(cs.ns(|| "c1"))?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::zero(cs.ns(|| "c2"))?;
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = FpGadget::<ConstraintF>::one(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::zero(cs.ns(|| "c1"))?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::zero(cs.ns(|| "c2"))?;
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
bit: &Boolean,
|
|
||||||
coeff: Fp3<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Fp3<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Fp3<P>,
|
|
||||||
) -> 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Fp3<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
power: usize,
|
power: usize,
|
||||||
) -> Result<Self, SynthesisError> {
|
) {
|
||||||
let mut result = self.clone();
|
*c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
result.frobenius_map_in_place(cs, power)?;
|
*c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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::<ConstraintF>::cost_of_mul()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost_of_mul_equals() -> usize {
|
|
||||||
6 * FpGadget::<ConstraintF>::cost_of_mul()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField> PartialEq
|
|
||||||
for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.c0 == other.c0 && self.c1 == other.c1 && self.c2 == other.c2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField> Eq
|
|
||||||
for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
EqGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
ConditionalEqGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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 * <FpGadget<ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
NEqGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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 * <FpGadget<ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
ToBitsGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn to_bits<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
ToBytesGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField> Clone
|
|
||||||
for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self::new(self.c0.clone(), self.c1.clone(), self.c2.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
CondSelectGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
cond: &Boolean,
|
|
||||||
first: &Self,
|
|
||||||
second: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = FpGadget::<ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c0"),
|
|
||||||
cond,
|
|
||||||
&first.c0,
|
|
||||||
&second.c0,
|
|
||||||
)?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c1"),
|
|
||||||
cond,
|
|
||||||
&first.c1,
|
|
||||||
&second.c1,
|
|
||||||
)?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c2"),
|
|
||||||
cond,
|
|
||||||
&first.c2,
|
|
||||||
&second.c2,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
3 * <FpGadget<ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
TwoBitLookupGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
type TableConstant = Fp3<P>;
|
|
||||||
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
|
|
||||||
let c0 = FpGadget::<ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c2"), b, &c2s)?;
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
3 * <FpGadget<ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
ThreeBitCondNegLookupGadget<ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
type TableConstant = Fp3<P>;
|
|
||||||
|
|
||||||
fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
b0b1: &Boolean,
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
let c2s = c.iter().map(|f| f.c2).collect::<Vec<_>>();
|
|
||||||
let c0 = FpGadget::<ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c0"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c0s,
|
|
||||||
)?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c1"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c1s,
|
|
||||||
)?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c2"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c2s,
|
|
||||||
)?;
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
3 * <FpGadget<ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Fp3Parameters<Fp = ConstraintF>, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
AllocGadget<Fp3<P>, ConstraintF> for Fp3Gadget<P, ConstraintF>
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
t: T,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
T: Borrow<Fp3<P>>,
|
|
||||||
{
|
|
||||||
Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp3<P>>,
|
|
||||||
{
|
|
||||||
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::<ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::alloc(&mut cs.ns(|| "c2"), || c2)?;
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp3<P>>,
|
|
||||||
{
|
|
||||||
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::<ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
|
||||||
let c1 = FpGadget::<ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
|
||||||
let c2 = FpGadget::<ConstraintF>::alloc_input(&mut cs.ns(|| "c2"), || c2)?;
|
|
||||||
Ok(Self::new(c0, c1, c2))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,757 +1,11 @@
|
|||||||
use algebra::{
|
use crate::fields::{fp2::Fp2Var, quadratic_extension::*};
|
||||||
fields::{Fp2, Fp2Parameters, Fp4, Fp4Parameters},
|
use algebra::fields::{Fp4Parameters, Fp4ParamsWrapper, QuadExtParameters};
|
||||||
BigInteger, PrimeField,
|
|
||||||
};
|
|
||||||
use core::{borrow::Borrow, marker::PhantomData};
|
|
||||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
|
||||||
|
|
||||||
use crate::{prelude::*, Vec};
|
pub type Fp4Var<P> = QuadExtVar<Fp2Var<<P as Fp4Parameters>::Fp2Params>, Fp4ParamsWrapper<P>>;
|
||||||
|
|
||||||
type Fp2Gadget<P, ConstraintF> =
|
impl<P: Fp4Parameters> QuadExtVarParams<Fp2Var<P::Fp2Params>> for Fp4ParamsWrapper<P> {
|
||||||
super::fp2::Fp2Gadget<<P as Fp4Parameters>::Fp2Params, ConstraintF>;
|
fn mul_base_field_var_by_frob_coeff(fe: &mut Fp2Var<P::Fp2Params>, power: usize) {
|
||||||
type Fp2GadgetVariable<P, ConstraintF> = <Fp2Gadget<P, ConstraintF> as FieldGadget<
|
fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
Fp2<<P as Fp4Parameters>::Fp2Params>,
|
fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
ConstraintF,
|
|
||||||
>>::Variable;
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug(bound = "ConstraintF: PrimeField"))]
|
|
||||||
#[must_use]
|
|
||||||
pub struct Fp4Gadget<P, ConstraintF: PrimeField>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
pub c0: Fp2Gadget<P, ConstraintF>,
|
|
||||||
pub c1: Fp2Gadget<P, ConstraintF>,
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
_params: PhantomData<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn to_constraint_field<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<FpGadget<ConstraintF>>, 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<P, ConstraintF: PrimeField> Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
pub fn new(c0: Fp2Gadget<P, ConstraintF>, c1: Fp2Gadget<P, ConstraintF>) -> Self {
|
|
||||||
Self {
|
|
||||||
c0,
|
|
||||||
c1,
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply a Fp2Gadget by quadratic nonresidue P::NONRESIDUE.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_fp2_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
cs: CS,
|
|
||||||
fe: &Fp2Gadget<P, ConstraintF>,
|
|
||||||
) -> Result<Fp2Gadget<P, ConstraintF>, SynthesisError> {
|
|
||||||
let new_c0 = Fp2Gadget::<P, ConstraintF>::mul_fp_gadget_by_nonresidue(cs, &fe.c1)?;
|
|
||||||
let new_c1 = fe.c0.clone();
|
|
||||||
Ok(Fp2Gadget::<P, ConstraintF>::new(new_c0, new_c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply a Fp4Gadget by an element of fp.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_by_fp_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
mut cs: CS,
|
|
||||||
fe: &<<P as Fp4Parameters>::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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
fe: &<<P as Fp4Parameters>::Fp2Params as Fp2Parameters>::Fp,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.mul_by_fp_constant_in_place(cs, fe)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unitary_inverse<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
Ok(Self::new(self.c0.clone(), self.c1.negate(cs)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn cyclotomic_exp<CS: ConstraintSystem<ConstraintF>, B: BigInteger>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
exponent: &B,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<P, ConstraintF: PrimeField> FieldGadget<Fp4<P>, ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
type Variable = (
|
|
||||||
Fp2GadgetVariable<P, ConstraintF>,
|
|
||||||
Fp2GadgetVariable<P, ConstraintF>,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_value(&self) -> Option<Fp4<P>> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = Fp2Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
bit: &Boolean,
|
|
||||||
coeff: Fp4<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.double_in_place(cs)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.negate_in_place(cs)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, 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");
|
|
||||||
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
power: usize,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
let _ = result.frobenius_map_in_place(cs, power)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
other: &Fp4<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
let _ = result.add_constant_in_place(cs, other)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Fp4<P>,
|
|
||||||
) -> 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
fe: &Fp4<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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::<P, ConstraintF>::cost_of_mul()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost_of_mul_equals() -> usize {
|
|
||||||
Self::cost_of_mul()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> PartialEq for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.c0 == other.c0 && self.c1 == other.c1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> Eq for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> EqGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> ConditionalEqGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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 * <Fp2Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> NEqGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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 * <Fp2Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> ToBitsGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn to_bits<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<P, ConstraintF: PrimeField> ToBytesGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<P, ConstraintF: PrimeField> Clone for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
c0: self.c0.clone(),
|
|
||||||
c1: self.c1.clone(),
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> CondSelectGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
cond: &Boolean,
|
|
||||||
first: &Self,
|
|
||||||
second: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c0"),
|
|
||||||
cond,
|
|
||||||
&first.c0,
|
|
||||||
&second.c0,
|
|
||||||
)?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c1"),
|
|
||||||
cond,
|
|
||||||
&first.c1,
|
|
||||||
&second.c1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2 * <Fp2Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> TwoBitLookupGadget<ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
type TableConstant = Fp4<P>;
|
|
||||||
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
let c0 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2 * <Fp2Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> ThreeBitCondNegLookupGadget<ConstraintF>
|
|
||||||
for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
type TableConstant = Fp4<P>;
|
|
||||||
|
|
||||||
fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
b0b1: &Boolean,
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
let c0 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c0"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c0s,
|
|
||||||
)?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c1"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c1s,
|
|
||||||
)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2 * <Fp2Gadget<P, ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField> AllocGadget<Fp4<P>, ConstraintF> for Fp4Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp4Parameters,
|
|
||||||
P::Fp2Params: Fp2Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
t: T,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
T: Borrow<Fp4<P>>,
|
|
||||||
{
|
|
||||||
Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp4<P>>,
|
|
||||||
{
|
|
||||||
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::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp4<P>>,
|
|
||||||
{
|
|
||||||
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::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
|
||||||
let c1 = Fp2Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,749 +1,12 @@
|
|||||||
use algebra::{
|
use crate::fields::{fp3::Fp3Var, quadratic_extension::*};
|
||||||
fields::{
|
use algebra::fields::{fp6_2over3::*, QuadExtParameters};
|
||||||
fp6_2over3::{Fp6, Fp6Parameters},
|
|
||||||
Fp3, Fp3Parameters,
|
|
||||||
},
|
|
||||||
BigInteger, PrimeField, SquareRootField,
|
|
||||||
};
|
|
||||||
use core::{borrow::Borrow, marker::PhantomData};
|
|
||||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
|
||||||
|
|
||||||
use crate::{prelude::*, Vec};
|
pub type Fp6Var<P> = QuadExtVar<Fp3Var<<P as Fp6Parameters>::Fp3Params>, Fp6ParamsWrapper<P>>;
|
||||||
|
|
||||||
type Fp3Gadget<P, ConstraintF> =
|
impl<P: Fp6Parameters> QuadExtVarParams<Fp3Var<P::Fp3Params>> for Fp6ParamsWrapper<P> {
|
||||||
super::fp3::Fp3Gadget<<P as Fp6Parameters>::Fp3Params, ConstraintF>;
|
fn mul_base_field_var_by_frob_coeff(fe: &mut Fp3Var<P::Fp3Params>, power: usize) {
|
||||||
type Fp3GadgetVariable<P, ConstraintF> = <Fp3Gadget<P, ConstraintF> as FieldGadget<
|
fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
Fp3<<P as Fp6Parameters>::Fp3Params>,
|
fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
ConstraintF,
|
fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
|
||||||
>>::Variable;
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug(bound = "ConstraintF: PrimeField + SquareRootField"))]
|
|
||||||
#[must_use]
|
|
||||||
pub struct Fp6Gadget<P, ConstraintF: PrimeField + SquareRootField>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
pub c0: Fp3Gadget<P, ConstraintF>,
|
|
||||||
pub c1: Fp3Gadget<P, ConstraintF>,
|
|
||||||
#[derivative(Debug = "ignore")]
|
|
||||||
_params: PhantomData<P>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> ToConstraintFieldGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn to_constraint_field<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<FpGadget<ConstraintF>>, 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<P, ConstraintF: PrimeField + SquareRootField> Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
pub fn new(c0: Fp3Gadget<P, ConstraintF>, c1: Fp3Gadget<P, ConstraintF>) -> Self {
|
|
||||||
Self {
|
|
||||||
c0,
|
|
||||||
c1,
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Multiply a Fp3Gadget by quadratic nonresidue P::NONRESIDUE.
|
|
||||||
#[inline]
|
|
||||||
pub fn mul_fp3_gadget_by_nonresidue<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
fe: &Fp3Gadget<P, ConstraintF>,
|
|
||||||
) -> Result<Fp3Gadget<P, ConstraintF>, SynthesisError> {
|
|
||||||
let mut res = Fp3Gadget::<P, ConstraintF>::new(fe.c2.clone(), fe.c0.clone(), fe.c1.clone());
|
|
||||||
res.c0.mul_by_constant_in_place(
|
|
||||||
cs.ns(|| "res * non_residue"),
|
|
||||||
&<P::Fp3Params as Fp3Parameters>::NONRESIDUE,
|
|
||||||
)?;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unitary_inverse<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
Ok(Self::new(self.c0.clone(), self.c1.negate(cs)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn cyclotomic_exp<CS: ConstraintSystem<ConstraintF>, B: BigInteger>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
exponent: &B,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<P, ConstraintF: PrimeField + SquareRootField> FieldGadget<Fp6<P>, ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
type Variable = (
|
|
||||||
Fp3GadgetVariable<P, ConstraintF>,
|
|
||||||
Fp3GadgetVariable<P, ConstraintF>,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_value(&self) -> Option<Fp6<P>> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = Fp3Gadget::<P, ConstraintF>::zero(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn one<CS: ConstraintSystem<ConstraintF>>(mut cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = Fp3Gadget::<P, ConstraintF>::one(cs.ns(|| "c0"))?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::zero(cs.ns(|| "c1"))?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
bit: &Boolean,
|
|
||||||
coeff: Fp6<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.double_in_place(cs)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
result.negate_in_place(cs)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<Self, 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");
|
|
||||||
|
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
power: usize,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
let _ = result.frobenius_map_in_place(cs, power)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
other: &Fp6<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut result = self.clone();
|
|
||||||
let _ = result.add_constant_in_place(cs, other)?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
mut cs: CS,
|
|
||||||
other: &Fp6<P>,
|
|
||||||
) -> 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
fe: &Fp6<P>,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
// 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::<P, ConstraintF>::cost_of_mul()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost_of_mul_equals() -> usize {
|
|
||||||
Self::cost_of_mul()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> PartialEq for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.c0 == other.c0 && self.c1 == other.c1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> Eq for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> EqGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> ConditionalEqGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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 * <Fp3Gadget<P, ConstraintF> as ConditionalEqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> NEqGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&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 * <Fp3Gadget<P, ConstraintF> as NEqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> ToBitsGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn to_bits<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<Boolean>, 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<P, ConstraintF: PrimeField + SquareRootField> ToBytesGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Vec<UInt8>, 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<P, ConstraintF: PrimeField + SquareRootField> Clone for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
c0: self.c0.clone(),
|
|
||||||
c1: self.c1.clone(),
|
|
||||||
_params: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> CondSelectGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
cond: &Boolean,
|
|
||||||
first: &Self,
|
|
||||||
second: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0 = Fp3Gadget::<P, ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c0"),
|
|
||||||
cond,
|
|
||||||
&first.c0,
|
|
||||||
&second.c0,
|
|
||||||
)?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::conditionally_select(
|
|
||||||
&mut cs.ns(|| "c1"),
|
|
||||||
cond,
|
|
||||||
&first.c1,
|
|
||||||
&second.c1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2 * <Fp3Gadget<P, ConstraintF> as CondSelectGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> TwoBitLookupGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
type TableConstant = Fp6<P>;
|
|
||||||
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
let c0 = Fp3Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c0"), b, &c0s)?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::two_bit_lookup(cs.ns(|| "Lookup c1"), b, &c1s)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2 * <Fp3Gadget<P, ConstraintF> as TwoBitLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> ThreeBitCondNegLookupGadget<ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
type TableConstant = Fp6<P>;
|
|
||||||
|
|
||||||
fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
b: &[Boolean],
|
|
||||||
b0b1: &Boolean,
|
|
||||||
c: &[Self::TableConstant],
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
|
||||||
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
|
||||||
let c0 = Fp3Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c0"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c0s,
|
|
||||||
)?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::three_bit_cond_neg_lookup(
|
|
||||||
cs.ns(|| "Lookup c1"),
|
|
||||||
b,
|
|
||||||
b0b1,
|
|
||||||
&c1s,
|
|
||||||
)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost() -> usize {
|
|
||||||
2 * <Fp3Gadget<P, ConstraintF> as ThreeBitCondNegLookupGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P, ConstraintF: PrimeField + SquareRootField> AllocGadget<Fp6<P>, ConstraintF>
|
|
||||||
for Fp6Gadget<P, ConstraintF>
|
|
||||||
where
|
|
||||||
P: Fp6Parameters,
|
|
||||||
P::Fp3Params: Fp3Parameters<Fp = ConstraintF>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
t: T,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
T: Borrow<Fp6<P>>,
|
|
||||||
{
|
|
||||||
Self::zero(cs.ns(|| "zero"))?.add_constant(cs.ns(|| "add constant"), t.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp6<P>>,
|
|
||||||
{
|
|
||||||
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::<P, ConstraintF>::alloc(&mut cs.ns(|| "c0"), || c0)?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::alloc(&mut cs.ns(|| "c1"), || c1)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
mut cs: CS,
|
|
||||||
value_gen: F,
|
|
||||||
) -> Result<Self, SynthesisError>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
T: Borrow<Fp6<P>>,
|
|
||||||
{
|
|
||||||
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::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c0"), || c0)?;
|
|
||||||
let c1 = Fp3Gadget::<P, ConstraintF>::alloc_input(&mut cs.ns(|| "c1"), || c1)?;
|
|
||||||
Ok(Self::new(c0, c1))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,15 @@
|
|||||||
use algebra::{fields::BitIterator, Field, PrimeField, Vec};
|
use algebra::{prelude::*, BitIterator};
|
||||||
use core::fmt::Debug;
|
use core::{
|
||||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
fmt::Debug,
|
||||||
|
ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
|
||||||
|
};
|
||||||
|
use r1cs_core::SynthesisError;
|
||||||
|
|
||||||
use crate::{prelude::*, Assignment};
|
use crate::{prelude::*, Assignment};
|
||||||
|
|
||||||
|
pub mod cubic_extension;
|
||||||
|
pub mod quadratic_extension;
|
||||||
|
|
||||||
pub mod fp;
|
pub mod fp;
|
||||||
pub mod fp12;
|
pub mod fp12;
|
||||||
pub mod fp2;
|
pub mod fp2;
|
||||||
@@ -12,278 +18,146 @@ pub mod fp4;
|
|||||||
pub mod fp6_2over3;
|
pub mod fp6_2over3;
|
||||||
pub mod fp6_3over2;
|
pub mod fp6_3over2;
|
||||||
|
|
||||||
use crate::fields::fp::FpGadget;
|
/// A hack used to work around the lack of implied bounds.
|
||||||
pub trait ToConstraintFieldGadget<ConstraintF: PrimeField> {
|
pub trait FieldOpsBounds<'a, F, T: 'a>:
|
||||||
fn to_constraint_field<CS: ConstraintSystem<ConstraintF>>(
|
Sized
|
||||||
&self,
|
+ Add<&'a T, Output = T>
|
||||||
cs: CS,
|
+ Sub<&'a T, Output = T>
|
||||||
) -> Result<Vec<FpGadget<ConstraintF>>, SynthesisError>;
|
+ Mul<&'a T, Output = T>
|
||||||
|
+ Add<T, Output = T>
|
||||||
|
+ Sub<T, Output = T>
|
||||||
|
+ Mul<T, Output = T>
|
||||||
|
+ Add<F, Output = T>
|
||||||
|
+ Sub<F, Output = T>
|
||||||
|
+ Mul<F, Output = T>
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FieldGadget<F: Field, ConstraintF: Field>:
|
/// A variable representing a field. Corresponds to the native type `F`.
|
||||||
Sized
|
pub trait FieldVar<F: Field, ConstraintF: Field>:
|
||||||
|
'static
|
||||||
+ Clone
|
+ Clone
|
||||||
|
+ From<Boolean<ConstraintF>>
|
||||||
|
+ R1CSVar<ConstraintF, Value = F>
|
||||||
+ EqGadget<ConstraintF>
|
+ EqGadget<ConstraintF>
|
||||||
+ NEqGadget<ConstraintF>
|
|
||||||
+ ConditionalEqGadget<ConstraintF>
|
|
||||||
+ ToBitsGadget<ConstraintF>
|
+ ToBitsGadget<ConstraintF>
|
||||||
+ AllocGadget<F, ConstraintF>
|
+ AllocVar<F, ConstraintF>
|
||||||
+ ToBytesGadget<ConstraintF>
|
+ ToBytesGadget<ConstraintF>
|
||||||
+ CondSelectGadget<ConstraintF>
|
+ CondSelectGadget<ConstraintF>
|
||||||
+ TwoBitLookupGadget<ConstraintF, TableConstant = F>
|
+ for<'a> FieldOpsBounds<'a, F, Self>
|
||||||
+ ThreeBitCondNegLookupGadget<ConstraintF, TableConstant = F>
|
+ for<'a> AddAssign<&'a Self>
|
||||||
|
+ for<'a> SubAssign<&'a Self>
|
||||||
|
+ for<'a> MulAssign<&'a Self>
|
||||||
|
+ AddAssign<Self>
|
||||||
|
+ SubAssign<Self>
|
||||||
|
+ MulAssign<Self>
|
||||||
|
+ AddAssign<F>
|
||||||
|
+ SubAssign<F>
|
||||||
|
+ MulAssign<F>
|
||||||
+ Debug
|
+ Debug
|
||||||
{
|
{
|
||||||
type Variable: Clone + Debug;
|
fn zero() -> Self;
|
||||||
|
|
||||||
fn get_value(&self) -> Option<F>;
|
fn is_zero(&self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
||||||
|
self.is_eq(&Self::zero())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_variable(&self) -> Self::Variable;
|
fn one() -> Self;
|
||||||
|
|
||||||
fn zero<CS: ConstraintSystem<ConstraintF>>(_: CS) -> Result<Self, SynthesisError>;
|
fn is_one(&self) -> Result<Boolean<ConstraintF>, SynthesisError> {
|
||||||
|
self.is_eq(&Self::one())
|
||||||
|
}
|
||||||
|
|
||||||
fn one<CS: ConstraintSystem<ConstraintF>>(_: CS) -> Result<Self, SynthesisError>;
|
fn constant(v: F) -> Self;
|
||||||
|
|
||||||
fn conditionally_add_constant<CS: ConstraintSystem<ConstraintF>>(
|
fn double(&self) -> Result<Self, SynthesisError> {
|
||||||
&self,
|
Ok(self.clone() + self)
|
||||||
_: CS,
|
}
|
||||||
_: &Boolean,
|
|
||||||
_: F,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn add<CS: ConstraintSystem<ConstraintF>>(
|
fn double_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
||||||
&self,
|
*self += self.double()?;
|
||||||
_: CS,
|
|
||||||
_: &Self,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn add_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.add(cs, other)?;
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn double<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
fn negate(&self) -> Result<Self, SynthesisError>;
|
||||||
self.add(cs, &self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn double_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
cs: CS,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.double(cs)?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
_: CS,
|
|
||||||
_: &Self,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn sub_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.sub(cs, other)?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn negate<CS: ConstraintSystem<ConstraintF>>(&self, _: CS) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn negate_in_place<CS: ConstraintSystem<ConstraintF>>(
|
fn negate_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
||||||
&mut self,
|
*self = self.negate()?;
|
||||||
cs: CS,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.negate(cs)?;
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul<CS: ConstraintSystem<ConstraintF>>(
|
fn square(&self) -> Result<Self, SynthesisError> {
|
||||||
&self,
|
Ok(self.clone() * self)
|
||||||
_: CS,
|
}
|
||||||
_: &Self,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn mul_in_place<CS: ConstraintSystem<ConstraintF>>(
|
fn square_in_place(&mut self) -> Result<&mut Self, SynthesisError> {
|
||||||
&mut self,
|
*self = self.square()?;
|
||||||
cs: CS,
|
|
||||||
other: &Self,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.mul(cs, other)?;
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn square<CS: ConstraintSystem<ConstraintF>>(&self, cs: CS) -> Result<Self, SynthesisError> {
|
/// Enforce that `self * other == result`.
|
||||||
self.mul(cs, &self)
|
fn mul_equals(&self, other: &Self, result: &Self) -> Result<(), SynthesisError> {
|
||||||
|
let actual_result = self.clone() * other;
|
||||||
|
result.enforce_equal(&actual_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn square_in_place<CS: ConstraintSystem<ConstraintF>>(
|
/// Enforce that `self * self == result`.
|
||||||
&mut self,
|
fn square_equals(&self, result: &Self) -> Result<(), SynthesisError> {
|
||||||
cs: CS,
|
let actual_result = self.square()?;
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
result.enforce_equal(&actual_result)
|
||||||
*self = self.square(cs)?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mul_equals<CS: ConstraintSystem<ConstraintF>>(
|
fn inverse(&self) -> Result<Self, SynthesisError>;
|
||||||
&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<CS: ConstraintSystem<ConstraintF>>(
|
/// Returns (self / denominator), but requires fewer constraints than
|
||||||
&self,
|
/// self * denominator.inverse()
|
||||||
mut cs: CS,
|
/// It is up to the caller to ensure that denominator is non-zero,
|
||||||
result: &Self,
|
/// since in that case the result is unconstrained.
|
||||||
) -> Result<(), SynthesisError> {
|
fn mul_by_inverse(&self, denominator: &Self) -> Result<Self, SynthesisError> {
|
||||||
let actual_result = self.square(cs.ns(|| "calc_actual_result"))?;
|
let result = Self::new_witness(self.cs().unwrap(), || {
|
||||||
result.enforce_equal(&mut cs.ns(|| "test_equals"), &actual_result)
|
let denominator_inv_native = denominator.value()?.inverse().get()?;
|
||||||
}
|
let result = self.value()? * &denominator_inv_native;
|
||||||
|
Ok(result)
|
||||||
fn add_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
_: CS,
|
|
||||||
_: &F,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn add_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
cs: CS,
|
|
||||||
other: &F,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.add_constant(cs, other)?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
cs: CS,
|
|
||||||
fe: &F,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
self.add_constant(cs, &(-(*fe)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sub_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
cs: CS,
|
|
||||||
other: &F,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
self.add_constant_in_place(cs, &(-(*other)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mul_by_constant<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
_: CS,
|
|
||||||
_: &F,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn mul_by_constant_in_place<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&mut self,
|
|
||||||
cs: CS,
|
|
||||||
other: &F,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.mul_by_constant(cs, other)?;
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inverse<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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()
|
|
||||||
})?;
|
})?;
|
||||||
self.mul_equals(cs.ns(|| "check inv"), &inverse, &one)?;
|
result.mul_equals(&denominator, &self)?;
|
||||||
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<CS: ConstraintSystem<ConstraintF>>(
|
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
denominator: &Self,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
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)?;
|
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frobenius_map<CS: ConstraintSystem<ConstraintF>>(
|
fn frobenius_map(&self, power: usize) -> Result<Self, SynthesisError>;
|
||||||
&self,
|
|
||||||
_: CS,
|
|
||||||
power: usize,
|
|
||||||
) -> Result<Self, SynthesisError>;
|
|
||||||
|
|
||||||
fn frobenius_map_in_place<CS: ConstraintSystem<ConstraintF>>(
|
fn frobenius_map_in_place(&mut self, power: usize) -> Result<&mut Self, SynthesisError> {
|
||||||
&mut self,
|
*self = self.frobenius_map(power)?;
|
||||||
cs: CS,
|
|
||||||
power: usize,
|
|
||||||
) -> Result<&mut Self, SynthesisError> {
|
|
||||||
*self = self.frobenius_map(cs, power)?;
|
|
||||||
Ok(self)
|
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.
|
/// form, are a scalar.
|
||||||
#[inline]
|
//
|
||||||
fn pow<CS: ConstraintSystem<ConstraintF>>(
|
// TODO: check that the input really should be in little-endian or not...
|
||||||
&self,
|
fn pow(&self, bits: &[Boolean<ConstraintF>]) -> Result<Self, SynthesisError> {
|
||||||
mut cs: CS,
|
let mut res = Self::one();
|
||||||
bits: &[Boolean],
|
for bit in bits.iter() {
|
||||||
) -> Result<Self, SynthesisError> {
|
res.square_in_place()?;
|
||||||
let mut res = Self::one(cs.ns(|| "Alloc result"))?;
|
let tmp = res.clone() * self;
|
||||||
for (i, bit) in bits.iter().enumerate() {
|
res = bit.select(&tmp, &res)?;
|
||||||
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,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pow_by_constant<S: AsRef<[u64]>, CS: ConstraintSystem<ConstraintF>>(
|
fn pow_by_constant<S: AsRef<[u64]>>(&self, exp: S) -> Result<Self, SynthesisError> {
|
||||||
&self,
|
|
||||||
mut cs: CS,
|
|
||||||
exp: S,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
let mut res = self.clone();
|
let mut res = self.clone();
|
||||||
let mut found_one = false;
|
let mut found_one = false;
|
||||||
|
|
||||||
for (i, bit) in BitIterator::new(exp).enumerate() {
|
for bit in BitIterator::new(exp) {
|
||||||
if found_one {
|
if found_one {
|
||||||
res = res.square(cs.ns(|| format!("square for bit {:?}", i)))?;
|
res = res.square()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if bit {
|
if bit {
|
||||||
if found_one {
|
if found_one {
|
||||||
res = res.mul(cs.ns(|| format!("mul for bit {:?}", i)), self)?;
|
res *= self;
|
||||||
}
|
}
|
||||||
found_one = true;
|
found_one = true;
|
||||||
}
|
}
|
||||||
@@ -291,16 +165,6 @@ pub trait FieldGadget<F: Field, ConstraintF: Field>:
|
|||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cost_of_mul() -> usize;
|
|
||||||
|
|
||||||
fn cost_of_mul_equals() -> usize {
|
|
||||||
Self::cost_of_mul() + <Self as EqGadget<ConstraintF>>::cost()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cost_of_inv() -> usize {
|
|
||||||
Self::cost_of_mul_equals()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -308,267 +172,197 @@ pub(crate) mod tests {
|
|||||||
use rand::{self, SeedableRng};
|
use rand::{self, SeedableRng};
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
|
|
||||||
use crate::{prelude::*, test_constraint_system::TestConstraintSystem, Vec};
|
use crate::{fields::*, Vec};
|
||||||
use algebra::{test_rng, BitIterator, Field, UniformRand};
|
use algebra::{test_rng, BitIterator, Field, UniformRand};
|
||||||
use r1cs_core::ConstraintSystem;
|
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn field_test<FE: Field, ConstraintF: Field, F: FieldGadget<FE, ConstraintF>>() {
|
pub(crate) fn field_test<F, ConstraintF, AF>() -> Result<(), SynthesisError>
|
||||||
let mut cs = TestConstraintSystem::<ConstraintF>::new();
|
where
|
||||||
|
F: Field,
|
||||||
|
ConstraintF: Field,
|
||||||
|
AF: FieldVar<F, ConstraintF>,
|
||||||
|
AF: TwoBitLookupGadget<ConstraintF, TableConstant = F>,
|
||||||
|
for<'a> &'a AF: FieldOpsBounds<'a, F, AF>,
|
||||||
|
{
|
||||||
|
let cs = ConstraintSystem::<ConstraintF>::new_ref();
|
||||||
|
|
||||||
let mut rng = test_rng();
|
let mut rng = test_rng();
|
||||||
let a_native = FE::rand(&mut rng);
|
let a_native = F::rand(&mut rng);
|
||||||
let b_native = FE::rand(&mut rng);
|
let b_native = F::rand(&mut rng);
|
||||||
let a = F::alloc(&mut cs.ns(|| "generate_a"), || Ok(a_native)).unwrap();
|
let a = AF::new_witness(cs.ns("generate_a"), || Ok(a_native))?;
|
||||||
let b = F::alloc(&mut cs.ns(|| "generate_b"), || Ok(b_native)).unwrap();
|
let b = AF::new_witness(cs.ns("generate_b"), || Ok(b_native))?;
|
||||||
let b_const = F::alloc_constant(&mut cs.ns(|| "generate_b_as_constant"), b_native).unwrap();
|
let b_const = AF::new_constant(cs.ns("b_as_constant"), b_native)?;
|
||||||
|
|
||||||
let zero = F::zero(cs.ns(|| "zero")).unwrap();
|
let zero = AF::zero();
|
||||||
let zero_native = zero.get_value().unwrap();
|
let zero_native = zero.value()?;
|
||||||
zero.enforce_equal(&mut cs.ns(|| "zero_equals?"), &zero)
|
zero.enforce_equal(&zero)?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(zero, zero);
|
|
||||||
|
|
||||||
let one = F::one(cs.ns(|| "one")).unwrap();
|
let one = AF::one();
|
||||||
let one_native = one.get_value().unwrap();
|
let one_native = one.value()?;
|
||||||
assert_eq!(one, one);
|
one.enforce_equal(&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.enforce_not_equal(&zero)?;
|
||||||
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();
|
let one_dup = &zero + &one;
|
||||||
two.enforce_equal(&mut cs.ns(|| "two_equals?"), &two)
|
one_dup.enforce_equal(&one)?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(two, two);
|
|
||||||
assert_ne!(zero, two);
|
|
||||||
assert_ne!(one, two);
|
|
||||||
|
|
||||||
// a == a
|
let two = &one + &one;
|
||||||
assert_eq!(a, a);
|
two.enforce_equal(&two)?;
|
||||||
|
two.enforce_equal(&one.double()?)?;
|
||||||
|
two.enforce_not_equal(&one)?;
|
||||||
|
two.enforce_not_equal(&zero)?;
|
||||||
|
|
||||||
// a + 0 = a
|
// a + 0 = a
|
||||||
let a_plus_zero = a.add(cs.ns(|| "a_plus_zero"), &zero).unwrap();
|
let a_plus_zero = &a + &zero;
|
||||||
assert_eq!(a_plus_zero, a);
|
assert_eq!(a_plus_zero.value()?, a_native);
|
||||||
assert_eq!(a_plus_zero.get_value().unwrap(), a_native);
|
a_plus_zero.enforce_equal(&a)?;
|
||||||
a_plus_zero
|
a_plus_zero.enforce_not_equal(&a.double()?)?;
|
||||||
.enforce_equal(&mut cs.ns(|| "a_plus_zero_equals?"), &a)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// a - 0 = a
|
// a - 0 = a
|
||||||
let a_minus_zero = a.sub(cs.ns(|| "a_minus_zero"), &zero).unwrap();
|
let a_minus_zero = &a - &zero;
|
||||||
assert_eq!(a_minus_zero, a);
|
assert_eq!(a_minus_zero.value()?, a_native);
|
||||||
assert_eq!(a_minus_zero.get_value().unwrap(), a_native);
|
a_minus_zero.enforce_equal(&a)?;
|
||||||
a_minus_zero
|
|
||||||
.enforce_equal(&mut cs.ns(|| "a_minus_zero_equals?"), &a)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// a - a = 0
|
// a - a = 0
|
||||||
let a_minus_a = a.sub(cs.ns(|| "a_minus_a"), &a).unwrap();
|
let a_minus_a = &a - &a;
|
||||||
assert_eq!(a_minus_a, zero);
|
assert_eq!(a_minus_a.value()?, zero_native);
|
||||||
assert_eq!(a_minus_a.get_value().unwrap(), zero_native);
|
a_minus_a.enforce_equal(&zero)?;
|
||||||
a_minus_a
|
|
||||||
.enforce_equal(&mut cs.ns(|| "a_minus_a_equals?"), &zero)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// a + b = b + a
|
// a + b = b + a
|
||||||
let a_b = a.add(cs.ns(|| "a_plus_b"), &b).unwrap();
|
let a_b = &a + &b;
|
||||||
let b_a = b.add(cs.ns(|| "b_plus_a"), &a).unwrap();
|
let b_a = &b + &a;
|
||||||
assert_eq!(a_b, b_a);
|
assert_eq!(a_b.value()?, a_native + &b_native);
|
||||||
assert_eq!(a_b.get_value().unwrap(), a_native + &b_native);
|
a_b.enforce_equal(&b_a)?;
|
||||||
a_b.enforce_equal(&mut cs.ns(|| "a+b == b+a"), &b_a)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// (a + b) + a = a + (b + a)
|
// (a + b) + a = a + (b + a)
|
||||||
let ab_a = a_b.add(cs.ns(|| "a_b_plus_a"), &a).unwrap();
|
let ab_a = &a_b + &a;
|
||||||
let a_ba = a.add(cs.ns(|| "a_plus_b_a"), &b_a).unwrap();
|
let a_ba = &a + &b_a;
|
||||||
assert_eq!(ab_a, a_ba);
|
assert_eq!(ab_a.value()?, a_native + &b_native + &a_native);
|
||||||
assert_eq!(ab_a.get_value().unwrap(), a_native + &b_native + &a_native);
|
ab_a.enforce_equal(&a_ba)?;
|
||||||
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_a_plus_b = &a_b * &b;
|
||||||
let b_times_b_plus_a = b_a.mul(cs.ns(|| "b * (b + a)"), &b).unwrap();
|
let b_times_b_plus_a = &b_a * &b;
|
||||||
assert_eq!(b_times_b_plus_a, b_times_a_plus_b);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b_times_a_plus_b.get_value().unwrap(),
|
b_times_a_plus_b.value()?,
|
||||||
b_native * &(b_native + &a_native)
|
b_native * &(b_native + &a_native)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b_times_a_plus_b.get_value().unwrap(),
|
b_times_a_plus_b.value()?,
|
||||||
(b_native + &a_native) * &b_native
|
(b_native + &a_native) * &b_native
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b_times_a_plus_b.get_value().unwrap(),
|
b_times_a_plus_b.value()?,
|
||||||
(a_native + &b_native) * &b_native
|
(a_native + &b_native) * &b_native
|
||||||
);
|
);
|
||||||
b_times_b_plus_a
|
b_times_b_plus_a.enforce_equal(&b_times_a_plus_b)?;
|
||||||
.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);
|
|
||||||
|
|
||||||
// a * 1 = a
|
// a * 1 = a
|
||||||
assert_eq!(a.mul(cs.ns(|| "a_times_one"), &one).unwrap(), a);
|
assert_eq!((&a * &one).value()?, a_native * &one_native);
|
||||||
assert_eq!(
|
|
||||||
a.mul(cs.ns(|| "a_times_one2"), &one)
|
|
||||||
.unwrap()
|
|
||||||
.get_value()
|
|
||||||
.unwrap(),
|
|
||||||
a_native * &one_native
|
|
||||||
);
|
|
||||||
|
|
||||||
// a * b = b * a
|
// a * b = b * a
|
||||||
let ab = a.mul(cs.ns(|| "a_times_b"), &b).unwrap();
|
let ab = &a * &b;
|
||||||
let ba = b.mul(cs.ns(|| "b_times_a"), &a).unwrap();
|
let ba = &b * &a;
|
||||||
assert_eq!(ab, ba);
|
assert_eq!(ab.value()?, ba.value()?);
|
||||||
assert_eq!(ab.get_value().unwrap(), a_native * &b_native);
|
assert_eq!(ab.value()?, a_native * &b_native);
|
||||||
|
|
||||||
let ab_const = a.mul(cs.ns(|| "a_times_b_const"), &b_const).unwrap();
|
let ab_const = &a * &b_const;
|
||||||
let b_const_a = b_const.mul(cs.ns(|| "b_const_times_a"), &a).unwrap();
|
let b_const_a = &b_const * &a;
|
||||||
assert_eq!(ab_const, b_const_a);
|
assert_eq!(ab_const.value()?, b_const_a.value()?);
|
||||||
assert_eq!(ab_const, ab);
|
assert_eq!(ab_const.value()?, ab.value()?);
|
||||||
assert_eq!(ab_const.get_value().unwrap(), a_native * &b_native);
|
assert_eq!(ab_const.value()?, a_native * &b_native);
|
||||||
|
|
||||||
// (a * b) * a = a * (b * a)
|
// (a * b) * a = a * (b * a)
|
||||||
let ab_a = ab.mul(cs.ns(|| "ab_times_a"), &a).unwrap();
|
let ab_a = &ab * &a;
|
||||||
let a_ba = a.mul(cs.ns(|| "a_times_ba"), &ba).unwrap();
|
let a_ba = &a * &ba;
|
||||||
assert_eq!(ab_a, a_ba);
|
assert_eq!(ab_a.value()?, a_ba.value()?);
|
||||||
assert_eq!(ab_a.get_value().unwrap(), a_native * &b_native * &a_native);
|
assert_eq!(ab_a.value()?, a_native * &b_native * &a_native);
|
||||||
|
|
||||||
let aa = a.mul(cs.ns(|| "a * a"), &a).unwrap();
|
let aa = &a * &a;
|
||||||
let a_squared = a.square(cs.ns(|| "a^2")).unwrap();
|
let a_squared = a.square()?;
|
||||||
a_squared
|
a_squared.enforce_equal(&aa)?;
|
||||||
.enforce_equal(&mut cs.ns(|| "a^2 == a*a"), &aa)
|
assert_eq!(aa.value()?, a_squared.value()?);
|
||||||
.unwrap();
|
assert_eq!(aa.value()?, a_native.square());
|
||||||
assert_eq!(aa, a_squared);
|
|
||||||
assert_eq!(aa.get_value().unwrap(), a_native.square());
|
|
||||||
|
|
||||||
let aa = a
|
let aa = &a * a.value()?;
|
||||||
.mul_by_constant(cs.ns(|| "a * a via mul_by_const"), &a.get_value().unwrap())
|
a_squared.enforce_equal(&aa)?;
|
||||||
.unwrap();
|
assert_eq!(aa.value()?, a_squared.value()?);
|
||||||
a_squared
|
assert_eq!(aa.value()?, a_native.square());
|
||||||
.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
|
let a_b2 = &a + b_native;
|
||||||
.add_constant(cs.ns(|| "a + b via add_const"), &b.get_value().unwrap())
|
a_b.enforce_equal(&a_b2)?;
|
||||||
.unwrap();
|
assert_eq!(a_b.value()?, a_b2.value()?);
|
||||||
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();
|
let a_inv = a.inverse()?;
|
||||||
a_inv
|
a_inv.mul_equals(&a, &one)?;
|
||||||
.mul_equals(cs.ns(|| "check a_inv * a = 1"), &a, &one)
|
assert_eq!(a_inv.value()?, a.value()?.inverse().unwrap());
|
||||||
.unwrap();
|
assert_eq!(a_inv.value()?, a_native.inverse().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();
|
let a_b_inv = a.mul_by_inverse(&b)?;
|
||||||
a_b_inv
|
a_b_inv.mul_equals(&b, &a)?;
|
||||||
.mul_equals(cs.ns(|| "check a_b_inv * b = a"), &b, &a)
|
assert_eq!(a_b_inv.value()?, a_native * b_native.inverse().unwrap());
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
a_b_inv.get_value().unwrap(),
|
|
||||||
a_native * b_native.inverse().unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// a * a * a = a^3
|
// a * a * a = a^3
|
||||||
let bits = BitIterator::new([0x3])
|
let bits = BitIterator::new([0x3])
|
||||||
.map(Boolean::constant)
|
.map(Boolean::constant)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(
|
assert_eq!(a_native.pow([0x3]), a.pow(&bits)?.value()?);
|
||||||
a_native * &(a_native * &a_native),
|
|
||||||
a.pow(cs.ns(|| "test_pow"), &bits)
|
|
||||||
.unwrap()
|
|
||||||
.get_value()
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// a * a * a = a^3
|
// a * a * a = a^3
|
||||||
assert_eq!(
|
assert_eq!(a_native.pow([0x3]), a.pow_by_constant(&[3])?.value()?);
|
||||||
a_native * &(a_native * &a_native),
|
|
||||||
a.pow_by_constant(cs.ns(|| "test_constant_pow"), &[3])
|
|
||||||
.unwrap()
|
|
||||||
.get_value()
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
|
|
||||||
// a * a * a = a^3
|
// a * a * a = a^3
|
||||||
let mut constants = [FE::zero(); 4];
|
let mut constants = [F::zero(); 4];
|
||||||
for c in &mut constants {
|
for c in &mut constants {
|
||||||
*c = UniformRand::rand(&mut test_rng());
|
*c = UniformRand::rand(&mut test_rng());
|
||||||
println!("Current c[i]: {:?}", c);
|
|
||||||
}
|
}
|
||||||
let bits = [Boolean::constant(false), Boolean::constant(true)];
|
let bits = [
|
||||||
let lookup_result =
|
Boolean::<ConstraintF>::constant(false),
|
||||||
F::two_bit_lookup(cs.ns(|| "Lookup"), &bits, constants.as_ref()).unwrap();
|
Boolean::constant(true),
|
||||||
assert_eq!(lookup_result.get_value().unwrap(), constants[2]);
|
];
|
||||||
|
let lookup_result = AF::two_bit_lookup(&bits, constants.as_ref())?;
|
||||||
|
assert_eq!(lookup_result.value()?, constants[2]);
|
||||||
|
|
||||||
let negone: FE = UniformRand::rand(&mut test_rng());
|
let negone: F = UniformRand::rand(&mut test_rng());
|
||||||
|
|
||||||
let n = F::alloc(&mut cs.ns(|| "alloc new var"), || Ok(negone)).unwrap();
|
let n = AF::new_witness(cs.ns("alloc new var"), || Ok(negone)).unwrap();
|
||||||
let _ = n.to_bytes(&mut cs.ns(|| "ToBytes")).unwrap();
|
let _ = n.to_bytes()?;
|
||||||
let _ = n
|
let _ = n.to_non_unique_bytes()?;
|
||||||
.to_non_unique_bytes(&mut cs.ns(|| "ToBytes Strict"))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ab_false = a
|
let ab_false = &a + (AF::from(Boolean::Constant(false)) * b_native);
|
||||||
.conditionally_add_constant(
|
assert_eq!(ab_false.value()?, a_native);
|
||||||
cs.ns(|| "Add bool with coeff false"),
|
let ab_true = &a + (AF::from(Boolean::Constant(true)) * b_native);
|
||||||
&Boolean::constant(false),
|
assert_eq!(ab_true.value()?, a_native + &b_native);
|
||||||
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() {
|
if !cs.is_satisfied().unwrap() {
|
||||||
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
println!("{:?}", cs.which_is_unsatisfied().unwrap());
|
||||||
}
|
}
|
||||||
assert!(cs.is_satisfied());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn frobenius_tests<
|
pub(crate) fn frobenius_tests<F: Field, ConstraintF, AF>(
|
||||||
FE: Field,
|
|
||||||
ConstraintF: Field,
|
|
||||||
F: FieldGadget<FE, ConstraintF>,
|
|
||||||
>(
|
|
||||||
maxpower: usize,
|
maxpower: usize,
|
||||||
) {
|
) -> Result<(), SynthesisError>
|
||||||
let mut cs = TestConstraintSystem::<ConstraintF>::new();
|
where
|
||||||
|
F: Field,
|
||||||
|
ConstraintF: Field,
|
||||||
|
AF: FieldVar<F, ConstraintF>,
|
||||||
|
for<'a> &'a AF: FieldOpsBounds<'a, F, AF>,
|
||||||
|
{
|
||||||
|
let cs = ConstraintSystem::<ConstraintF>::new_ref();
|
||||||
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
let mut rng = XorShiftRng::seed_from_u64(1231275789u64);
|
||||||
for i in 0..=maxpower {
|
for i in 0..=maxpower {
|
||||||
let mut a = FE::rand(&mut rng);
|
let mut a = F::rand(&mut rng);
|
||||||
let mut a_gadget = F::alloc(cs.ns(|| format!("a_gadget_{:?}", i)), || Ok(a)).unwrap();
|
let mut a_gadget = AF::new_witness(cs.ns(format!("a_gadget_{:?}", i)), || Ok(a))?;
|
||||||
a_gadget = a_gadget
|
a_gadget.frobenius_map_in_place(i)?;
|
||||||
.frobenius_map(cs.ns(|| format!("frob_map_{}", i)), i)
|
|
||||||
.unwrap();
|
|
||||||
a.frobenius_map(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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
504
r1cs-std/src/fields/quadratic_extension.rs
Normal file
504
r1cs-std/src/fields/quadratic_extension.rs
Normal file
@@ -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<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: QuadExtVarParams<BF>>
|
||||||
|
where
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
{
|
||||||
|
pub c0: BF,
|
||||||
|
pub c1: BF,
|
||||||
|
#[derivative(Debug = "ignore")]
|
||||||
|
_params: PhantomData<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait QuadExtVarParams<BF: FieldVar<Self::BaseField, Self::BasePrimeField>>:
|
||||||
|
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<BF: FieldVar<P::BaseField, P::BasePrimeField>, P: QuadExtVarParams<BF>> QuadExtVar<BF, P>
|
||||||
|
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<BF, SynthesisError> {
|
||||||
|
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<Self, SynthesisError> {
|
||||||
|
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<Self, SynthesisError>
|
||||||
|
where
|
||||||
|
Self: FieldVar<QuadExtField<P>, 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<BF, P> R1CSVar<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
type Value = QuadExtField<P>;
|
||||||
|
|
||||||
|
fn cs(&self) -> Option<ConstraintSystemRef<P::BasePrimeField>> {
|
||||||
|
[&self.c0, &self.c1].cs()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn value(&self) -> Result<Self::Value, SynthesisError> {
|
||||||
|
match (self.c0.value(), self.c1.value()) {
|
||||||
|
(Ok(c0), Ok(c1)) => Ok(QuadExtField::new(c0, c1)),
|
||||||
|
(..) => Err(SynthesisError::AssignmentMissing),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BF, P> From<Boolean<P::BasePrimeField>> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn from(other: Boolean<P::BasePrimeField>) -> Self {
|
||||||
|
let c0 = BF::from(other);
|
||||||
|
let c1 = BF::zero();
|
||||||
|
Self::new(c0, c1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, BF, P> FieldOpsBounds<'a, QuadExtField<P>, QuadExtVar<BF, P>> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
impl<'a, BF, P> FieldOpsBounds<'a, QuadExtField<P>, QuadExtVar<BF, P>> for &'a QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BF, P> FieldVar<QuadExtField<P>, P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn constant(other: QuadExtField<P>) -> 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<Self, SynthesisError> {
|
||||||
|
let c0 = self.c0.double()?;
|
||||||
|
let c1 = self.c1.double()?;
|
||||||
|
Ok(Self::new(c0, c1))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn negate(&self) -> Result<Self, SynthesisError> {
|
||||||
|
let mut result = self.clone();
|
||||||
|
result.c0.negate_in_place()?;
|
||||||
|
result.c1.negate_in_place()?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn square(&self) -> Result<Self, SynthesisError> {
|
||||||
|
// 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<Self, SynthesisError> {
|
||||||
|
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<Self, SynthesisError> {
|
||||||
|
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<BF, P>,
|
||||||
|
QuadExtField<P>,
|
||||||
|
Add,
|
||||||
|
add,
|
||||||
|
AddAssign,
|
||||||
|
add_assign,
|
||||||
|
|this: &'a QuadExtVar<BF, P>, other: &'a QuadExtVar<BF, P>| {
|
||||||
|
let c0 = &this.c0 + &other.c0;
|
||||||
|
let c1 = &this.c1 + &other.c1;
|
||||||
|
QuadExtVar::new(c0, c1)
|
||||||
|
},
|
||||||
|
|this: &'a QuadExtVar<BF, P>, other: QuadExtField<P>| {
|
||||||
|
this + QuadExtVar::constant(other)
|
||||||
|
},
|
||||||
|
(BF: FieldVar<P::BaseField, P::BasePrimeField>, P: QuadExtVarParams<BF>),
|
||||||
|
for <'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>
|
||||||
|
);
|
||||||
|
impl_bounded_ops!(
|
||||||
|
QuadExtVar<BF, P>,
|
||||||
|
QuadExtField<P>,
|
||||||
|
Sub,
|
||||||
|
sub,
|
||||||
|
SubAssign,
|
||||||
|
sub_assign,
|
||||||
|
|this: &'a QuadExtVar<BF, P>, other: &'a QuadExtVar<BF, P>| {
|
||||||
|
let c0 = &this.c0 - &other.c0;
|
||||||
|
let c1 = &this.c1 - &other.c1;
|
||||||
|
QuadExtVar::new(c0, c1)
|
||||||
|
},
|
||||||
|
|this: &'a QuadExtVar<BF, P>, other: QuadExtField<P>| {
|
||||||
|
this - QuadExtVar::constant(other)
|
||||||
|
},
|
||||||
|
(BF: FieldVar<P::BaseField, P::BasePrimeField>, P: QuadExtVarParams<BF>),
|
||||||
|
for <'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>
|
||||||
|
);
|
||||||
|
impl_bounded_ops!(
|
||||||
|
QuadExtVar<BF, P>,
|
||||||
|
QuadExtField<P>,
|
||||||
|
Mul,
|
||||||
|
mul,
|
||||||
|
MulAssign,
|
||||||
|
mul_assign,
|
||||||
|
|this: &'a QuadExtVar<BF, P>, other: &'a QuadExtVar<BF, P>| {
|
||||||
|
// 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::<BF, P>::mul_base_field_by_nonresidue(&v1).unwrap();
|
||||||
|
result
|
||||||
|
},
|
||||||
|
|this: &'a QuadExtVar<BF, P>, other: QuadExtField<P>| {
|
||||||
|
this * QuadExtVar::constant(other)
|
||||||
|
},
|
||||||
|
(BF: FieldVar<P::BaseField, P::BasePrimeField>, P: QuadExtVarParams<BF>),
|
||||||
|
for <'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<BF, P> EqGadget<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn is_eq(&self, other: &Self) -> Result<Boolean<P::BasePrimeField>, 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<P::BasePrimeField>,
|
||||||
|
) -> 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<P::BasePrimeField>,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
let is_equal = self.is_eq(other)?;
|
||||||
|
is_equal
|
||||||
|
.and(condition)?
|
||||||
|
.enforce_equal(&Boolean::Constant(false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BF, P> ToBitsGadget<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn to_bits(&self) -> Result<Vec<Boolean<P::BasePrimeField>>, 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<Vec<Boolean<P::BasePrimeField>>, 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<BF, P> ToBytesGadget<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn to_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, 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<Vec<UInt8<P::BasePrimeField>>, 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<BF, P> CondSelectGadget<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn conditionally_select(
|
||||||
|
cond: &Boolean<P::BasePrimeField>,
|
||||||
|
true_value: &Self,
|
||||||
|
false_value: &Self,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
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<BF, P> TwoBitLookupGadget<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>
|
||||||
|
+ TwoBitLookupGadget<P::BasePrimeField, TableConstant = P::BaseField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
type TableConstant = QuadExtField<P>;
|
||||||
|
|
||||||
|
fn two_bit_lookup(
|
||||||
|
b: &[Boolean<P::BasePrimeField>],
|
||||||
|
c: &[Self::TableConstant],
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||||
|
let c0 = BF::two_bit_lookup(b, &c0s)?;
|
||||||
|
let c1 = BF::two_bit_lookup(b, &c1s)?;
|
||||||
|
Ok(Self::new(c0, c1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BF, P> ThreeBitCondNegLookupGadget<P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>
|
||||||
|
+ ThreeBitCondNegLookupGadget<P::BasePrimeField, TableConstant = P::BaseField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
type TableConstant = QuadExtField<P>;
|
||||||
|
|
||||||
|
fn three_bit_cond_neg_lookup(
|
||||||
|
b: &[Boolean<P::BasePrimeField>],
|
||||||
|
b0b1: &Boolean<P::BasePrimeField>,
|
||||||
|
c: &[Self::TableConstant],
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
let c0s = c.iter().map(|f| f.c0).collect::<Vec<_>>();
|
||||||
|
let c1s = c.iter().map(|f| f.c1).collect::<Vec<_>>();
|
||||||
|
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<BF, P> AllocVar<QuadExtField<P>, P::BasePrimeField> for QuadExtVar<BF, P>
|
||||||
|
where
|
||||||
|
BF: FieldVar<P::BaseField, P::BasePrimeField>,
|
||||||
|
for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,
|
||||||
|
P: QuadExtVarParams<BF>,
|
||||||
|
{
|
||||||
|
fn new_variable<T: Borrow<QuadExtField<P>>>(
|
||||||
|
cs: impl Into<Namespace<P::BasePrimeField>>,
|
||||||
|
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||||
|
mode: AllocationMode,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user